diff --git a/.ci/bin/xldd b/.ci/bin/xldd new file mode 100755 index 0000000..ba08d01 --- /dev/null +++ b/.ci/bin/xldd @@ -0,0 +1,371 @@ +#!/bin/bash + +# ldd drop-in replacement for cross-compilation toolchains. + +# This file is a slightly modified version of xldd.in from +# crosstool-ng 1.22.0 + +# In order to use it, copy it in same directory than other +# toolchain binaries and rename it with same tuple. +# (i.e. /opt/arm-sysmic-linux-gnueabihf/bin/arm-sysmic-linux-gnueabihf-ldd) +# Thus, this will automaticaly detect necessary information +# about your toolchain. + +export LC_ALL=C +version="forked from crosstool-ng 1.22.0" +# Change it to 64 if necessary +bits="32" +sed="${SED:-sed}" +grep="${GREP:-grep}" + +abs_path="$(realpath $0)" +my_name="$( basename "${abs_path}" )" +prefix="${abs_path%-ldd}" +gcc="${prefix}-gcc" +readelf="${prefix}-readelf" +fake_load_addr_root="$((0xdeadbeef))" +fake_load_addr_rpath="$((0xdeadc0de))" +fake_load_addr_sysroot="$((0x8badf00d))" +ld_library_path="/lib:/usr/lib:$LD_LIBRARY_PATH" + +do_error() { + printf "%s: %s\n" "${my_name}" "$*" >&2 +} + +do_opt_error() { + do_error "$@" + printf "Try \`%s --help' for more information\n" "${my_name}" >&2 +} + +do_trace() { + local depth=0 + + [ -z "${CT_XLDD_VERBOSE}" ] && return 0 + + for((depth=0; "${#FUNCNAME[$((depth+1))]}" != 0; depth++)); do :; done + printf "%*s" $((4*(depth-1))) "" >&2 + printf -- "$@" >&2 +} + +show_version() { + # Fake a real ldd, just in case some dumb script would check + cat <<_EOF_ +ldd (crosstool-NG) ${version} +Copyright (C) 2010 "Yann E. MORIN" +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +Licensed under the GPLv2, see the file LICENSES in the top-directory of the +sources for this package. +_EOF_ +} + +show_help() { + cat <<_EOF_ +Usage: ${my_name} [OPTION]... --root DIR FILE... + --help print this help and exit + --version print version information and exit + --root dir treat dir as being the root of the target + -s, --show-system mark libs from the sysroot with a trailing '[*]' + and libs found via RPATH with a trailing '[+]' + +_EOF_ + cat <<_EOF_ |fmt +${my_name} tries to mimick the behavior of a real native ldd, but can be +used in a cross-development environment. Here is how it differs from a +real native ldd: + +If the CT_XLDD_VERBOSE variable is set and non-empty, then ${my_name} will +print a lot of debug messages, explaining how it builds the library +search path, and how each library was found and why. + +The LD_LIBRARY_PATH variable is not used, as it can not reliably be +guessed except at runtime, and we can't run. + +${my_name} does not scan /etc/ld.so.cache, but instead uses /etc/ld.so.conf +(it understands the include directives therein for libces that have that). + +${my_name} also interprets (tries to!) the RPATH/RUNPATH records found in +the dynamic ELF section. Such paths are searched for only relative to +the specified root, not from the sysroot (see below). Also, those paths +are searched for not only for the file they appear in, but also for its +dependencies. + +${my_name} will search the directory specified with --root for libraries +to resolve the NEEDED tags. If --root is not set, then ${my_name} will +use the value in the environment variable \${CT_XLDD_ROOT}. If neither +is set, then this is an error. + +If NEEDED libraries can't be found in the specified root directory, then +${my_name} will also look in the sysroot of the toolchain to see if it +can find them. + +For NEEDED libraries that were found, the output will look like: + libneeded.so => /path/to/libneeded.so (0xloadaddr) + +and for those that were not found, the output will look like: + libneeded.so not found + +The paths are relative to the host root directory. + +The expected load address 'loadaddr' is a faked address to match the output +of the real ldd, but has no actual meaning (set to some constants for now, +0x8badf00d for libraries from the sysroot, 0xdeadc0de for those found via +the RPATH/RUNPATH records, and 0xdeadbeef for others). +_EOF_ + +# Unimplemeted yet: +# -d, --data-relocs process data relocations +# -r, --function-relocs process data and function relocations +# -u, --unused print unused direct dependencies +# -v, --verbose print all information + +# See also this thread: +# http://sourceware.org/ml/crossgcc/2008-09/msg00057.html +} + +# Parse command line options +root="${CT_XLDD_ROOT}" +show_system= +while true; do + case "${1}" in + --help) + show_help + exit 0 + ;; + --version) + show_version + exit 0 + ;; + --root) + root="$2" + shift + ;; + --root=*) + root="${1#--root=}" + ;; + --show-system|-s) + show_system=1 + ;; + -*) + do_opt_error "unrecognized option \`${1}'" + exit 1 + ;; + *) + break + ;; + esac + shift +done + +# Sanity checks +sysroot="$( "${gcc}" -print-sysroot 2>/dev/null )" +if [ -z "${sysroot}" ]; then + sysroot="$( "${gcc}" -print-file-name=libc.so 2>/dev/null \ + |${sed} -r -e 's:/usr/lib/libc.so$::;' \ + )" +fi +if [ -z "${sysroot}" ]; then + do_error "unable to find sysroot for \`${gcc}'" +fi + +if [ -z "${root}" ]; then + root=${sysroot} +fi +if [ ! -d "${root}" ]; then + do_error "\`${root}': no such file or directory" + exit 1 +fi + +do_report_needed_found() { + local needed="${1}" + local path="${2}" + local origin="${3}" + local loadaddr + local sys + local root_append="" + + case "${origin}" in + root) + loadaddr="${fake_load_addr_root}" + root_append="${root}" + ;; + rpath) + loadaddr="${fake_load_addr_rpath}" + if [ -n "${show_system}" ]; then + sys=" [+]" + fi + ;; + sysroot) + loadaddr="${fake_load_addr_sysroot}" + if [ -n "${show_system}" ]; then + sys=" [*]" + fi + ;; + esac + + printf "%8s%s => %s (0x%0*x)%s\n" \ + "" \ + "${needed}" \ + "${path}" \ + "$((bits/4))" \ + "${loadaddr}" \ + "${sys}" | sed "s| => /| => ${root_append}/|" +} + +# Search a needed file, scanning ${lib_dir} in the root directory +do_find_needed() { + local needed="${1}" + local -a list + local -a dirs + local found + local where + local base + local d i + + do_trace "Searching for '%s'\n" "${needed}" + + # rpath shall come first! + list=( \ + "rpath:${root}" \ + "root:${root}" \ + "sysroot:${sysroot}" \ + ) + + for i in "${list[@]}"; do + where="${i%%:*}" + base="${i#*:}" + if [ "${where}" = "rpath" ]; then + dirs=( "${search_rpath[@]}" ) + else + dirs=( "${needed_search_path[@]}" ) + fi + for d in "${dirs[@]}"; do + do_trace "-> looking in '%s' (%s)\n" "${d}" "${where}" + if [ -f "${base}${d}/${needed}" ]; then + found="${d}/${needed}" + do_trace "---> found\n" + break 2 + fi + done + done + + if [ -n "${found}" ]; then + do_report_needed_found "${needed}" "${found}" "${where}" + do_process_file "${base}${found}" + else + printf "%8s%s not found\n" "" "${needed}" + fi + + do_trace "Done searching for '%s'\n" "${needed}" +} + +# Scan a file for all NEEDED tags +do_process_file() { + local file="${1}" + local -a save_search_rpath + local n m + local found + + do_trace "Parsing file '%s'\n" "${file}" + + save_search_rpath=( "${search_rpath[@]}" ) + for n in $( "${readelf}" -d "${file}" \ + |"${grep}" -E '\((RPATH|RUNPATH)\)' \ + |"${sed}" -r -e 's/^.*Library r(|un)path:[[:space:]]+\[(.*)\]$/\2/;'\ + ); do + + OIFS=$IFS; + IFS=":"; + narray=($n) + for subn in "${narray[@]}"; do + do_trace "-> adding rpath '%s'\n" "${subn}" + search_rpath+=( "${subn}" ) + done + IFS=$OIFS; + done + do_trace ": search path:\n" + for n in "${search_rpath[@]}" "${needed_search_path[@]}"; do + do_trace ": - '%s'\n" "${n}" + done + do_trace ": end search path\n" + + for n in $( "${readelf}" -d "${file}" \ + |"${grep}" -E '\(NEEDED\)' \ + |"${sed}" -r -e 's/^.*Shared library:[[:space:]]+\[([^]]+)\].*/\1/;' \ + ); do + found=0 + for m in "${needed_list[@]}"; do + [ "${n}" = "${m}" ] && found=1 && break + done + if [ ${found} -ne 0 ]; then + do_trace "-> skipping already known dependency '%s'\n" "${n}" + continue + fi + do_trace "-> handling new dependency '%s'\n" "${n}" + needed_list+=( "${n}" ) + do_find_needed "${n}" + do_trace "-> done handling dependency '%s'\n" "${n}" + done + + search_rpath=( "${save_search_rpath[@]}" ) + + do_trace "Finished parsing file '%s'\n" "${file}" +} + +# Recursively scan a /etc/ld.so.conf file +do_scan_etc_ldsoconf() { + local ldsoconf="${1}" + local g + local f + + [ -f "${ldsoconf}" ] || return 0 + do_trace "Parsing ld.so.conf: '%s'\n" "${ldsoconf}" + + while read line; do + case "${line}" in + include\ *) + g="${root}${line#include }" + do_trace "-> handling include directive '%s'\n" "${g}" + for f in ${g}; do + do_scan_etc_ldsoconf "${f}" + done + do_trace "-> finished handling include directive '%s'\n" "${g}" + ;; + \#*|"") + ;; + *) + do_trace "-> adding search dir '%s'\n" "${line}" + needed_search_path+=( "${line}" ) + ;; + esac + done <"${ldsoconf}" + + do_trace "Finished parsing ld.so.conf: '%s'\n" "${ldsoconf}" +} + +# Build up the full list of search directories +declare -a needed_search_path +do_trace "Adding basic lib dirs\n" +ld_library_path="${ld_library_path}:" +while [ -n "${ld_library_path}" ]; do + d="${ld_library_path%%:*}" + if [ -n "${d}" ]; then + do_trace "-> adding search dir '%s'\n" "${d}" + needed_search_path+=( "${d}" ) + fi + ld_library_path="${ld_library_path#*:}" +done +do_trace "Done adding basic lib dirs\n" +do_trace "Scanning '/etc/ld.so.conf'\n" +do_scan_etc_ldsoconf "${root}/etc/ld.so.conf" +do_trace "Done scanning '/etc/ld.so.conf'\n" +do_trace "Search path:\n" +for p in "${needed_search_path[@]}"; do + do_trace "-> '%s'\n" "${p}" +done + +declare -a needed_list +declare -a search_rpath +do_trace "Scanning file '%s'\n" "${1}" +do_process_file "${1}" +do_trace "Done scanning file '%s'\n" "${1}" diff --git a/.ci/build_appimage.sh b/.ci/build_appimage.sh new file mode 100755 index 0000000..f558edb --- /dev/null +++ b/.ci/build_appimage.sh @@ -0,0 +1,89 @@ +#!/bin/bash + +sudo () +{ + [[ $EUID = 0 ]] || set -- command sudo "$@" + "$@" +} + +# Build +if [[ "$1" != "0" ]]; then + .ci/common/build.sh appimage_build linux || exit 1 +fi + +repo_dir=$(pwd) +cd appimage_build + +# Build linuxdeploy +sudo apt install -y build-essential g++ pkg-config curl wget libpng-dev libjpeg-dev zsync desktop-file-utils libxcb-cursor0 && +git clone https://github.com/Open-Typer/linuxdeploy linuxdeploy-build --recurse-submodules && +mkdir linuxdeploy-build/build +cd linuxdeploy-build/build && +# Add src/core/copyright to include paths (see https://github.com/linuxdeploy/linuxdeploy/issues/212) +cmake -DCMAKE_CXX_FLAGS=-isystem\ $(pwd)/../src/core/copyright .. && +make -j$(nproc --all) && +mv bin/linuxdeploy ../.. && +cd ../.. && +rm -rf linuxdeploy-build && + +# Build linuxdeploy-plugin-qt +sudo apt install -y nlohmann-json3-dev && +git clone https://github.com/Open-Typer/linuxdeploy-plugin-qt plugin-qt --recurse-submodules && +mkdir plugin-qt/build && +cd plugin-qt/build && +cmake .. && +make -j$(nproc --all) && +mv bin/linuxdeploy-plugin-qt ../.. && +cd ../.. && +rm -rf plugin-qt && + +# Build linuxdeploy-plugin-appimage +git clone https://github.com/linuxdeploy/linuxdeploy-plugin-appimage plugin-appimage --recurse-submodules && +mkdir plugin-appimage/build && +cd plugin-appimage/build && +cmake .. && +make -j$(nproc --all) && +mv src/linuxdeploy-plugin-appimage ../.. && +cd ../.. && +rm -rf plugin-appimage && + +# Build AppImageKit +sudo apt install -y snapd squashfs-tools && +sudo snap install docker && +git clone https://github.com/AppImage/AppImageKit --recurse-submodules && +cd AppImageKit && +sudo env ARCH=$(arch) bash ci/build.sh +sudo cp out/appimagetool /usr/bin/ && +sudo cp out/digest /usr/bin/ && +sudo cp out/validate /usr/bin/ && +cd .. && +sudo mkdir -p /usr/lib/appimagekit && +sudo ln -s /usr/bin/mksquashfs /usr/lib/appimagekit/mksquashfs && + +# Install patchelf from PyPI (see https://github.com/linuxdeploy/linuxdeploy-plugin-qt/issues/133#issuecomment-1608168363) +sudo apt install -y python3-pip +pip3 install patchelf +export PATH=$PATH:~/.local/bin + +# Build AppImage +export QML_SOURCES_PATHS=$(pwd)/src && +export EXTRA_QT_PLUGINS="svg;" && +export LDAI_UPDATE_INFORMATION="${appimage_zsync_prefix}${app_name}*-${APPIMAGE_ARCH-$(arch)}.AppImage.zsync" +echo "AppImage update information: ${LDAI_UPDATE_INFORMATION}" + +case "$(qmake -query QMAKE_XSPEC)" in + linux-arm-gnueabi-g++) + wget https://github.com/AppImage/AppImageKit/releases/download/continuous/runtime-armhf + export ARCH=arm + export LDAI_RUNTIME_FILE=runtime-armhf + ;; + linux-aarch64-gnu-g++) + wget https://github.com/AppImage/AppImageKit/releases/download/continuous/runtime-aarch64 + export ARCH=arm_aarch64 + export LDAI_RUNTIME_FILE=runtime-aarch64 + ;; +esac + +./linuxdeploy --appdir AppDir -e src/app/${executable_name} -i $repo_dir/res/${executable_name}.png -d $repo_dir/release/appimage.desktop --plugin qt --output appimage + +mv *.AppImage* $repo_dir diff --git a/.ci/build_qt6.sh b/.ci/build_qt6.sh new file mode 100755 index 0000000..8ccca89 --- /dev/null +++ b/.ci/build_qt6.sh @@ -0,0 +1,104 @@ +#!/bin/bash + +sudo apt install -y lsb-release + +qt_version="$1" +qt_modules="$(echo $2 | tr ' ' ',')" +target_arch="$3" +root_path="$(pwd)" +sysroot_path="${root_path}/sysroot" +sysroot_ubuntu_version="$(lsb_release -rs).1" +sysroot_ubuntu_codename="$(lsb_release -cs)" +host_prefix="${root_path}/qt-host" +cross_prefix="${root_path}/qt-cross" +target_prefix="/usr/local/qt" +toolchain_config="${root_path}/.ci/qt6-toolchain.cmake" + +case "$target_arch" in + aarch64) + target_arch_name="armv8-a" + toolchain_name="aarch64-linux-gnu" + target_platform="linux-aarch64-gnu-g++" + ;; + armv7) + target_arch_name="armv7-a" + toolchain_name="arm-linux-gnueabihf" + target_platform="linux-arm-gnueabi-g++" + ;; +esac + +toolchain_prefix="${toolchain_name}-" + +case "$target_arch" in + aarch64) + target_arch_debian_name="arm64" + ;; + armv7) + target_arch_debian_name="armhf" + ;; +esac + +echo "Qt version to build: ${qt_version}" +echo "Qt modules: ${qt_modules}" +echo "Target architecture: ${target_arch} (${target_arch_name})" + +# Install dependencies +${root_path}/.ci/qt6_deps.sh || exit 1 +${root_path}/.ci/install_cross_compiler.sh "${target_arch}" || exit 1 +sudo apt install -y qemu-user-static || exit 1 +sudo apt install -y symlinks || exit 1 + +# Clone Qt +git clone https://github.com/qt/qt5 qt || exit 1 +cd qt +git checkout "v$qt_version" || exit 1 +./init-repository --module-subset=qtbase,qttools,qtdeclarative,qtsvg,${qt_modules} || exit 1 + +# Build Qt (host) +mkdir host-build +cd host-build +echo "Building host Qt..." +../configure -release -nomake examples -nomake tests -opensource -confirm-license -prefix "$host_prefix" || exit 1 +cmake --build . --parallel $(nproc --all) || exit 1 +echo "Installing host Qt..." +cmake --install . || exit 1 +cd .. +rm -rf host-build + +# Prepare sysroot +echo "Preparing sysroot..." +curl "https://cdimage.ubuntu.com/ubuntu-base/releases/${sysroot_ubuntu_codename}/release/ubuntu-base-${sysroot_ubuntu_version}-base-${target_arch_debian_name}.tar.gz" > ./ubuntu-base.tar.gz || exit 1 +mkdir -p "$sysroot_path" +sudo tar -xvzf ubuntu-base.tar.gz -C "$sysroot_path" || exit 1 +sudo update-binfmts --enable qemu-arm || exit 1 +sudo mount -o bind /dev "${sysroot_path}/dev" || exit 1 +sudo cp /etc/resolv.conf "${sysroot_path}/etc" || exit 1 +sudo chmod 1777 "${sysroot_path}/tmp" || exit 1 +sudo cp "${root_path}/.ci/qt6_deps.sh" "${sysroot_path}/" +sudo chroot "$sysroot_path" /bin/bash -c "/qt6_deps.sh" || exit 1 +sudo chroot "$sysroot_path" /bin/bash -c "apt install -y symlinks && symlinks -rc /" || exit 1 + +# Build Qt (cross) +mkdir cross-build +cd cross-build +echo "Cross-compiling Qt..." +export BUILD_SYSROOT_PATH=${sysroot_path} +export BUILD_TOOLCHAIN_NAME=${toolchain_name} +export BUILD_TOOLCHAIN_PREFIX=${toolchain_prefix} +export BUILD_ARCH_NAME=${target_arch_name} +../configure -release -opengl es2 -nomake examples -nomake tests -qt-host-path "$host_prefix" -xplatform "$target_platform" \ + -device-option CROSS_COMPILE="$toolchain_prefix" -sysroot "$sysroot_path" -opensource -confirm-license \ + -prefix "$target_prefix" -extprefix "$cross_prefix" -- -DCMAKE_TOOLCHAIN_FILE="$toolchain_config" \ + -DQT_FEATURE_xcb=ON -DFEATURE_xcb_xlib=ON -DQT_FEATURE_xlib=ON || exit 1 +cmake --build . --parallel $(nproc --all) || exit 1 +echo "Installing cross-compiled Qt..." +cmake --install . || exit 1 +cd .. +rm -rf cross-build + +# Cleanup +sudo umount "${sysroot_path}/dev" || exit 1 +cd .. +rm -rf qt +# Required for cache +sudo chmod 777 -R ${sysroot_path} diff --git a/.ci/common/build.sh b/.ci/common/build.sh new file mode 100755 index 0000000..356594e --- /dev/null +++ b/.ci/common/build.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +BUILD_DIR=$1 +PLATFORM=$2 + +# exit codes: +# 1: unsupported platform +# 3: configuration failed +# 4: build failed + +mkdir -p "$BUILD_DIR" +cmake -B "$BUILD_DIR" -DCMAKE_BUILD_TYPE=Release -DSCRATCHCPP_PLAYER_BUILD_UNIT_TESTS=OFF || exit 3 + +if [[ "$PLATFORM" == "win32" ]]; then + cmake --build "$BUILD_DIR" -j4 || exit 4 +elif [[ "$PLATFORM" == "win64" ]]; then + cmake --build "$BUILD_DIR" -j4 || exit 4 +elif [[ "$PLATFORM" == "macos" ]]; then + cmake --build "$BUILD_DIR" -j6 || exit 4 +else + cmake --build "$BUILD_DIR" -j$(nproc --all) || exit 4 +fi diff --git a/.ci/install_cross_compiler.sh b/.ci/install_cross_compiler.sh new file mode 100755 index 0000000..cf70b6d --- /dev/null +++ b/.ci/install_cross_compiler.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +sudo apt update +case "$1" in + aarch64) + sudo apt install -y g++-aarch64-linux-gnu + ;; + armv7) + sudo apt install -y g++-arm-linux-gnueabihf +esac diff --git a/.ci/prepare_cross_build.sh b/.ci/prepare_cross_build.sh new file mode 100755 index 0000000..5588c2b --- /dev/null +++ b/.ci/prepare_cross_build.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +target_arch="$1" + +case "$target_arch" in + aarch64) + toolchain_prefix="aarch64-linux-gnu-" + echo "APPIMAGE_ARCH=aarch64" >> "${GITHUB_ENV}" + ;; + armv7) + toolchain_prefix="arm-linux-gnueabihf-" + echo "APPIMAGE_ARCH=armhf" >> "${GITHUB_ENV}" + ;; +esac + +echo "$(pwd)/qt-cross/bin:$(pwd)/qt-host/libexec" >> $GITHUB_PATH +echo "LD_LIBRARY_PATH=$(pwd)/qt-cross/lib:$(pwd)/qt-host/lib" >> "${GITHUB_ENV}" +.ci/install-cross-compiler.sh "$target_arch" +.ci/qt6-dependencies.sh +if [[ "$target_arch" == "armv7" ]]; then + echo "QMAKE_CC=arm-linux-gnueabihf-gcc + QMAKE_CXX=arm-linux-gnueabihf-g++ + QMAKE_LINK=arm-linux-gnueabihf-g++ + QMAKE_LINK_SHLIB=arm-linux-gnueabihf-g++ + QMAKE_AR=arm-linux-gnueabihf-ar cqs + QMAKE_OBJCOPY=arm-linux-gnueabihf-objcopy + QMAKE_NM=arm-linux-gnueabihf-nm -P + QMAKE_STRIP=arm-linux-gnueabihf-strip" >> .qmake.conf +fi + +# Prepare cross-tools for linuxdeploy +sudo cp /usr/bin/${toolchain_prefix}strip strip +sudo mv /usr/bin/ldd /usr/bin/ldd-amd64 +sudo cp .ci/bin/xldd /usr/bin/${toolchain_prefix}ldd +sudo ln -s /usr/bin/${toolchain_prefix}ldd /usr/bin/ldd +echo "CT_XLDD_ROOT=$(pwd)/sysroot" >> "${GITHUB_ENV}" diff --git a/.ci/qt6_deps.sh b/.ci/qt6_deps.sh new file mode 100755 index 0000000..15d564a --- /dev/null +++ b/.ci/qt6_deps.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +sudo () +{ + [[ $EUID = 0 ]] || set -- command sudo "$@" + "$@" +} + +sudo apt-get clean +sudo apt-get update + +sudo apt-get upgrade -y + +DEBIAN_FRONTEND=noninteractive apt-get install -y tzdata +dpkg-reconfigure --frontend noninteractive tzdata + +sudo apt install -y libboost-all-dev libudev-dev libinput-dev libts-dev \ + libmtdev-dev libjpeg-dev libfontconfig1-dev libssl-dev \ + libdbus-1-dev libglib2.0-dev libxkbcommon-dev libegl1-mesa-dev \ + libgbm-dev libgles2-mesa-dev mesa-common-dev libasound2-dev \ + libpulse-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ + gstreamer1.0-alsa libvpx-dev libsrtp2-dev libsnappy-dev \ + libnss3-dev "^libxcb.*" flex bison libxslt-dev ruby gperf \ + libbz2-dev libcups2-dev libatkmm-1.6-dev libxi6 libxcomposite1 \ + libfreetype6-dev libicu-dev libsqlite3-dev libxslt1-dev \ + libavcodec-dev libavformat-dev libswscale-dev libx11-dev \ + freetds-dev libsqlite3-dev libpq-dev libiodbc2 libiodbc2-dev firebird-dev \ + libgst-dev libxext-dev libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev \ + libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev \ + libxcb-shm0 libxcb-shm0-dev libxcb-icccm4 libxcb-icccm4-dev \ + libxcb-sync1 libxcb-sync-dev libxcb-render-util0 libxcb-render-util0-dev \ + libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev \ + libxcb-glx0-dev libxi-dev libdrm-dev libxcb-xinerama0 libxcb-xinerama0-dev \ + libatspi2.0-dev libxcursor-dev libxcomposite-dev libxdamage-dev libxss-dev \ + libxtst-dev libpci-dev libcap-dev libxrandr-dev libdirectfb-dev libaudio-dev \ + libxkbcommon-x11-dev libclang-dev libclang-12-dev || exit 1 + +#sudo apt install -y gstreamer1.0-omx || exit 1 diff --git a/.github/config.env b/.github/config.env new file mode 100644 index 0000000..b215257 --- /dev/null +++ b/.github/config.env @@ -0,0 +1,46 @@ +# The documentation repository name +# User name defaults to github.repository_owner and can be changed in .github/workflows/docs.yml +docs_repo=docs + +# Whether to build documentation +build_docs=0 + +# Whether to deploy documentation to the docs repository (requires build_docs=1) +deploy_docs=0 + +# The executable name, should be same as the TARGET value in the .pro file +executable_name=scratchcpp-player + +# Application name +app_name=ScratchCPP Player + +# AppImage update information prefix +appimage_zsync_prefix=gh-releases-zsync|scratchcpp|scratchcpp-player|latest| + +# Whether to build when a new tag is pushed +build_on_new_tags=1 + +# Whether to create an installer for Windows +create_windows_installer=1 + +# App name used by the installer for Windows (must be same as in the installer configuration) +windows_app_name=com.scratchcpp.player + +# Whether to update version and release date in the Windows installer configuration (requires create_windows_installer=1) +# Needs a PUSH_TOKEN secret with a personal access token +update_windows_installer=1 + +# Whether to upload new release to the Windows release repository (requires create_windows_installer=1) +# Needs a PUSH_TOKEN secret with a personal access token +update_windows_repository=1 + +# Whether to upload WebAssembly build to the online app repository +# Needs a PUSH_TOKEN secret with a personal access token +upload_online_app=1 + +# Online app repository name +# User name defaults to github.repository_owner and can be changed in .github/workflows/release.yml +online_app_repo=web-app + +# Whether to automatically create a release on new tags (requires build_on_new_tags=1) +create_release=1 diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml new file mode 100644 index 0000000..f0e578a --- /dev/null +++ b/.github/workflows/linux-build.yml @@ -0,0 +1,77 @@ +name: Linux build + +on: + push: + branches: '*' + tags: '*' + pull_request: + types: [opened, reopened, synchronize, edited] + +jobs: + build: + runs-on: ubuntu-20.04 + strategy: + matrix: + qt-version: ['6.6'] + qt-target: ['desktop'] + qt-modules: ['qtshadertools'] + arch: ['amd64'] + ubuntu-version: ['20.04'] + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + submodules: 'recursive' + - name: Setup environment + run: | + sed -i -e '/^#/d' .github/config.env + sed -i -e '/^$/d' .github/config.env + cat .github/config.env >> "${GITHUB_ENV}" + shell: bash + - if: contains(matrix.arch, 'amd64') + name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y libxkbcommon-x11-0 + shell: bash + ## Install Qt + - if: contains(matrix.arch, 'amd64') + name: Install Qt (Ubuntu) + uses: jurplel/install-qt-action@v3 + with: + version: ${{ matrix.qt-version }} + host: 'linux' + target: ${{ matrix.qt-target }} + modules: ${{ matrix.qt-modules }} + - if: "!contains(matrix.arch, 'amd64')" + name: Restore cross-compiled Qt from cache + id: cache-qt-cross + uses: actions/cache@v3 + with: + path: | + ./qt-host/ + ./qt-cross/ + ./sysroot/ + key: qt-cross-${{ runner.os }}-${{ matrix.qt-version }}-${{ matrix.qt-target }}-${{ matrix.qt-modules }}-${{ matrix.arch }} + restore-keys: + qt-cross-${{ runner.os }}-${{ matrix.qt-version }}-${{ matrix.qt-target }}-${{ matrix.qt-modules }}-${{ matrix.arch }} + - if: "!contains(matrix.arch, 'amd64') && steps.cache-qt-cross.outputs.cache-hit != 'true'" + name: Cross-compile Qt + shell: bash + run: .ci/build_qt6.sh "${{ matrix.qt-version }}" "${{ matrix.qt-modules }}" "${{ matrix.arch }}" + ## Build + - if: "!contains(matrix.arch, 'amd64')" + name: Prepare cross-compilation environment + run: .ci/prepare_cross_build.sh "${{ matrix.arch }}" + shell: bash + - name: Build AppImage + run: .ci/build_appimage.sh + shell: 'script -q -e -c "bash {0}"' + ## Upload + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: build-Qt-${{ matrix.qt-version }}-${{ matrix.arch }} + path: | + *.AppImage + *.zsync diff --git a/release/appimage.desktop b/release/appimage.desktop new file mode 100644 index 0000000..da9634f --- /dev/null +++ b/release/appimage.desktop @@ -0,0 +1,9 @@ +#!/usr/bin/env xdg-open +[Desktop Entry] +Name=ScratchCPP Player +Exec=scratchcpp-player %U +Terminal=false +Type=Application +Icon=scratchcpp-player +Comment=Scratch project player written in C++ and QML +Categories=Utility; diff --git a/res/scratchcpp-player.png b/res/scratchcpp-player.png new file mode 100644 index 0000000..87d1385 Binary files /dev/null and b/res/scratchcpp-player.png differ diff --git a/scratchcpp-render b/scratchcpp-render index 1d0b5d8..4b66e98 160000 --- a/scratchcpp-render +++ b/scratchcpp-render @@ -1 +1 @@ -Subproject commit 1d0b5d8c55d7c5fe60a7ae91fae97cd6f25d28d8 +Subproject commit 4b66e98f46846edd85f0787c1371c494bbb64a79 diff --git a/src/app/app.cpp b/src/app/app.cpp index 7af09a3..3067f48 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "app.h" #include "globalmodule.h" @@ -21,7 +22,8 @@ App::App() int App::run(int argc, char **argv) { - qputenv("QSG_RENDER_LOOP", "basic"); + // Init scratchcpp-render + scratchcpprender::init(); // Set up application object QApplication app(argc, argv); diff --git a/src/app/qml/main.qml b/src/app/qml/main.qml index 1f36ac2..7fe09f1 100644 --- a/src/app/qml/main.qml +++ b/src/app/qml/main.qml @@ -124,7 +124,6 @@ ApplicationWindow { Layout.minimumHeight: stageHeight activeFocusOnTab: true focus: true - spriteFencing: false turboMode: AppMenuBar.turboMode fps: AppMenuBar.fps60Mode ? 60 : 30 } diff --git a/src/global/test/CMakeLists.txt b/src/global/test/CMakeLists.txt index 96b56fd..c7d28e9 100644 --- a/src/global/test/CMakeLists.txt +++ b/src/global/test/CMakeLists.txt @@ -10,7 +10,9 @@ set(MODULE_TEST_SRC include(${PROJECT_SOURCE_DIR}/build/module_test.cmake) -set(TARGET ${MODULE}_test) -target_link_libraries(${TARGET} cmake_git_version_tracking) -string(TIMESTAMP BUILD_YEAR "%Y") -target_compile_definitions(${TARGET} PRIVATE BUILD_YEAR=${BUILD_YEAR}) +if (SCRATCHCPP_PLAYER_BUILD_UNIT_TESTS) + set(TARGET ${MODULE}_test) + target_link_libraries(${TARGET} cmake_git_version_tracking) + string(TIMESTAMP BUILD_YEAR "%Y") + target_compile_definitions(${TARGET} PRIVATE BUILD_YEAR=${BUILD_YEAR}) +endif() diff --git a/src/uicomponents/CustomMenuBar.qml b/src/uicomponents/CustomMenuBar.qml index 4085dfa..bfb7f96 100644 --- a/src/uicomponents/CustomMenuBar.qml +++ b/src/uicomponents/CustomMenuBar.qml @@ -21,7 +21,8 @@ MenuBar { } function getComponentString(typeName) { - var imports = "import QtQuick; import QtQuick.Controls; import Qt.labs.platform as Platform;" + //var imports = "import QtQuick; import QtQuick.Controls; import Qt.labs.platform as Platform;" + var imports = "import QtQuick; import QtQuick.Controls;" return imports + " " + typeName + " {}"; }