mirror of
https://github.com/citron-neo/emulator.git
synced 2026-07-05 15:21:57 -07:00
Revert "build(linux): add pkgforge AppImage builder, fix aarch64/Qt/libusb, and clean up obsolete scripts"
This commit is contained in:
+435
@@ -0,0 +1,435 @@
|
||||
# GitLab CI config for Citron - DISABLED
|
||||
# This CI configuration has been disabled as GitHub Actions is now being used
|
||||
# To re-enable, uncomment the configuration below
|
||||
|
||||
# stages:
|
||||
# - build
|
||||
# - test
|
||||
# - package
|
||||
|
||||
# variables:
|
||||
# BUILD_TYPE: "Release"
|
||||
# CMAKE_BUILD_PARALLEL_LEVEL: "4"
|
||||
# DOCKER_DRIVER: overlay2
|
||||
# DOCKER_TLS_CERTDIR: ""
|
||||
|
||||
# # Cache configuration for faster builds
|
||||
# cache:
|
||||
# paths:
|
||||
# - build/vcpkg_installed/
|
||||
# - build/_deps/
|
||||
# key: "$CI_COMMIT_REF_SLUG"
|
||||
# policy: pull-push
|
||||
|
||||
|
||||
# # Linux Build (CachyOS optimized)
|
||||
# build-linux:
|
||||
# stage: build
|
||||
# image: cachyos/cachyos:latest
|
||||
# tags:
|
||||
# - citron-build
|
||||
# before_script:
|
||||
# - pacman -Syu --noconfirm
|
||||
# - pacman -S --noconfirm alsa-lib base-devel boost boost-libs catch2 cmake curl ffmpeg fmt fuse2 gamemode gcc gdb git glslang glu libusb libxi libxkbcommon libxkbcommon-x11 libxss libzip lz4 mbedtls2 mesa nasm ninja nlohmann-json openal openssl opus perl pipewire-audio pulseaudio pulseaudio-alsa qt6-base qt6-multimedia qt6-tools qt6-wayland sdl2 sdl2-compat unzip vulkan-headers vulkan-icd-loader vulkan-mesa-layers wget xcb-util-cursor xcb-util-image xcb-util-keysyms xcb-util-renderutil xcb-util-wm xorg-server-xvfb zip zstd zsync
|
||||
# - echo '#!/bin/bash' > /usr/local/bin/shasum
|
||||
# - echo 'if [ "$1" = "-a" ] && [ "$2" = "256" ]; then' >> /usr/local/bin/shasum
|
||||
# - echo ' shift 2' >> /usr/local/bin/shasum
|
||||
# - echo ' sha256sum "$@"' >> /usr/local/bin/shasum
|
||||
# - echo 'else' >> /usr/local/bin/shasum
|
||||
# - echo ' sha256sum "$@"' >> /usr/local/bin/shasum
|
||||
# - echo 'fi' >> /usr/local/bin/shasum
|
||||
# - chmod +x /usr/local/bin/shasum
|
||||
# script: |
|
||||
# # Clean and reset submodules to handle any inconsistencies
|
||||
# git submodule deinit --all --force || true
|
||||
# # Forcefully remove stale lock files that can block submodule updates
|
||||
# find .git/modules -type f -name "*.lock" -delete
|
||||
# git submodule update --init --recursive --force
|
||||
# mkdir -p build && cd build
|
||||
# # Dynamically find the Qt private header path
|
||||
# HEADER_PATH=$(pacman -Ql qt6-base | grep 'qpa/qplatformnativeinterface.h$' | awk '{print $2}')
|
||||
# if [ -z "$HEADER_PATH" ]; then echo "ERROR: Could not find qplatformnativeinterface.h path." >&2; exit 1; fi
|
||||
# QT_PRIVATE_INCLUDE_DIR=$(dirname "$(dirname "$HEADER_PATH")")
|
||||
# echo "Found Qt private include directory: ${QT_PRIVATE_INCLUDE_DIR}"
|
||||
# # Configure and build with the dynamically found path
|
||||
# cmake .. \
|
||||
# -G Ninja \
|
||||
# -DCMAKE_C_COMPILER=gcc \
|
||||
# -DCMAKE_CXX_COMPILER=g++ \
|
||||
# -DCMAKE_BUILD_TYPE=Release \
|
||||
# -DCMAKE_INSTALL_PREFIX=/usr \
|
||||
# -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
||||
# -DUSE_SYSTEM_QT=ON \
|
||||
# -DCMAKE_C_FLAGS="-O3 -ffast-math -funsafe-math-optimizations -fno-math-errno -fno-trapping-math -freciprocal-math -ffp-contract=fast -fassociative-math -fno-rounding-math -fno-signaling-nans -march=x86-64-v2 -mtune=native -fomit-frame-pointer -fstrict-aliasing -frename-registers -fno-plt -funroll-loops -floop-interchange -floop-strip-mine -floop-block -fprefetch-loop-arrays -fpredictive-commoning -ftree-vectorize -fsplit-paths -finline-functions -finline-limit=1000 -finline-functions-called-once -fgcse-sm -fgcse-las -freorder-blocks-and-partition -fipa-pta -fipa-cp-clone -fwhole-program -flto=auto -fuse-linker-plugin -fvisibility=hidden -fdata-sections -ffunction-sections -fno-semantic-interposition -fopenmp -fexceptions -frtti" \
|
||||
# -DCMAKE_CXX_FLAGS="-I${QT_PRIVATE_INCLUDE_DIR} -O3 -ffast-math -funsafe-math-optimizations -fno-math-errno -fno-trapping-math -freciprocal-math -ffp-contract=fast -fassociative-math -fno-rounding-math -fno-signaling-nans -march=x86-64-v2 -mtune=native -fomit-frame-pointer -fstrict-aliasing -frename-registers -fno-plt -funroll-loops -floop-interchange -floop-strip-mine -floop-block -fprefetch-loop-arrays -fpredictive-commoning -ftree-vectorize -fsplit-paths -finline-functions -finline-limit=1000 -finline-functions-called-once -fgcse-sm -fgcse-las -freorder-blocks-and-partition -fipa-pta -fipa-cp-clone -fwhole-program -flto=auto -fuse-linker-plugin -fvisibility=hidden -fdata-sections -ffunction-sections -fno-semantic-interposition -fopenmp -fexceptions -frtti -Wno-error -w" \
|
||||
# -DCMAKE_EXE_LINKER_FLAGS="-Wl,--gc-sections -Wl,-O2 -Wl,--as-needed" \
|
||||
# -DSDL_PIPEWIRE=OFF \
|
||||
# -DCITRON_USE_BUNDLED_VCPKG=OFF \
|
||||
# -DUSE_DISCORD_PRESENCE=OFF \
|
||||
# -DBUNDLE_SPEEX=ON \
|
||||
# -DCITRON_USE_BUNDLED_FFMPEG=OFF \
|
||||
# -DCITRON_USE_QT_MULTIMEDIA=OFF \
|
||||
# -DCITRON_USE_QT_WEB_ENGINE=OFF
|
||||
# ninja
|
||||
# cd ..
|
||||
# echo "Build completed, checking for executables..."
|
||||
# ls -la build/bin/ || echo "No build/bin directory found"
|
||||
# find build/ -name "*.so" -o -name "citron*" | head -20 || echo "No shared libraries or executables found"
|
||||
# echo "Checking library dependencies..."
|
||||
# ldd build/bin/citron || echo "citron binary not found or ldd failed"
|
||||
# chmod +x AppImage-build-local.sh
|
||||
# echo "Attempting AppImage build with FUSE workaround..."
|
||||
# export APPIMAGE_EXTRACT_AND_RUN=1
|
||||
# export ARCH=x86_64
|
||||
# export VERSION=$(git describe --tags --always)
|
||||
# echo "Testing shasum compatibility..."
|
||||
# which shasum || echo "shasum not found in PATH"
|
||||
# echo "Testing shasum -a 256 syntax..."
|
||||
# echo "test" | shasum -a 256 || echo "shasum -a 256 failed"
|
||||
# echo "test" | sha256sum || echo "sha256sum failed"
|
||||
# echo "Running AppImage build..."
|
||||
# ./AppImage-build-local.sh || echo "AppImage build failed, continuing..."
|
||||
# ls -la *.AppImage || echo "No AppImage files found"
|
||||
# echo "AppImage size information:"
|
||||
# du -h *.AppImage || echo "No AppImage to measure"
|
||||
# echo "Total artifact size:"
|
||||
# du -sh citron.AppImage build/bin/citron 2>/dev/null || echo "Cannot measure artifact sizes"
|
||||
# echo "Compressing AppImage for upload..."
|
||||
# gzip -9 -c citron.AppImage > citron.AppImage.gz
|
||||
# ls -la citron.AppImage.gz
|
||||
# du -h citron.AppImage.gz
|
||||
# echo "AppImage compressed with gzip successfully"
|
||||
# tar -czf citron-binary.tar.gz build/bin/
|
||||
# ls -la citron-binary.tar.gz
|
||||
# du -h citron-binary.tar.gz
|
||||
# artifacts:
|
||||
# paths:
|
||||
# - citron.AppImage.gz
|
||||
# - citron-binary.tar.gz
|
||||
# expire_in: 1 week
|
||||
# when: always
|
||||
# only:
|
||||
# - main
|
||||
# - master
|
||||
# - develop
|
||||
# - ci-fixes
|
||||
# - Boss-Build-Flags-For-CI-CMAKE-ON-CachyOS
|
||||
# - merge_requests
|
||||
|
||||
# # Linux Build (CachyOS v3 optimized)
|
||||
# build-linux-v3:
|
||||
# stage: build
|
||||
# image: cachyos/cachyos-v3
|
||||
# tags:
|
||||
# - citron-build
|
||||
# before_script:
|
||||
# - pacman -Syu --noconfirm
|
||||
# - pacman -S --noconfirm alsa-lib base-devel boost boost-libs catch2 cmake curl ffmpeg fmt fuse2 gamemode gcc gdb git glslang glu libusb libxi libxkbcommon libxkbcommon-x11 libxss libzip lz4 mbedtls2 mesa nasm ninja nlohmann-json openal openssl opus perl pipewire-audio pulseaudio pulseaudio-alsa python-pip qt6-base qt6-multimedia qt6-tools qt6-wayland sdl2 sdl2-compat unzip vulkan-headers vulkan-icd-loader vulkan-mesa-layers wget xcb-util-cursor xcb-util-image xcb-util-keysyms xcb-util-renderutil xcb-util-wm xorg-server-xvfb zip zstd zsync
|
||||
# - echo '#!/bin/bash' > /usr/local/bin/shasum
|
||||
# - echo 'if [ "$1" = "-a" ] && [ "$2" = "256" ]; then' >> /usr/local/bin/shasum
|
||||
# - echo ' shift 2' >> /usr/local/bin/shasum
|
||||
# - echo ' sha256sum "$@"' >> /usr/local/bin/shasum
|
||||
# - echo 'else' >> /usr/local/bin/shasum
|
||||
# - echo ' sha256sum "$@"' >> /usr/local/bin/shasum
|
||||
# - echo 'fi' >> /usr/local/bin/shasum
|
||||
# - chmod +x /usr/local/bin/shasum
|
||||
# script: |
|
||||
# git submodule deinit --all --force || true
|
||||
# find .git/modules -type f -name "*.lock" -delete
|
||||
# git submodule update --init --recursive --force
|
||||
# mkdir -p build && cd build
|
||||
# HEADER_PATH=$(pacman -Ql qt6-base | grep 'qpa/qplatformnativeinterface.h$' | awk '{print $2}')
|
||||
# if [ -z "$HEADER_PATH" ]; then echo "ERROR: Could not find qplatformnativeinterface.h path." >&2; exit 1; fi
|
||||
# QT_PRIVATE_INCLUDE_DIR=$(dirname "$(dirname "$HEADER_PATH")")
|
||||
# echo "Found Qt private include directory: ${QT_PRIVATE_INCLUDE_DIR}"
|
||||
# cmake .. \
|
||||
# -G Ninja \
|
||||
# -DCMAKE_C_COMPILER=gcc \
|
||||
# -DCMAKE_CXX_COMPILER=g++ \
|
||||
# -DCMAKE_BUILD_TYPE=Release \
|
||||
# -DCMAKE_INSTALL_PREFIX=/usr \
|
||||
# -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
||||
# -DCMAKE_C_FLAGS="-O3 -ffast-math -funsafe-math-optimizations -fno-math-errno -fno-trapping-math -freciprocal-math -ffp-contract=fast -fassociative-math -fno-rounding-math -fno-signaling-nans -march=x86-64-v3 -mtune=native -fomit-frame-pointer -fstrict-aliasing -frename-registers -fno-plt -funroll-loops -floop-interchange -floop-strip-mine -floop-block -fprefetch-loop-arrays -fpredictive-commoning -ftree-vectorize -fsplit-paths -finline-functions -finline-limit=1000 -finline-functions-called-once -fgcse-sm -fgcse-las -freorder-blocks-and-partition -fipa-pta -fipa-cp-clone -fwhole-program -flto=auto -fuse-linker-plugin -fvisibility=hidden -fdata-sections -ffunction-sections -fno-semantic-interposition -fopenmp -fexceptions -frtti" \
|
||||
# -DCMAKE_CXX_FLAGS="-I${QT_PRIVATE_INCLUDE_DIR} -O3 -ffast-math -funsafe-math-optimizations -fno-math-errno -fno-trapping-math -freciprocal-math -ffp-contract=fast -fassociative-math -fno-rounding-math -fno-signaling-nans -march=x86-64-v3 -mtune=native -fomit-frame-pointer -fstrict-aliasing -frename-registers -fno-plt -funroll-loops -floop-interchange -floop-strip-mine -floop-block -fprefetch-loop-arrays -fpredictive-commoning -ftree-vectorize -fsplit-paths -finline-functions -finline-limit=1000 -finline-functions-called-once -fgcse-sm -fgcse-las -freorder-blocks-and-partition -fipa-pta -fipa-cp-clone -fwhole-program -flto=auto -fuse-linker-plugin -fvisibility=hidden -fdata-sections -ffunction-sections -fno-semantic-interposition -fopenmp -fexceptions -frtti -Wno-error -w" \
|
||||
# -DCMAKE_EXE_LINKER_FLAGS="-Wl,--gc-sections -Wl,-O2 -Wl,--as-needed" \
|
||||
# -DSDL_PIPEWIRE=OFF \
|
||||
# -DCITRON_USE_BUNDLED_VCPKG=OFF \
|
||||
# -DUSE_DISCORD_PRESENCE=OFF \
|
||||
# -DBUNDLE_SPEEX=ON \
|
||||
# -DCITRON_USE_BUNDLED_FFMPEG=OFF \
|
||||
# -DCITRON_USE_QT_MULTIMEDIA=OFF \
|
||||
# -DCITRON_USE_QT_WEB_ENGINE=OFF
|
||||
# ninja
|
||||
# cd ..
|
||||
# echo "Build completed, checking for executables..."
|
||||
# ls -la build/bin/ || echo "No build/bin directory found"
|
||||
# find build/ -name "*.so" -o -name "citron*" | head -20 || echo "No shared libraries or executables found"
|
||||
# echo "Checking library dependencies..."
|
||||
# ldd build/bin/citron || echo "citron binary not found or ldd failed"
|
||||
# chmod +x AppImage-build-local.sh
|
||||
# echo "Attempting AppImage build with FUSE workaround..."
|
||||
# export APPIMAGE_EXTRACT_AND_RUN=1
|
||||
# export ARCH=x86_64
|
||||
# export VERSION=$(git describe --tags --always)
|
||||
# echo "Testing shasum compatibility..."
|
||||
# which shasum || echo "shasum not found in PATH"
|
||||
# echo "Testing shasum -a 256 syntax..."
|
||||
# echo "test" | shasum -a 256 || echo "shasum -a 256 failed"
|
||||
# echo "test" | sha256sum || echo "sha256sum failed"
|
||||
# echo "Running AppImage build..."
|
||||
# ./AppImage-build-local.sh || echo "AppImage build failed, continuing..."
|
||||
# ls -la *.AppImage || echo "No AppImage files found"
|
||||
# echo "AppImage size information:"
|
||||
# du -h *.AppImage || echo "No AppImage to measure"
|
||||
# echo "Total artifact size:"
|
||||
# du -sh citron.AppImage build/bin/citron 2>/dev/null || echo "Cannot measure artifact sizes"
|
||||
# echo "Compressing AppImage for upload..."
|
||||
# gzip -9 -c citron.AppImage > citron.AppImage.gz
|
||||
# ls -la citron.AppImage.gz
|
||||
# du -h citron.AppImage.gz
|
||||
# echo "AppImage compressed with gzip successfully"
|
||||
# tar -czf citron-binary.tar.gz build/bin/
|
||||
# ls -la citron-binary.tar.gz
|
||||
# du -h citron-binary.tar.gz
|
||||
# artifacts:
|
||||
# paths:
|
||||
# - citron.AppImage.gz
|
||||
# - citron-binary.tar.gz
|
||||
# expire_in: 1 week
|
||||
# when: always
|
||||
# only:
|
||||
# - main
|
||||
# - master
|
||||
# - develop
|
||||
# - ci-fixes
|
||||
# - Boss-Build-Flags-For-CI-CMAKE-ON-CachyOS
|
||||
# - merge_requests
|
||||
|
||||
# # Linux Build (Steam Deck optimized - AMD Zen 2)
|
||||
# build-linux-steamdeck:
|
||||
# stage: build
|
||||
# image: cachyos/cachyos:latest
|
||||
# tags:
|
||||
# - citron-build
|
||||
# before_script:
|
||||
# - pacman -Syu --noconfirm
|
||||
# - pacman -S --noconfirm alsa-lib base-devel boost boost-libs catch2 cmake curl ffmpeg fmt fuse2 gamemode gcc gdb git glslang glu libusb libxi libxkbcommon libxkbcommon-x11 libxss libzip lz4 mbedtls2 mesa nasm ninja nlohmann-json openal openssl opus perl pipewire-audio pulseaudio pulseaudio-alsa python-pip qt6-base qt6-multimedia qt6-tools qt6-wayland sdl2 sdl2-compat unzip vulkan-headers vulkan-icd-loader vulkan-mesa-layers wget xcb-util-cursor xcb-util-image xcb-util-keysyms xcb-util-renderutil xcb-util-wm xorg-server-xvfb zip zstd zsync
|
||||
# - echo '#!/bin/bash' > /usr/local/bin/shasum
|
||||
# - echo 'if [ "$1" = "-a" ] && [ "$2" = "256" ]; then' >> /usr/local/bin/shasum
|
||||
# - echo ' shift 2' >> /usr/local/bin/shasum
|
||||
# - echo ' sha256sum "$@"' >> /usr/local/bin/shasum
|
||||
# - echo 'else' >> /usr/local/bin/shasum
|
||||
# - echo ' sha256sum "$@"' >> /usr/local/bin/shasum
|
||||
# - echo 'fi' >> /usr/local/bin/shasum
|
||||
# - chmod +x /usr/local/bin/shasum
|
||||
# script: |
|
||||
# git submodule deinit --all --force || true
|
||||
# find .git/modules -type f -name "*.lock" -delete
|
||||
# git submodule update --init --recursive --force
|
||||
# mkdir -p build && cd build
|
||||
# HEADER_PATH=$(pacman -Ql qt6-base | grep 'qpa/qplatformnativeinterface.h$' | awk '{print $2}')
|
||||
# if [ -z "$HEADER_PATH" ]; then echo "ERROR: Could not find qplatformnativeinterface.h path." >&2; exit 1; fi
|
||||
# QT_PRIVATE_INCLUDE_DIR=$(dirname "$(dirname "$HEADER_PATH")")
|
||||
# echo "Found Qt private include directory: ${QT_PRIVATE_INCLUDE_DIR}"
|
||||
# cmake .. \
|
||||
# -G Ninja \
|
||||
# -DCMAKE_C_COMPILER=gcc \
|
||||
# -DCMAKE_CXX_COMPILER=g++ \
|
||||
# -DCMAKE_BUILD_TYPE=Release \
|
||||
# -DCMAKE_INSTALL_PREFIX=/usr \
|
||||
# -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
||||
# -DCMAKE_C_FLAGS="-O3 -ffast-math -funsafe-math-optimizations -fno-math-errno -fno-trapping-math -freciprocal-math -ffp-contract=fast -fassociative-math -fno-rounding-math -fno-signaling-nans -march=znver2 -mtune=znver2 -fomit-frame-pointer -fstrict-aliasing -frename-registers -fno-plt -funroll-loops -floop-interchange -floop-strip-mine -floop-block -fprefetch-loop-arrays -fpredictive-commoning -ftree-vectorize -fsplit-paths -finline-functions -finline-limit=1000 -finline-functions-called-once -fgcse-sm -fgcse-las -freorder-blocks-and-partition -fipa-pta -fipa-cp-clone -fwhole-program -flto=auto -fuse-linker-plugin -fvisibility=hidden -fdata-sections -ffunction-sections -fno-semantic-interposition -fopenmp -fexceptions -frtti" \
|
||||
# -DCMAKE_CXX_FLAGS="-I${QT_PRIVATE_INCLUDE_DIR} -O3 -ffast-math -funsafe-math-optimizations -fno-math-errno -fno-trapping-math -freciprocal-math -ffp-contract=fast -fassociative-math -fno-rounding-math -fno-signaling-nans -march=znver2 -mtune=znver2 -fomit-frame-pointer -fstrict-aliasing -frename-registers -fno-plt -funroll-loops -floop-interchange -floop-strip-mine -floop-block -fprefetch-loop-arrays -fpredictive-commoning -ftree-vectorize -fsplit-paths -finline-functions -finline-limit=1000 -finline-functions-called-once -fgcse-sm -fgcse-las -freorder-blocks-and-partition -fipa-pta -fipa-cp-clone -fwhole-program -flto=auto -fuse-linker-plugin -fvisibility=hidden -fdata-sections -ffunction-sections -fno-semantic-interposition -fopenmp -fexceptions -frtti -Wno-error -w" \
|
||||
# -DCMAKE_EXE_LINKER_FLAGS="-Wl,--gc-sections -Wl,-O2 -Wl,--as-needed" \
|
||||
# -DSDL_PIPEWIRE=OFF \
|
||||
# -DCITRON_USE_BUNDLED_VCPKG=OFF \
|
||||
# -DUSE_DISCORD_PRESENCE=OFF \
|
||||
# -DBUNDLE_SPEEX=ON \
|
||||
# -DCITRON_USE_BUNDLED_FFMPEG=OFF \
|
||||
# -DCITRON_USE_QT_MULTIMEDIA=OFF \
|
||||
# -DCITRON_USE_QT_WEB_ENGINE=OFF
|
||||
# ninja
|
||||
# cd ..
|
||||
# echo "Build completed, checking for executables..."
|
||||
# ls -la build/bin/ || echo "No build/bin directory found"
|
||||
# find build/ -name "*.so" -o -name "citron*" | head -20 || echo "No shared libraries or executables found"
|
||||
# echo "Checking library dependencies..."
|
||||
# ldd build/bin/citron || echo "citron binary not found or ldd failed"
|
||||
# chmod +x AppImage-build-local.sh
|
||||
# echo "Attempting AppImage build with FUSE workaround..."
|
||||
# export APPIMAGE_EXTRACT_AND_RUN=1
|
||||
# export ARCH=x86_64
|
||||
# export VERSION=$(git describe --tags --always)
|
||||
# echo "Testing shasum compatibility..."
|
||||
# which shasum || echo "shasum not found in PATH"
|
||||
# echo "Testing shasum -a 256 syntax..."
|
||||
# echo "test" | shasum -a 256 || echo "shasum -a 256 failed"
|
||||
# echo "test" | sha256sum || echo "sha256sum failed"
|
||||
# echo "Running AppImage build..."
|
||||
# ./AppImage-build-local.sh || echo "AppImage build failed, continuing..."
|
||||
# ls -la *.AppImage || echo "No AppImage files found"
|
||||
# echo "AppImage size information:"
|
||||
# du -h *.AppImage || echo "No AppImage to measure"
|
||||
# echo "Total artifact size:"
|
||||
# du -sh citron.AppImage build/bin/citron 2>/dev/null || echo "Cannot measure artifact sizes"
|
||||
# echo "Compressing AppImage for upload..."
|
||||
# gzip -9 -c citron.AppImage > citron.AppImage.gz
|
||||
# ls -la citron.AppImage.gz
|
||||
# du -h citron.AppImage.gz
|
||||
# echo "AppImage compressed with gzip successfully"
|
||||
# tar -czf citron-binary.tar.gz build/bin/
|
||||
# ls -la citron-binary.tar.gz
|
||||
# du -h citron-binary.tar.gz
|
||||
# artifacts:
|
||||
# paths:
|
||||
# - citron.AppImage.gz
|
||||
# - citron-binary.tar.gz
|
||||
# expire_in: 1 week
|
||||
# when: always
|
||||
# only:
|
||||
# - main
|
||||
# - master
|
||||
# - develop
|
||||
# - ci-fixes
|
||||
# - Boss-Build-Flags-For-CI-CMAKE-ON-CachyOS
|
||||
# - merge_requests
|
||||
|
||||
# # Android Build (Ubuntu 24.04 optimized)
|
||||
# build-android:
|
||||
# stage: build
|
||||
# image: ubuntu:24.04
|
||||
# tags:
|
||||
# - citron-build
|
||||
# before_script:
|
||||
# - apt-get update -qq
|
||||
# - apt-get install -y -qq openjdk-17-jdk wget unzip curl git cmake build-essential pkg-config zip glslang-tools
|
||||
# - wget -q https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip
|
||||
# - unzip -q commandlinetools-linux-11076708_latest.zip
|
||||
# - mkdir -p android-sdk/cmdline-tools
|
||||
# - mv cmdline-tools android-sdk/cmdline-tools/latest
|
||||
# - export ANDROID_SDK_ROOT=$PWD/android-sdk
|
||||
# - export PATH=$PATH:$ANDROID_SDK_ROOT/cmdline-tools/latest/bin
|
||||
# - printf "y\ny\ny\ny\ny\ny\ny\n" | sdkmanager --licenses || true
|
||||
# - sdkmanager "platform-tools" "platforms;android-34" "build-tools;34.0.0"
|
||||
# - wget -q https://dl.google.com/android/repository/android-ndk-r26b-linux.zip
|
||||
# - unzip -q android-ndk-r26b-linux.zip
|
||||
# - export ANDROID_NDK_HOME=$PWD/android-ndk-r26b
|
||||
# script:
|
||||
# - git submodule deinit --all --force || true
|
||||
# - git submodule update --init --recursive --force
|
||||
# - cmake -B build-android -S . -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-30 -DENABLE_QT=OFF -DENABLE_SDL2=OFF -DENABLE_WEB_SERVICE=OFF -DCITRON_USE_BUNDLED_VCPKG=ON -DCITRON_USE_BUNDLED_FFMPEG=ON -DANDROID_ARM_NEON=ON -DCITRON_ENABLE_LTO=ON
|
||||
# - cmake --build build-android --config $BUILD_TYPE --parallel $CMAKE_BUILD_PARALLEL_LEVEL
|
||||
# - cd src/android
|
||||
# - ./gradlew assembleMainlineRelease
|
||||
# artifacts:
|
||||
# paths:
|
||||
# - src/android/app/build/outputs/apk/mainline/release/*.apk
|
||||
# expire_in: 1 week
|
||||
# only:
|
||||
# - main
|
||||
# - master
|
||||
# - develop
|
||||
# - ci-fixes
|
||||
# - merge_requests
|
||||
# allow_failure: true
|
||||
|
||||
# # Unit Tests
|
||||
# test-unit:
|
||||
# stage: test
|
||||
# image: ubuntu:24.04
|
||||
# tags:
|
||||
# - citron-build
|
||||
# before_script:
|
||||
# # Set the environment to be non-interactive
|
||||
# - export DEBIAN_FRONTEND=noninteractive
|
||||
#
|
||||
# # Update package lists and install tool for managing repositories
|
||||
# - apt-get update -qq
|
||||
# - apt-get install -y -qq software-properties-common
|
||||
#
|
||||
# # Reliably add the 'universe' and 'multiverse' repositories
|
||||
# - add-apt-repository universe
|
||||
# - add-apt-repository multiverse
|
||||
#
|
||||
# # Update the package lists again to include the new repositories
|
||||
# - apt-get update -qq
|
||||
#
|
||||
# # Install all required system packages for the build
|
||||
# - apt-get install -y -qq
|
||||
# build-essential
|
||||
# cmake
|
||||
# ninja-build
|
||||
# git
|
||||
# pkg-config
|
||||
# catch2
|
||||
# libasound2-dev
|
||||
# libpulse-dev
|
||||
# libboost-all-dev
|
||||
# libavcodec-dev
|
||||
# libavfilter-dev
|
||||
# libavformat-dev
|
||||
# libavutil-dev
|
||||
# libswscale-dev
|
||||
# llvm-17-dev
|
||||
# gamemode-dev
|
||||
# libenet-dev
|
||||
# libfmt-dev
|
||||
# liblz4-dev
|
||||
# libopus-dev
|
||||
# libssl-dev
|
||||
# libusb-1.0-0-dev
|
||||
# libvulkan-dev
|
||||
# nlohmann-json3-dev
|
||||
# zlib1g-dev
|
||||
# libzstd-dev
|
||||
# libopenal-dev
|
||||
# libedit-dev
|
||||
# libcurl4-openssl-dev
|
||||
# libstb-dev
|
||||
# libcubeb-dev
|
||||
# libspeexdsp-dev
|
||||
# libva-dev
|
||||
# glslang-tools
|
||||
# libx11-dev
|
||||
#
|
||||
|
||||
# script:
|
||||
# - git submodule deinit --all --force || true
|
||||
# - git submodule update --init --recursive --force
|
||||
# - cmake -B build-test -S . -DCMAKE_BUILD_TYPE=Debug -DCITRON_TESTS=ON -DENABLE_QT=OFF -DENABLE_SDL2=OFF -DENABLE_WEB_SERVICE=OFF
|
||||
# - cmake --build build-test --config Debug --parallel $CMAKE_BUILD_PARALLEL_LEVEL
|
||||
# - cd build-test
|
||||
# - ctest --output-on-failure
|
||||
# artifacts:
|
||||
# reports:
|
||||
# junit: build-test/Testing/**/*.xml
|
||||
# expire_in: 1 week
|
||||
# only:
|
||||
# - main
|
||||
# - master
|
||||
# - develop
|
||||
# - ci-fixes
|
||||
# - merge_requests
|
||||
|
||||
# # Package Release
|
||||
# package-release:
|
||||
# stage: package
|
||||
# image: ubuntu:24.04
|
||||
# tags:
|
||||
# - citron-build
|
||||
# dependencies:
|
||||
# - build-linux
|
||||
# script:
|
||||
# - mkdir -p release
|
||||
# - echo "Creating release package..."
|
||||
# - tar -czf citron-release-$(date +%Y%m%d).tar.gz release/ || true
|
||||
# artifacts:
|
||||
# paths:
|
||||
# - citron-release-*.tar.gz
|
||||
# expire_in: 1 month
|
||||
# only:
|
||||
# - main
|
||||
# - master
|
||||
# - develop
|
||||
# - ci-fixes
|
||||
# - tags
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
#! /bin/bash
|
||||
set -e
|
||||
|
||||
# Make sure script is called from inside our container
|
||||
test -e /tmp/torzu-src-ro || (echo "Script MUST NOT be called directly!" ; exit 1)
|
||||
|
||||
# Set up environment
|
||||
export LANG=C.UTF-8
|
||||
export LC_ALL=C.UTF-8
|
||||
unset LC_ADDRESS LC_NAME LC_MONETARY LC_PAPER LC_TELEPHONE LC_MEASUREMENT LC_TIME
|
||||
|
||||
# Raise max open files count
|
||||
ulimit -n 50000
|
||||
|
||||
# Install dependencies
|
||||
apt -y install cmake ninja-build build-essential autoconf pkg-config locales wget git file mold libtool lsb-release wget software-properties-common gnupg \
|
||||
qtbase5-dev qtmultimedia5-dev qtbase5-private-dev glslang-tools libssl-dev libavcodec-dev libavfilter-dev libavutil-dev libswscale-dev libpulse-dev libasound2-dev
|
||||
if [ ! "$BUILD_USE_CPM" = 1 ]; then
|
||||
apt -y install libfmt-dev libenet-dev liblz4-dev nlohmann-json3-dev zlib1g-dev libopus-dev libsimpleini-dev libstb-dev libzstd-dev libusb-1.0-0-dev libcubeb-dev libcpp-jwt-dev libvulkan-dev gamemode-dev libasound2-dev libglu1-mesa-dev libxext-dev mesa-common-dev libva-dev
|
||||
|
||||
if [ ! -f /usr/local/lib/cmake/Boost-1.88.0/BoostConfigVersion.cmake ]; then
|
||||
# Install Boost
|
||||
wget https://archives.boost.io/release/1.88.0/source/boost_1_88_0.tar.bz2
|
||||
echo "Extracting Boost sources..."
|
||||
tar xf boost_1_88_0.tar.bz2
|
||||
cd boost_1_88_0
|
||||
./bootstrap.sh
|
||||
./b2 install --with-{headers,context,system,fiber,atomic,filesystem} link=static
|
||||
cd ..
|
||||
rm -rf boost_1_88_0 boost_1_88_0.tar.bz2
|
||||
fi
|
||||
fi
|
||||
|
||||
# Install Clang
|
||||
if ([ "$BUILD_USE_CLANG" = 1 ] && ! clang-19 --version); then
|
||||
cd /tmp
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
./llvm.sh 19
|
||||
rm llvm.sh
|
||||
fi
|
||||
|
||||
# Mount Torzu sources with temporary overlay
|
||||
cd /tmp
|
||||
mkdir torzu-src-upper torzu-src-work torzu-src
|
||||
mount -t overlay overlay -olowerdir=torzu-src-ro,upperdir=torzu-src-upper,workdir=torzu-src-work torzu-src
|
||||
|
||||
# Get extra configuration/compilation options
|
||||
EXTRA_COMPILE_FLAGS=""
|
||||
EXTRA_CMAKE_FLAGS=""
|
||||
if [ "$BUILD_USE_CLANG" = 1 ]; then
|
||||
EXTRA_CMAKE_FLAGS="-DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19"
|
||||
FATLTO_FLAG="-flto=full"
|
||||
else
|
||||
FATLTO_FLAG="-flto"
|
||||
fi
|
||||
if [ "$BUILD_USE_THIN_LTO" = 1 ]; then
|
||||
EXTRA_COMPILE_FLAGS="-flto=thin"
|
||||
fi
|
||||
if [ "$BUILD_USE_FAT_LTO" = 1 ]; then
|
||||
EXTRA_COMPILE_FLAGS="$FATLTO_FLAG"
|
||||
fi
|
||||
if [ "$BUILD_USE_CPM" = 1 ]; then
|
||||
EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS -DYUZU_USE_CPM=ON"
|
||||
fi
|
||||
|
||||
# Build Torzu
|
||||
cd /tmp
|
||||
mkdir torzu-build
|
||||
cd torzu-build
|
||||
cmake /tmp/torzu-src -GNinja -DCMAKE_BUILD_TYPE=Release -DYUZU_TESTS=OFF -DENABLE_QT_TRANSLATION=OFF -DSPIRV_WERROR=OFF -DCMAKE_FIND_LIBRARY_SUFFIXES=".a;.so" -DSPIRV-Headers_SOURCE_DIR=/tmp/torzu-src/externals/SPIRV-Headers -DCMAKE_{C,CXX}_FLAGS="$EXTRA_COMPILE_FLAGS -fdata-sections -ffunction-sections" -DCMAKE_{EXE,SHARED}_LINKER_FLAGS="-Wl,--gc-sections" $EXTRA_CMAKE_FLAGS
|
||||
ninja || (
|
||||
echo "Compilation has failed. Dropping you into a shell so you can inspect the situation. Run 'ninja' to retry and exit shell once compilation has finished successfully."
|
||||
echo "Note that any changes made here will not be reflected to the host environment, but changes made from the host environment will be reflected here."
|
||||
bash
|
||||
)
|
||||
|
||||
# Generate AppImage
|
||||
cp -rv /tmp/torzu-src/AppImageBuilder /tmp/AppImageBuilder
|
||||
cd /tmp/AppImageBuilder
|
||||
./build.sh /tmp/torzu-build /tmp/torzu.AppImage || echo "This error is known. Using workaround..."
|
||||
cp /lib/$(uname -m)-linux-gnu/libICE.so.6 build/
|
||||
mv build /tmp/hosttmp/torzu-debian-appimage-rootfs
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
#! /bin/bash
|
||||
set -e
|
||||
|
||||
# Parse options
|
||||
for i in "$@"
|
||||
do
|
||||
case $i in
|
||||
-l|--clang)
|
||||
export BUILD_USE_CLANG=1
|
||||
echo "-> Using Clang for compilation."
|
||||
;;
|
||||
-o|--thin-lto)
|
||||
export BUILD_USE_THIN_LTO=1
|
||||
echo "-> Thin link time optimization enabled."
|
||||
;;
|
||||
-O|--fat-lto)
|
||||
export BUILD_USE_FAT_LTO=1
|
||||
echo "-> Fat link time optimization enabled."
|
||||
;;
|
||||
-p|--use-cpm)
|
||||
export BUILD_USE_CPM=1
|
||||
echo "-> Using CPM to download most dependencies."
|
||||
;;
|
||||
-k|--keep-rootfs)
|
||||
BUILD_KEEP_ROOTFS=1
|
||||
echo "-> Not deleting rootfs after successful build."
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 [--clang/-l] [--thin-lto/-o] [--fat-lto/-O] [--use-cpm/-p] [--keep-rootfs/-k]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Make sure options are valid
|
||||
if [ "$BUILD_USE_THIN_LTO" = 1 ] && [ "$BUILD_USE_CLANG" != 1 ]; then
|
||||
echo "Thin LTO can't be used without Clang!"
|
||||
exit 2
|
||||
fi
|
||||
if [ "$BUILD_USE_THIN_LTO" = 1 ] && [ "$BUILD_USE_FAT_LTO" = 1 ]; then
|
||||
echo "Only either thin or fat LTO can be used!"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Get citron source dir
|
||||
citron_SOURCE_DIR="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
|
||||
echo "-> Source dir is $citron_SOURCE_DIR"
|
||||
rm -rf "$citron_SOURCE_DIR/AppImageBuilder/build"
|
||||
|
||||
# Generate debian rootfs
|
||||
cd /tmp
|
||||
echo "Cleaning up before build..."
|
||||
rm -rf citron-debian-appimage-rootfs
|
||||
[ -d rootfs-citron-appimage-build ] ||
|
||||
debootstrap stable rootfs-citron-appimage-build http://deb.debian.org/debian/
|
||||
bwrap --bind rootfs-citron-appimage-build / \
|
||||
--unshare-pid \
|
||||
--dev-bind /dev /dev --proc /proc --tmpfs /tmp --ro-bind /sys /sys --dev-bind /run /run \
|
||||
--tmpfs /var/tmp \
|
||||
--chmod 1777 /tmp \
|
||||
--ro-bind /etc/resolv.conf /etc/resolv.conf \
|
||||
--ro-bind "$citron_SOURCE_DIR" /tmp/citron-src-ro \
|
||||
--chdir / \
|
||||
--tmpfs /home \
|
||||
--setenv HOME /home \
|
||||
--bind /tmp /tmp/hosttmp \
|
||||
/tmp/citron-src-ro/AppImage-build-debian-inner.sh
|
||||
appimagetool citron-debian-appimage-rootfs citron.AppImage
|
||||
echo "AppImage generated at /tmp/citron.AppImage! Cleaning up..."
|
||||
rm -rf citron-debian-appimage-rootfs
|
||||
if [ ! "$BUILD_KEEP_ROOTFS" = 1 ]; then
|
||||
rm -rf rootfs-citron-appimage-build
|
||||
fi
|
||||
Executable
+42
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
FILE=build/bin/citron
|
||||
if test -f "$FILE"; then
|
||||
# remove any previously made AppImage in the base citron git folder
|
||||
rm ./citron.AppImage
|
||||
|
||||
# enter AppImage utility folder
|
||||
cd AppImageBuilder
|
||||
|
||||
# run the build script to create the AppImage
|
||||
# (usage) ./build.sh [source citron build folder] [destination .AppImage file]
|
||||
./build.sh ../build ./citron.AppImage
|
||||
|
||||
FILE=./citron.AppImage
|
||||
if test -f "$FILE"; then
|
||||
# move the AppImage to the main citron folder
|
||||
mv citron.AppImage ..
|
||||
# return to main citron folder
|
||||
cd ..
|
||||
# show contents of current folder
|
||||
echo
|
||||
ls
|
||||
# show AppImages specifically
|
||||
echo
|
||||
ls *.AppImage
|
||||
echo
|
||||
echo "'citron.AppImage' is now located in the current folder."
|
||||
echo
|
||||
else
|
||||
cd ..
|
||||
echo "AppImage was not built."
|
||||
fi
|
||||
else
|
||||
echo
|
||||
echo "$FILE does not exist."
|
||||
echo
|
||||
echo "No citron executable found in the /citron/build/bin folder!"
|
||||
echo
|
||||
echo "You must first build a native linux version of citron before running this script!"
|
||||
echo
|
||||
fi
|
||||
Executable
+104
@@ -0,0 +1,104 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Get script directory
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
BUILD_DIR_RAW="${1:-$SCRIPT_DIR/../build/generate}"
|
||||
BUILD_DIR="$(readlink -f "${BUILD_DIR_RAW}")"
|
||||
|
||||
# Save environment overrides
|
||||
ENV_QT_PATH="${CITRON_QT_PATH:-}"
|
||||
ENV_ICU_PATH="${CITRON_ICU_PATH:-}"
|
||||
ENV_XCB_PATH="${CITRON_XCB_PATH:-}"
|
||||
ENV_BINARY_DIR="${CITRON_BINARY_DIR:-}"
|
||||
|
||||
if [ ! -f "$BUILD_DIR/AppImageBuilder/config.sh" ]; then
|
||||
echo "Error: config.sh not found in $BUILD_DIR/AppImageBuilder/"
|
||||
echo "Please run CMake first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source "$BUILD_DIR/AppImageBuilder/config.sh"
|
||||
|
||||
# Allow environment variables to override config.sh
|
||||
export CITRON_QT_PATH="${ENV_QT_PATH:-$CITRON_QT_PATH}"
|
||||
export CITRON_ICU_PATH="${ENV_ICU_PATH:-$CITRON_ICU_PATH}"
|
||||
export CITRON_XCB_PATH="${ENV_XCB_PATH:-$CITRON_XCB_PATH}"
|
||||
export CITRON_BINARY_DIR="${ENV_BINARY_DIR:-$CITRON_BINARY_DIR}"
|
||||
|
||||
# Tools setup
|
||||
TOOLS_DIR="$BUILD_DIR/AppImageBuilder/tools"
|
||||
mkdir -p "$TOOLS_DIR"
|
||||
LINUXDEPLOY="$TOOLS_DIR/linuxdeploy-x86_64.AppImage"
|
||||
LINUXDEPLOY_QT="$TOOLS_DIR/linuxdeploy-plugin-qt-x86_64.AppImage"
|
||||
|
||||
download_tool() {
|
||||
local url=$1
|
||||
local file=$2
|
||||
if [ ! -f "$file" ]; then
|
||||
echo "Downloading $file..."
|
||||
wget -q --show-progress "$url" -O "$file"
|
||||
chmod +x "$file"
|
||||
fi
|
||||
}
|
||||
|
||||
download_tool "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage" "$LINUXDEPLOY"
|
||||
download_tool "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage" "$LINUXDEPLOY_QT"
|
||||
|
||||
# AppDir setup
|
||||
APPDIR="$BUILD_DIR/AppImageBuilder/AppDir"
|
||||
rm -rf "$APPDIR"
|
||||
mkdir -p "$APPDIR"
|
||||
|
||||
# Build the AppImage
|
||||
# We need to tell linuxdeploy where our custom libraries are
|
||||
export LD_LIBRARY_PATH="$CITRON_ICU_PATH:$CITRON_XCB_PATH/lib:$LD_LIBRARY_PATH"
|
||||
export EXTRA_QT_PLUGINS="wayland" # Add any extra plugins needed
|
||||
export NO_STRIP=1 # Avoid strip failures on modern binaries (SHT_RELR)
|
||||
|
||||
# Run linuxdeploy
|
||||
# --plugin qt handles Qt dependencies
|
||||
export QMAKE="$CITRON_QT_PATH/bin/qmake"
|
||||
export QT_ROOT="$CITRON_QT_PATH"
|
||||
export LD_LIBRARY_PATH="$CITRON_QT_PATH/lib:$CITRON_ICU_PATH:$CITRON_XCB_PATH/lib:${LD_LIBRARY_PATH:-}"
|
||||
|
||||
pushd "$BUILD_DIR" > /dev/null
|
||||
# Run linuxdeploy to populate AppDir
|
||||
"$LINUXDEPLOY" --appdir "$APPDIR" \
|
||||
--executable "$CITRON_BINARY_DIR/citron" \
|
||||
--desktop-file "$SCRIPT_DIR/assets/citron.desktop" \
|
||||
--icon-file "$SCRIPT_DIR/assets/citron.svg" \
|
||||
--library "$CITRON_ICU_PATH/libicuuc.so.73" \
|
||||
--library "$CITRON_ICU_PATH/libicui18n.so.73" \
|
||||
--library "$CITRON_ICU_PATH/libicudata.so.73" \
|
||||
--library "$CITRON_XCB_PATH/lib/libxcb.so.1" \
|
||||
--library "$CITRON_XCB_PATH/lib/libxcb-cursor.so.0" \
|
||||
--library "$CITRON_XCB_PATH/lib/libxcb-xkb.so.1" \
|
||||
--library "$CITRON_XCB_PATH/lib/libxcb-render.so.0" \
|
||||
--library "$CITRON_XCB_PATH/lib/libxcb-render-util.so.0" \
|
||||
--library "$CITRON_XCB_PATH/lib/libxcb-shape.so.0" \
|
||||
--library "$CITRON_XCB_PATH/lib/libxcb-shm.so.0" \
|
||||
--library "$CITRON_XCB_PATH/lib/libxcb-sync.so.1" \
|
||||
--library "$CITRON_XCB_PATH/lib/libxcb-xfixes.so.0" \
|
||||
--library "$CITRON_XCB_PATH/lib/libxcb-randr.so.0" \
|
||||
--library "$CITRON_XCB_PATH/lib/libxcb-image.so.0" \
|
||||
--library "$CITRON_XCB_PATH/lib/libxcb-keysyms.so.1" \
|
||||
--library "$CITRON_XCB_PATH/lib/libxcb-icccm.so.4" \
|
||||
--library "$CITRON_XCB_PATH/lib/libXau.so.6" \
|
||||
--library "$CITRON_XCB_PATH/lib/libXdmcp.so.6" \
|
||||
--plugin qt
|
||||
|
||||
# Inject the wrapper script to support portable mode (user folder) and PGO profiles
|
||||
# We rename the real binary to citron.bin and put the script as 'citron'
|
||||
if [ -f "$APPDIR/usr/bin/citron" ]; then
|
||||
mv "$APPDIR/usr/bin/citron" "$APPDIR/usr/bin/citron.bin"
|
||||
cp "$SCRIPT_DIR/assets/citron.sh" "$APPDIR/usr/bin/citron"
|
||||
chmod +x "$APPDIR/usr/bin/citron"
|
||||
fi
|
||||
|
||||
# Build the final AppImage
|
||||
"$LINUXDEPLOY" --appdir "$APPDIR" --output appimage
|
||||
|
||||
popd > /dev/null
|
||||
|
||||
echo "AppImage build complete in $BUILD_DIR"
|
||||
Executable
+170
@@ -0,0 +1,170 @@
|
||||
#! /bin/bash
|
||||
set -e
|
||||
|
||||
# Check arguments
|
||||
if [[ $# != 2 ]]; then
|
||||
>&2 echo "Bad usage!"
|
||||
echo "Usage: $0 <build dir> <output file>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get paths
|
||||
ARCH="$(uname -m)"
|
||||
SYSTEM_LIBS="/usr/lib"
|
||||
SYSTEM_LIBS64="/usr/lib64"
|
||||
citron_BIN="${1}/bin"
|
||||
citron_BIN_GUI="${citron_BIN}/citron"
|
||||
|
||||
# Make sure executable exists
|
||||
if [[ $(file -b --mime-type "$citron_BIN_GUI") != application/x-pie-executable ]]; then
|
||||
>&2 echo "Invalid citron executable!"
|
||||
fi
|
||||
|
||||
# Clean up build dir
|
||||
rm -rf build
|
||||
mkdir build
|
||||
|
||||
# NOTE: some of these aren't used now, but can be reordered in priority when citron is converted to QT6
|
||||
# QT5 - /usr/lib/${ARCH}-linux-gnu/qt5 (debian), /usr/lib64/qt5 (fedora), /usr/lib/qt (steam deck)
|
||||
# QT5 - /usr/lib/${ARCH}-linux-gnu/qt6 (debian), /usr/lib64/qt6 (fedora), /usr/lib/qt6 (steam deck)
|
||||
QTFOUND="true"
|
||||
QTDIR="$SYSTEM_LIBS"/${ARCH}-linux-gnu/qt5/plugins
|
||||
if [ ! -d "$QTDIR" ]; then
|
||||
# default qt5 folder not found, check for 64-bit qt5 folder
|
||||
QTDIR="$SYSTEM_LIBS64"/qt5/plugins
|
||||
if [ ! -d "$QTDIR" ]; then
|
||||
# 64-bit qt5 folder not found, check for Steam Deck qt (qt5) folder
|
||||
QTDIR="$SYSTEM_LIBS"/qt/plugins
|
||||
if [ ! -d "$QTDIR" ]; then
|
||||
# Steam Deck qt (qt5) folder not found, check for regular qt6 folder
|
||||
QTDIR="$SYSTEM_LIBS"/${ARCH}-linux-gnu/qt6/plugins
|
||||
if [ ! -d "$QTDIR" ]; then
|
||||
# regular qt6 folder not found, check for 64-bit qt6 folder
|
||||
QTDIR="$SYSTEM_LIBS64"/qt6/plugins
|
||||
if [ ! -d "$QTDIR" ]; then
|
||||
# 64-bit qt6 folder not found, check for Steam Deck qt6 folder
|
||||
QTDIR="$SYSTEM_LIBS"/qt6/plugins
|
||||
if [ ! -d "$QTDIR" ]; then
|
||||
QTFOUND="false"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [ $QTFOUND == "true" ]; then
|
||||
echo "QT plugins from $QTDIR will be used."
|
||||
|
||||
# Copy system dependencies used to build and required by the citron binary
|
||||
# includes:
|
||||
# - '/lib64/ld-linux-x86-64.so.2' or `/lib/ld-linux-aarch64.so.1` file per architecture
|
||||
# - required files from `/usr/lib/x86_64-linux-gnu` or `/usr/lib/aarch64-linux-gnu`
|
||||
# - different for SteamDeck, but still does it automatically
|
||||
function copy_libs {
|
||||
for lib in $(ldd "$1"); do
|
||||
(cp -vn "$lib" ./build/ 2> /dev/null) || true
|
||||
done
|
||||
}
|
||||
echo "Copying main dependencies..."
|
||||
copy_libs "$citron_BIN_GUI"
|
||||
|
||||
# Copy QT dependency folders, path determined above
|
||||
echo "Copying Qt dependencies..."
|
||||
mkdir ./build/qt5
|
||||
cp -rv "$QTDIR"/{imageformats,platforms,platformthemes,xcbglintegrations} ./build/qt5/
|
||||
|
||||
# Discover indirect dependencies (mostly from runtime-loaded Qt plugins)
|
||||
echo "Copying extra dependencies..."
|
||||
while true; do
|
||||
LIBS="$(find ./build -name \*.so\*)"
|
||||
LIB_COUNT=$(echo "$LIBS" | wc -l)
|
||||
echo "$LIB_COUNT dependency libraries discovered so far..."
|
||||
if [ $LIB_COUNT == "$PREV_LIB_COUNT" ]; then
|
||||
break
|
||||
fi
|
||||
PREV_LIB_COUNT=$LIB_COUNT
|
||||
|
||||
for plib in $LIBS; do
|
||||
if [ -f "$plib" ]; then
|
||||
copy_libs "$plib"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# Copy executable
|
||||
cp -v "$citron_BIN_GUI" ./build/
|
||||
|
||||
# Copy assets for the appropriate arch
|
||||
cp -v ./assets_"${ARCH}"/* ./build/
|
||||
# Copy common assets
|
||||
cp -v ./assets/* ./build/
|
||||
|
||||
# Strip all libraries and executables
|
||||
for file in $(find ./build -type f); do
|
||||
(strip -v "$file" 2> /dev/null) || true
|
||||
done
|
||||
|
||||
PASSED_CHECKSUM="false"
|
||||
FILE=appimagetool.AppImage
|
||||
# total number of times to try downloading if a checksum doesn't match
|
||||
DL_TRIES=3
|
||||
while [ $PASSED_CHECKSUM == "false" ] && [ "$DL_TRIES" -gt 0 ]; do
|
||||
case $ARCH in
|
||||
x86_64)
|
||||
# Static copy from the 'ext-linux-bin' repo.
|
||||
# Checksum will need to be changed when/if this file in the repo is updated.
|
||||
if ! test -f "$FILE"; then
|
||||
echo "Downloading appimagetool for architecture '$ARCH'"
|
||||
wget -O appimagetool.AppImage https://github.com/litucks/ext-linux-bin/raw/refs/heads/main/appimage/appimagetool-x86_64.AppImage
|
||||
fi
|
||||
if [ $(shasum -a 256 appimagetool.AppImage | cut -d' ' -f1) = "110751478abece165a18460acbd7fd1398701f74a9405ad8ac053427d937bd5d" ] ; then
|
||||
PASSED_CHECKSUM="true"
|
||||
fi
|
||||
# DISABLED TO USE THE ABOVE
|
||||
# The current continuous release channel option, until a static copy is put in 'ext-linux-bin'.
|
||||
# The checksum will pass until the continuous release is updated, then a new one needs to be
|
||||
# generated to update this script.
|
||||
#if ! test -f "$FILE"; then
|
||||
# echo "Downloading appimagetool for architecture '$ARCH'"
|
||||
# wget -O appimagetool.AppImage https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage
|
||||
#fi
|
||||
#if [ $(shasum -a 256 appimagetool.AppImage | cut -d' ' -f1) = "46fdd785094c7f6e545b61afcfb0f3d98d8eab243f644b4b17698c01d06083d1" ] ; then
|
||||
# PASSED_CHECKSUM="true"
|
||||
#fi
|
||||
;;
|
||||
aarch64)
|
||||
# Currently set to the continuous release channel until a static copy is put in 'ext-linux-bin'.
|
||||
# The checksum will pass until the continuous release is updated, then a new one needs to be
|
||||
# generated to update this script.
|
||||
if ! test -f "$FILE"; then
|
||||
echo "Downloading appimagetool for architecture '$ARCH'"
|
||||
wget -O appimagetool.AppImage https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-aarch64.AppImage
|
||||
fi
|
||||
if [ $(shasum -a 256 appimagetool.AppImage | cut -d' ' -f1) = "04f45ea45b5aa07bb2b071aed9dbf7a5185d3953b11b47358c1311f11ea94a96" ] ; then
|
||||
PASSED_CHECKSUM="true"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
PASSED_CHECKSUM="invalid_arch"
|
||||
;;
|
||||
esac
|
||||
# delete the appimagetool downloaded if the checksum doesn't match.
|
||||
if [ ! $PASSED_CHECKSUM == "true" ]; then
|
||||
rm -f appimagetool.AppImage
|
||||
fi
|
||||
((DL_TRIES-=1))
|
||||
done
|
||||
if [ $PASSED_CHECKSUM == "true" ]; then
|
||||
echo "Checksum passed. Proceeding to build image."
|
||||
# Build AppImage
|
||||
chmod a+x appimagetool.AppImage
|
||||
./appimagetool.AppImage ./build "$2"
|
||||
elif [ $PASSED_CHECKSUM == "invalid_arch" ]; then
|
||||
echo "No download found for architecture '$ARCH'. Building halted."
|
||||
else
|
||||
echo "Checksum for appimagetool does not match. Building halted."
|
||||
echo "If the file to be downloaded has been changed, a new checksum will need to be generated for this script."
|
||||
fi
|
||||
else
|
||||
echo "QT not found, aborting AppImage build."
|
||||
fi
|
||||
@@ -1,410 +0,0 @@
|
||||
#!/bin/sh
|
||||
# SPDX-FileCopyrightText: 2026 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# package-citron-linux.sh — Build AppImage + tar.zst using pkgforge tooling.
|
||||
#
|
||||
# Called by build-citron-linux.sh's build_appimage_stage() after the install
|
||||
# tree has been staged under build_dir/install-root.
|
||||
# Can also be invoked directly if the install tree is already in place.
|
||||
#
|
||||
# Environment variables:
|
||||
# APP_VERSION Version string embedded in artifact filenames (required)
|
||||
# ARCH CPU architecture (default: uname -m)
|
||||
# ARCH_SUFFIX Extra suffix appended to filenames (e.g. _v3)
|
||||
# DEVEL Set to 'true' to rename app to "citron nightly" in AppImage
|
||||
# OUTPATH Output directory for finished artifacts (default: $PWD/dist)
|
||||
# DESTDIR Staging root prepended to /usr paths when locating the
|
||||
# citron binary, desktop file, icon, and Qt translations
|
||||
# (default: "" — real /usr, e.g. for an already-installed
|
||||
# system citron). Set by build-citron-linux.sh's
|
||||
# build_appimage_stage() to a build-tree staging directory so
|
||||
# no system files are read from /usr and nothing is written
|
||||
# there. Does NOT apply to libpulse/libgamemode — those are
|
||||
# genuine host runtime libraries, not part of citron's install.
|
||||
# STRACE_MODE Enable dlopen/LD_DEBUG scan when citron starts up (default: 0)
|
||||
# Set to 1 only if xvfb-run is installed; see comment below.
|
||||
# DEPLOY_VULKAN Deploy Mesa/Vulkan DRI driver collection (default: 0)
|
||||
# DEPLOY_OPENGL Deploy OpenGL/EGL/GLX libs into AppImage (default: 0)
|
||||
# DEPLOY_PIPEWIRE Deploy PipeWire SPA/ALSA plugin trees (default: 0)
|
||||
# DEPLOY_GTK Deploy GTK modules and gvfs into AppImage (default: 0)
|
||||
# CITRON_QT_PATH Qt6 installation prefix (= QT_TARGET_PATH from CMake,
|
||||
# e.g. .../cpmcache/qt-bin/6.9.3/gcc_64). Set automatically
|
||||
# by build-citron-linux.sh. Used to derive QT_LOCATION so
|
||||
# quick-sharun bundles CPM Qt plugins, not system Qt plugins.
|
||||
|
||||
set -ex
|
||||
|
||||
ARCH="${ARCH:-$(uname -m)}"
|
||||
|
||||
if [ -z "${APP_VERSION:-}" ]; then
|
||||
echo "Error: APP_VERSION environment variable is not set." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DESTDIR="${DESTDIR:-}"
|
||||
OUTNAME_BASE="citron_nightly-${APP_VERSION}-linux-${ARCH}${ARCH_SUFFIX:-}"
|
||||
OUTPATH="${OUTPATH:-$PWD/dist}"
|
||||
export OUTNAME="${OUTNAME_BASE}.AppImage"
|
||||
export DESKTOP="${DESTDIR}/usr/share/applications/org.citron_emu.citron.desktop"
|
||||
|
||||
# Prefer a rasterised 256×256 PNG for .DirIcon — SVG fails silently as an
|
||||
# AppImage icon on most desktop environments (appimaged, KDE, GNOME all expect
|
||||
# a PNG for taskbar/launcher display).
|
||||
#
|
||||
# Priority:
|
||||
# 1. Pre-built PNG installed by cmake (dist/org.citron_emu.citron.png →
|
||||
# share/icons/hicolor/256x256/apps/); commit this to the emulator repo
|
||||
# to make it permanent without needing any conversion tool.
|
||||
# 2. Convert the installed SVG if a conversion tool is present.
|
||||
# 3. Fall back to the raw SVG (icon will be broken on most desktops).
|
||||
_png_from_repo="${DESTDIR}/usr/share/icons/hicolor/256x256/apps/org.citron_emu.citron.png"
|
||||
_svg_icon="${DESTDIR}/usr/share/icons/hicolor/scalable/apps/org.citron_emu.citron.svg"
|
||||
_png_icon="/tmp/citron-diricon-$$.png"
|
||||
_try_svg2png() {
|
||||
if command -v rsvg-convert >/dev/null 2>&1; then
|
||||
rsvg-convert -w 256 -h 256 "$1" -o "$2" 2>/dev/null && return 0
|
||||
fi
|
||||
if command -v inkscape >/dev/null 2>&1; then
|
||||
inkscape --export-filename="$2" --export-width=256 --export-height=256 \
|
||||
"$1" >/dev/null 2>&1 && return 0
|
||||
fi
|
||||
if command -v convert >/dev/null 2>&1; then
|
||||
convert -background none -size 256x256 "$1" "$2" 2>/dev/null && return 0
|
||||
fi
|
||||
if command -v magick >/dev/null 2>&1; then
|
||||
magick -background none -size 256x256 "$1" "$2" 2>/dev/null && return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
if [ -f "$_png_from_repo" ]; then
|
||||
export ICON="$_png_from_repo"
|
||||
elif [ -f "$_svg_icon" ] && _try_svg2png "$_svg_icon" "$_png_icon"; then
|
||||
export ICON="$_png_icon"
|
||||
else
|
||||
export ICON="$_svg_icon"
|
||||
fi
|
||||
|
||||
# ── quick-sharun deployment flags ───────────────────────────────────────────
|
||||
#
|
||||
# STRACE_MODE=0 — disable the dlopen / LD_DEBUG scan entirely.
|
||||
#
|
||||
# In default mode (STRACE_MODE=1) quick-sharun spawns citron under
|
||||
# LD_DEBUG=libs for STRACE_TIME (default 5 s), then kills it, to capture
|
||||
# libs only dlopen'd at runtime. When xvfb-run is NOT installed, citron
|
||||
# opens a real display window and the user must close it manually for the
|
||||
# build to continue. Even with xvfb-run the scan is harmful for citron:
|
||||
# everything it captures is stuff we explicitly do NOT want bundled —
|
||||
# Vulkan ICD drivers (libvulkan_radeon.so, libvulkan_intel.so,
|
||||
# libLLVM.so.20.1 at 137 MB, …) loaded by vkCreateInstance(), the full
|
||||
# PipeWire SPA plugin tree loaded by PulseAudio's backend discovery, and
|
||||
# GTK accessibility modules from the GNOME a11y bus. All of citron's
|
||||
# legitimate runtime deps are already covered by the static ldd scan
|
||||
# (explicit Qt plugins + libpulse + libgamemode).
|
||||
export STRACE_MODE="${STRACE_MODE:-0}"
|
||||
#
|
||||
# DEPLOY_VULKAN=0 / DEPLOY_OPENGL=0 — do not stage Mesa DRI or the OpenGL set.
|
||||
# NOTE: DEPLOY_VULKAN=0 does NOT suppress Vulkan ICDs captured by the dlopen
|
||||
# scan (that requires STRACE_MODE=0 above); it only suppresses the separate
|
||||
# static DEPLOY_VULKAN staging block (libVkLayer_*.so, etc.).
|
||||
# libvulkan.so.1, libgbm.so, libdrm.so, libxcb-dri3.so, libxcb-glx.so are
|
||||
# all DT_NEEDED of citron / Qt plugins and are captured by ldd regardless.
|
||||
# SHARUN_ALLOW_SYS_VK_ICD=1 (set in .env below) makes sharun prefer the
|
||||
# host GPU ICD at runtime, so we never need to bundle GPU-vendor drivers.
|
||||
export DEPLOY_VULKAN="${DEPLOY_VULKAN:-0}"
|
||||
export DEPLOY_OPENGL="${DEPLOY_OPENGL:-0}"
|
||||
#
|
||||
# DEPLOY_PIPEWIRE=0 — suppress the PipeWire/SPA/ALSA plugin staging block.
|
||||
# On Ubuntu 24.04 PulseAudio is a PipeWire shim: ldd libpulse.so.0 shows
|
||||
# libpipewire-0.3.so.0. quick-sharun's NEEDED_LIBS scan therefore sets
|
||||
# DEPLOY_PIPEWIRE=1, staging pipewire-0.3/* + spa-0.2/**/* + alsa-lib/
|
||||
# *pipewire* (~41 PipeWire plugins + 11 ALSA plugins + Bluetooth codecs).
|
||||
# These are backend-resolution plugins resolved from the HOST at runtime;
|
||||
# bundling them bloats the AppImage and causes host-version conflicts.
|
||||
# We carry libpulse.so.0 itself (passed explicitly below) — that is enough.
|
||||
export DEPLOY_PIPEWIRE="${DEPLOY_PIPEWIRE:-0}"
|
||||
#
|
||||
# DEPLOY_GTK=0 — suppress GTK module / gvfs staging.
|
||||
# quick-sharun includes libqgtk3.so in the Qt plugin glob and runs ldd on
|
||||
# it; ldd sees libgtk-3.so.0, triggering DEPLOY_GTK=1 which stages
|
||||
# gtk-3.0/immodules/*, gvfs/libgvfscommon.so, and gio/modules/* (the full
|
||||
# GTK accessibility + filesystem integration stack). quick-sharun already
|
||||
# sets QUICK_SHARUN_SKIP_DEPS_FOR="libqgtk3.so" so lib4bin skips transitive
|
||||
# deps of that plugin — but the DETECTION loop fires before SKIP applies and
|
||||
# sets DEPLOY_GTK=1 anyway. The GTK platform theme should dlopen the HOST
|
||||
# GTK at runtime; bundling it causes version conflicts.
|
||||
export DEPLOY_GTK="${DEPLOY_GTK:-0}"
|
||||
|
||||
# Tell quick-sharun to use CPM's Qt installation for plugins, not the SYSTEM
|
||||
# Qt. Without this, quick-sharun defaults to $LIB_DIR/qt6/plugins (system Qt),
|
||||
# whose shared libraries depend on the SYSTEM libQt6Core.so.6. That system
|
||||
# version may be older than the CPM-built Qt 6.9.3 citron was compiled against,
|
||||
# causing at AppImage runtime:
|
||||
# libQt6Core.so.6: version 'Qt_6.9' not found
|
||||
#
|
||||
# CITRON_QT_PATH = QT_TARGET_PATH from CMake = the Qt6 prefix directory (e.g.
|
||||
# .../cpmcache/qt-bin/6.9.3/gcc_64). quick-sharun uses QT_LOCATION as the
|
||||
# prefix and looks for plugins at $QT_LOCATION/plugins — that's exactly where
|
||||
# aqt installs them.
|
||||
if [ -n "${CITRON_QT_PATH:-}" ] && [ -z "${QT_LOCATION:-}" ]; then
|
||||
export QT_LOCATION="${CITRON_QT_PATH}"
|
||||
fi
|
||||
|
||||
# Fix for "libQt6Core.so.6: version 'Qt_6.9' not found" at AppImage runtime.
|
||||
#
|
||||
# Root cause: quick-sharun COPIES each CPM Qt plugin into AppDir/shared/lib/
|
||||
# before running ldd on the copy. The plugins carry a $ORIGIN-relative
|
||||
# DT_RUNPATH (e.g. "$ORIGIN/../../../lib") that is correct when the plugin is
|
||||
# in $QT_LOCATION/plugins/platforms/, but resolves to a nonsense path once
|
||||
# $ORIGIN becomes AppDir/shared/lib/. ldd then falls through to the system
|
||||
# linker search path and resolves libQt6Core.so.6 to the SYSTEM Qt (e.g.
|
||||
# Qt 6.4.2 on Ubuntu 24.04). That system copy is staged into the AppImage as
|
||||
# AppDir/shared/lib/libQt6Core.so.6. At runtime the CPM Qt 6.9 plugins
|
||||
# require the Qt_6.9 version symbol — absent from the system 6.4 core — and
|
||||
# the AppImage aborts with the version error.
|
||||
#
|
||||
# Fix: prepend CPM Qt's lib dir to LD_LIBRARY_PATH before calling quick-sharun.
|
||||
# LD_LIBRARY_PATH has higher priority than DT_RUNPATH, so every ldd invocation
|
||||
# inside lib4bin now finds CPM libQt6Core.so.6 (and all other Qt libs)
|
||||
# regardless of broken $ORIGIN resolution. The citron binary itself is
|
||||
# unaffected: it carries DT_RPATH (set by _patch_binary_rpaths via
|
||||
# patchelf --force-rpath), which has higher priority than LD_LIBRARY_PATH.
|
||||
if [ -n "${CITRON_QT_PATH:-}" ]; then
|
||||
export LD_LIBRARY_PATH="${CITRON_QT_PATH}/lib${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
|
||||
fi
|
||||
|
||||
|
||||
# Use the vendored quick-sharun.sh committed alongside this script rather than
|
||||
# downloading from pkgforge HEAD. The vendored copy has appimagetool pinned to
|
||||
# 0.3.2 and sharun pinned to 1.0.0 (both already in the file); all DEPLOY_*
|
||||
# overrides and cleanup rules in this script were calibrated against that
|
||||
# specific version. Pulling HEAD risks silent regressions if pkgforge changes
|
||||
# DEPLOY_* defaults or the AppDir layout between releases.
|
||||
_qs_src="$(cd "$(dirname "$0")" && pwd)/quick-sharun.sh"
|
||||
if [ ! -f "${_qs_src}" ]; then
|
||||
printf 'ERROR: vendored quick-sharun.sh not found at %s\n' "${_qs_src}" >&2
|
||||
printf 'Commit quick-sharun.sh to the repo alongside this script.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
cp "${_qs_src}" ./quick-sharun
|
||||
chmod +x ./quick-sharun
|
||||
|
||||
# Bundle binary + optional runtime libs.
|
||||
#
|
||||
# gamemode and libpulse are dlopen'd by cubeb/citron at runtime rather than
|
||||
# linked at build time, so quick-sharun's automatic dependency scan won't
|
||||
# find them — they must be passed explicitly if present.
|
||||
#
|
||||
# Locate them via `ldconfig -p` rather than a hardcoded /usr/lib/*.so* glob:
|
||||
# on multiarch systems (Debian/Ubuntu) these libs live under
|
||||
# /usr/lib/<triplet>/, not /usr/lib/ directly. A glob that matches nothing is
|
||||
# passed to quick-sharun as a literal non-existent path by POSIX sh (no
|
||||
# nullglob), which quick-sharun then treats as a hard requirement and aborts.
|
||||
# Neither lib is guaranteed to be present (e.g. pure PipeWire setups have no
|
||||
# libpulse.so), so both are best-effort.
|
||||
EXTRA_LIBS=""
|
||||
|
||||
GAMEMODE_LIB="$(ldconfig -p 2>/dev/null | awk '/libgamemode\.so/ {print $NF; exit}')"
|
||||
if [ -n "$GAMEMODE_LIB" ]; then
|
||||
EXTRA_LIBS="$EXTRA_LIBS $GAMEMODE_LIB"
|
||||
fi
|
||||
|
||||
PULSE_LIB="$(ldconfig -p 2>/dev/null | awk '/libpulse\.so/ {print $NF; exit}')"
|
||||
if [ -n "$PULSE_LIB" ]; then
|
||||
EXTRA_LIBS="$EXTRA_LIBS $PULSE_LIB"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
./quick-sharun "${DESTDIR}/usr/bin/citron"* $EXTRA_LIBS
|
||||
|
||||
# Qt translations: do NOT copy manually here.
|
||||
# quick-sharun's --make-appimage (lib4bin) already copies
|
||||
# /usr/share/qt6/translations → AppDir/lib/qt6/translations/ automatically
|
||||
# when it detects Qt. Adding a second copy from install-root would produce
|
||||
# two separate translation trees (AppDir/usr/share/qt6/ and
|
||||
# AppDir/lib/qt6/) wasting ~100 MB in the AppImage with no benefit.
|
||||
# The qt.conf written below points Translations to the one quick-sharun stages.
|
||||
|
||||
# ── Post-dep-scan cleanup ────────────────────────────────────────────────────
|
||||
# Belt-and-suspenders: remove any bloat that slipped past the DEPLOY_* flags
|
||||
# or the STRACE_MODE=0 suppression (e.g. from ldd transitive chains we don't
|
||||
# fully control, or from a future quick-sharun version that overrides our flags
|
||||
# differently). This runs BEFORE --make-appimage so lib4bin never sees them.
|
||||
#
|
||||
# Pattern Why excluded
|
||||
# libvulkan_*.so Mesa Vulkan ICD drivers: system-specific, huge.
|
||||
# SHARUN_ALLOW_SYS_VK_ICD=1 uses host GPU driver.
|
||||
# libLLVM*.so Mesa LLVM backend (137 MB): only needed by Mesa ICDs.
|
||||
# vulkan/icd.d/* ICD manifest JSONs: useless without the ICD .so files.
|
||||
# vulkan/implicit_layer.d/* Vulkan validation / overlay layers: dev tooling only.
|
||||
# pipewire-*/ PipeWire backend plugins: resolved from host at runtime.
|
||||
# spa-*/ PipeWire SPA plugins: same.
|
||||
# alsa-lib/ ALSA plugins: same.
|
||||
# gconv/ glibc character-set converters: citron never calls iconv.
|
||||
_appdir="${PWD}/AppDir"
|
||||
|
||||
# Explicitly stage ld-linux into AppDir/shared/lib/ — lib4bin's AppRun.lib
|
||||
# prints "Interpreter not found!" and aborts if this file is absent.
|
||||
#
|
||||
# sharun -g (called by quick-sharun) is supposed to copy it, but it reads the
|
||||
# interpreter path from AppDir/shared/bin/citron AFTER _patch_away_usr_lib_dir
|
||||
# has run sed -i on the binary. GNU sed on an ELF can silently corrupt the
|
||||
# section that encodes PT_INTERP, causing sharun -g to find no interpreter and
|
||||
# skip the copy. We do it here from the original install-root binary (never
|
||||
# touched by sed) so it is guaranteed present regardless of sharun -g's outcome.
|
||||
_interp=$(patchelf --print-interpreter "${DESTDIR}/usr/bin/citron" 2>/dev/null || true)
|
||||
if [ -z "${_interp}" ]; then
|
||||
# patchelf fallback: parse readelf output
|
||||
_interp=$(readelf -l "${DESTDIR}/usr/bin/citron" 2>/dev/null \
|
||||
| awk '/\[Requesting program interpreter:/{gsub(/[][]/,""); print $NF}')
|
||||
fi
|
||||
if [ -n "${_interp}" ] && [ -f "${_interp}" ]; then
|
||||
mkdir -p "${_appdir}/shared/lib"
|
||||
cp -L "${_interp}" "${_appdir}/shared/lib/${_interp##*/}"
|
||||
printf 'Staged interpreter: %s\n' "${_interp##*/}"
|
||||
else
|
||||
printf 'WARNING: could not determine PT_INTERP of citron; AppImage may fail with "Interpreter not found!"\n' >&2
|
||||
fi
|
||||
|
||||
find "${_appdir}" -name 'libvulkan_*.so' -delete 2>/dev/null || true
|
||||
find "${_appdir}" -name 'libLLVM*.so*' -delete 2>/dev/null || true
|
||||
find "${_appdir}" -path '*/vulkan/icd.d/*' -delete 2>/dev/null || true
|
||||
find "${_appdir}" -path '*/vulkan/implicit_layer.d/*' -delete 2>/dev/null || true
|
||||
find "${_appdir}" -path '*/pipewire-*/*' -type f -delete 2>/dev/null || true
|
||||
find "${_appdir}" -path '*/spa-*/*' -type f -delete 2>/dev/null || true
|
||||
find "${_appdir}" -path '*/alsa-lib/*' -type f -delete 2>/dev/null || true
|
||||
find "${_appdir}" -path '*/gconv/*' -type f -delete 2>/dev/null || true
|
||||
|
||||
# Remove system XCB libs that duplicate the CPM xcb-build copies.
|
||||
#
|
||||
# citron's DT_RPATH (set by _patch_binary_rpaths) points to xcb-build before
|
||||
# any system path. lib4bin strips those RPATHs, so at runtime all resolution
|
||||
# goes through lib.path. The Qt XCB plugin (libqxcb.so from CPM Qt) pulls in
|
||||
# system libxcb.so.1, libXau.so.6, and libXdmcp.so.6 via its own ldd chain,
|
||||
# staging them flat in AppDir/lib/. The xcb-build copies of the same three
|
||||
# libraries are already in AppDir at the absolute-mirrored path; keeping both
|
||||
# is dead weight and violates the "CPM over system" policy.
|
||||
#
|
||||
# -maxdepth 1 on AppDir/lib/ hits only the flat system copies; the xcb-build
|
||||
# copies are nested deep under home/thayne/…/xcb-build/lib/ and are untouched.
|
||||
find "${_appdir}/lib" -maxdepth 1 -name 'libxcb.so*' -delete 2>/dev/null || true
|
||||
find "${_appdir}/lib" -maxdepth 1 -name 'libXau.so*' -delete 2>/dev/null || true
|
||||
find "${_appdir}/lib" -maxdepth 1 -name 'libXdmcp.so*' -delete 2>/dev/null || true
|
||||
|
||||
# Qt Multimedia's FFmpeg backend — belt-and-suspenders removal.
|
||||
#
|
||||
# citron is built with -DCITRON_USE_BUNDLED_FFMPEG=ON (static FFmpeg for game
|
||||
# media decoding) and -DCITRON_USE_QT_MULTIMEDIA=OFF (set in build-citron-linux.sh).
|
||||
# With CITRON_USE_QT_MULTIMEDIA=OFF these files are never staged because Qt
|
||||
# Multimedia is not linked. These find/delete lines remain as a safety net in
|
||||
# case the flag is ever changed or if a future Qt version starts staging the
|
||||
# shared FFmpeg unconditionally.
|
||||
#
|
||||
# Background: Qt Multimedia pulls in a SEPARATE shared FFmpeg
|
||||
# (libavcodec.so.61 ~80 MB, libavformat.so.61, libavutil.so.59, libswresample,
|
||||
# libswscale) via libffmpegmediaplugin.so. This is distinct from citron's own
|
||||
# statically compiled FFmpeg and adds ~100 MB to the AppImage for no benefit
|
||||
# (all camera/video recording code is Qt5-only and permanently dead under Qt6).
|
||||
find "${_appdir}" -name 'libffmpegmediaplugin.so' -delete 2>/dev/null || true
|
||||
find "${_appdir}" -name 'libQt6FFmpegStub-*.so*' -delete 2>/dev/null || true
|
||||
find "${_appdir}" -name 'libavcodec.so*' -delete 2>/dev/null || true
|
||||
find "${_appdir}" -name 'libavformat.so*' -delete 2>/dev/null || true
|
||||
find "${_appdir}" -name 'libavutil.so*' -delete 2>/dev/null || true
|
||||
find "${_appdir}" -name 'libswresample.so*' -delete 2>/dev/null || true
|
||||
find "${_appdir}" -name 'libswscale.so*' -delete 2>/dev/null || true
|
||||
|
||||
|
||||
|
||||
# Write a portable qt.conf next to libQt6Core.so.6 in shared/lib/.
|
||||
#
|
||||
# CPM Qt's libQt6Core.so.6 has a baked-in INSTALL_PREFIX of
|
||||
# /home/<user>/cpmcache/qt-bin/6.9.x/gcc_64. Without an explicit qt.conf,
|
||||
# Qt uses that absolute prefix to locate its plugins and translations at
|
||||
# runtime — which works on the build machine (the CPM directory exists on
|
||||
# disk, outside the AppImage) but silently breaks on every other machine.
|
||||
#
|
||||
# A qt.conf placed next to libQt6Core.so.6 (AppDir/shared/lib/) overrides
|
||||
# the embedded prefix. All [Paths] entries are relative to Prefix; Prefix
|
||||
# itself is relative to the qt.conf file's directory.
|
||||
#
|
||||
# qt.conf at: AppDir/shared/lib/qt.conf
|
||||
# Prefix = .. → AppDir/shared/
|
||||
# Plugins = lib → AppDir/shared/lib/ (flat dir where lib4bin stages all plugins)
|
||||
# Translations → AppDir/usr/share/qt6/translations
|
||||
#
|
||||
# This also clobbers any qt.conf from the CPM installation that lib4bin or
|
||||
# strace may have staged here, ensuring absolute build-machine paths cannot
|
||||
# escape into the AppImage.
|
||||
mkdir -p ./AppDir/shared/lib
|
||||
cat > ./AppDir/shared/lib/qt.conf << 'QTCONF_EOF'
|
||||
[Paths]
|
||||
Prefix = ..
|
||||
Plugins = lib
|
||||
Imports = lib/qt6/qml
|
||||
Qml2Imports = lib/qt6/qml
|
||||
Translations = lib/qt6/translations
|
||||
QTCONF_EOF
|
||||
|
||||
# Rename app in desktop file if building a devel/nightly AppImage
|
||||
if [ "${DEVEL:-false}" = 'true' ]; then
|
||||
sed -i 's|^Name=citron$|Name=citron nightly|' ./AppDir/*.desktop 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Allow system Vulkan ICD to override the bundled one at runtime, and write
|
||||
# PGO profile data next to the running AppImage on exit (matches the old
|
||||
# linuxdeploy $APPIMAGE_DIR convention). $APPIMAGE is exported by sharun's
|
||||
# AppRun at runtime and points to the AppImage file's own path.
|
||||
{
|
||||
printf 'SHARUN_ALLOW_SYS_VK_ICD=1\n'
|
||||
printf 'LLVM_PROFILE_FILE=$(dirname "$APPIMAGE")/default-%%p.profraw\n'
|
||||
} > ./AppDir/.env
|
||||
|
||||
# Build the AppImage
|
||||
./quick-sharun --make-appimage
|
||||
|
||||
mkdir -p "${OUTPATH}"
|
||||
mv -v ./*.AppImage "${OUTPATH}/" 2>/dev/null || true
|
||||
mv -v ./*.AppImage.* "${OUTPATH}/" 2>/dev/null || true
|
||||
|
||||
# Pack the portable tar.zst alongside the AppImage — the "+ tar.zst" half
|
||||
# promised by this script's header comment, previously unimplemented.
|
||||
#
|
||||
# AppDir at this point is the exact same fully-debloated tree that was just
|
||||
# squashed into the AppImage above (every DEPLOY_*/STRACE_MODE suppression and
|
||||
# the post-dep-scan find/-delete block above already ran before
|
||||
# --make-appimage), so this is a complete, AppRun-runnable, install-free
|
||||
# alternative to the AppImage — not a partial copy. (AppDir/usr/ is not used
|
||||
# by this layout — translations land under AppDir/lib/qt6/, see comment above —
|
||||
# so packing only "usr", as the pre-quick-sharun pipeline did, would produce a
|
||||
# near-empty archive here.)
|
||||
#
|
||||
# AppDir is then removed. It has no further purpose once packed, and leaving
|
||||
# it sitting in OUTPATH means anything that uploads/copies OUTPATH wholesale
|
||||
# (e.g. a CI artifact upload step) drags along every loose unpacked library a
|
||||
# second time alongside the AppImage that already contains them compressed.
|
||||
if [ -d ./AppDir ]; then
|
||||
if ! command -v zstd >/dev/null 2>&1; then
|
||||
printf 'ERROR: zstd is not installed — cannot produce %s.tar.zst\n' "${OUTNAME_BASE}" >&2
|
||||
printf 'Install zstd (e.g. apt-get install zstd) and re-run.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
tar -c --zstd -f "${OUTPATH}/${OUTNAME_BASE}.tar.zst" -C ./AppDir .
|
||||
rm -rf ./AppDir
|
||||
fi
|
||||
|
||||
# Clean up build-tool byproducts that land in this same directory (OUTPATH),
|
||||
# not just AppDir. build-citron-linux.sh sets OUTPATH to the same working
|
||||
# directory this script cd's into and downloads tooling into, so anything
|
||||
# these tools write to cwd ends up sitting next to the final artifacts unless
|
||||
# removed explicitly:
|
||||
#
|
||||
# quick-sharun — the tool itself, copied to ./quick-sharun near the top of
|
||||
# this script (see the vendoring comment above). It has no
|
||||
# purpose after packaging finishes.
|
||||
# appinfo — a debug/metadata text file written to cwd by the
|
||||
# underlying appimagetool binary during --make-appimage.
|
||||
# Not generated by quick-sharun.sh itself, not meant for
|
||||
# redistribution — internal build debug output only.
|
||||
rm -f ./quick-sharun ./appinfo
|
||||
|
||||
echo "Artifacts in: ${OUTPATH}"
|
||||
File diff suppressed because it is too large
Load Diff
+1
-11
@@ -604,17 +604,7 @@ if (ENABLE_QT AND NOT CITRON_USE_CPM)
|
||||
message(STATUS "Using host Qt at ${QT_HOST_PATH}")
|
||||
endif()
|
||||
|
||||
# Early Qt6 discovery: find Widgets and Concurrent unconditionally; add
|
||||
# Multimedia only when CITRON_USE_QT_MULTIMEDIA is ON. This makes the
|
||||
# cmake flag actually functional: with CITRON_USE_QT_MULTIMEDIA=OFF the
|
||||
# Multimedia module and its shared FFmpeg backend (libffmpegmediaplugin.so,
|
||||
# libavcodec.so.61 ~80 MB, etc.) are not linked at all.
|
||||
set(_qt_probe_components Widgets Concurrent)
|
||||
if (CITRON_USE_QT_MULTIMEDIA)
|
||||
list(APPEND _qt_probe_components Multimedia)
|
||||
endif()
|
||||
find_package(Qt6 REQUIRED COMPONENTS ${_qt_probe_components})
|
||||
unset(_qt_probe_components)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Concurrent)
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
find_package(Qt6 REQUIRED COMPONENTS DBus)
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
#
|
||||
# Target variants:
|
||||
# Windows (native or cross-compile target) → win64_llvm_mingw
|
||||
# Linux native x86-64 → linux_gcc_64 (via aqt)
|
||||
# Linux native aarch64 → linux_gcc_arm64 (host: linux_arm64)
|
||||
# Linux native → linux_gcc_64
|
||||
#
|
||||
# Cross-compilation (Linux host → Windows target):
|
||||
# QT_HOST_PATH is set to a Linux Qt install so moc/rcc/uic run on the host.
|
||||
@@ -71,22 +70,12 @@ else()
|
||||
set(_QT_DIR_NAME "macos")
|
||||
set(_QT_CMAKE_SUB "lib/cmake/Qt6")
|
||||
else()
|
||||
# Native Linux — pick host/arch variant to match the build processor so
|
||||
# moc/rcc/uic (which run on the build host) are the correct ELF arch.
|
||||
# aqt uses separate host-OS strings for x86-64 ("linux") and arm64
|
||||
# ("linux_arm64"); the arch token is then linux_gcc_64 or linux_gcc_arm64.
|
||||
# Native Linux
|
||||
set(_QT_OS "linux")
|
||||
set(_QT_TARGET "desktop")
|
||||
set(_QT_ARCH "linux_gcc_64")
|
||||
set(_QT_DIR_NAME "gcc_64")
|
||||
set(_QT_CMAKE_SUB "lib/cmake/Qt6")
|
||||
if (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "aarch64|arm64" OR ARCHITECTURE_arm64)
|
||||
set(_QT_OS "linux_arm64")
|
||||
set(_QT_TARGET "desktop")
|
||||
set(_QT_ARCH "linux_gcc_arm64")
|
||||
set(_QT_DIR_NAME "gcc_arm64")
|
||||
else()
|
||||
set(_QT_OS "linux")
|
||||
set(_QT_TARGET "desktop")
|
||||
set(_QT_ARCH "linux_gcc_64")
|
||||
set(_QT_DIR_NAME "gcc_64")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -123,29 +112,23 @@ else()
|
||||
message(STATUS "[Qt] Qt ${CITRON_QT_VERSION} target downloaded")
|
||||
endif()
|
||||
|
||||
# Download additional modules (imageformats, svg, tools).
|
||||
# qtmultimedia is intentionally excluded: all Qt Multimedia camera code in
|
||||
# citron is guarded by (QT_VERSION < 6.0.0) && CITRON_USE_QT_MULTIMEDIA,
|
||||
# so it is permanently dead code under Qt6. CITRON_USE_QT_MULTIMEDIA=OFF
|
||||
# is set in build-citron-linux.sh, but the Qt5-only guard makes it a no-op
|
||||
# regardless. Including qtmultimedia in --modules was the sole cause of the
|
||||
# recurring "[Qt] Additional module install failed" CI warning: aqt silently
|
||||
# rejects module name mismatches for certain arch/version combos.
|
||||
# Download additional modules (multimedia, imageformats, svg)
|
||||
set(_QT_MM_CMAKE "${_QT_TARGET_DIR}/lib/cmake/Qt6Multimedia/Qt6MultimediaConfig.cmake")
|
||||
set(_QT_SVG_CMAKE "${_QT_TARGET_DIR}/lib/cmake/Qt6Svg/Qt6SvgConfig.cmake")
|
||||
set(_QT_TOOL_CMAKE "${_QT_TARGET_DIR}/lib/cmake/Qt6CoreTools/Qt6CoreToolsConfig.cmake")
|
||||
if (NOT EXISTS "${_QT_SVG_CMAKE}" OR NOT EXISTS "${_QT_TOOL_CMAKE}")
|
||||
message(STATUS "[Qt] Downloading Qt ${CITRON_QT_VERSION} additional modules (qttools, qtimageformats)...")
|
||||
if (NOT EXISTS "${_QT_MM_CMAKE}" OR NOT EXISTS "${_QT_SVG_CMAKE}" OR NOT EXISTS "${_QT_TOOL_CMAKE}")
|
||||
message(STATUS "[Qt] Downloading Qt ${CITRON_QT_VERSION} additional modules...")
|
||||
execute_process(
|
||||
COMMAND ${_AQT_EXECUTABLE} install-qt
|
||||
${_QT_OS} ${_QT_TARGET}
|
||||
${CITRON_QT_VERSION} ${_QT_ARCH}
|
||||
--outputdir "${CITRON_QT_BASE_DIR}"
|
||||
--modules qttools qtimageformats
|
||||
RESULT_VARIABLE _qt_addl_result
|
||||
--modules qttools qtmultimedia qtimageformats
|
||||
RESULT_VARIABLE _qt_mm_result
|
||||
OUTPUT_QUIET ERROR_QUIET
|
||||
)
|
||||
if (NOT _qt_addl_result EQUAL 0)
|
||||
message(WARNING "[Qt] Additional module install failed (qttools/qtimageformats) — build may fail")
|
||||
if (NOT _qt_mm_result EQUAL 0)
|
||||
message(WARNING "[Qt] Additional module install failed — build may fail")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -189,7 +172,14 @@ if (CMAKE_HOST_UNIX AND WIN32)
|
||||
if (NOT _qt_host_result EQUAL 0)
|
||||
message(WARNING "[Qt] Host Qt download failed — cross-compile may fail")
|
||||
endif()
|
||||
# qtmultimedia not downloaded for host Qt — dead code under Qt6 (see target Qt comment above)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${_AQT_EXECUTABLE} install-qt linux desktop
|
||||
${CITRON_QT_VERSION} linux_gcc_64
|
||||
--outputdir "${CITRON_QT_BASE_DIR}"
|
||||
--modules qtmultimedia
|
||||
OUTPUT_QUIET ERROR_QUIET
|
||||
)
|
||||
endif()
|
||||
|
||||
if (EXISTS "${_QT_HOST_CMAKE}")
|
||||
|
||||
+44
-400
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,86 @@
|
||||
@echo off
|
||||
REM SPDX-FileCopyrightText: 2026 citron Emulator Project
|
||||
REM SPDX-License-Identifier: GPL-3.0-or-later
|
||||
REM
|
||||
REM One-click MinGW build launcher for Citron Neo.
|
||||
REM Finds your MSYS2 installation and runs build-for-mingw.sh in UCRT64 mode.
|
||||
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
REM ---------- locate MSYS2 ----------
|
||||
set "MSYS2_PATH="
|
||||
|
||||
REM Check common install locations
|
||||
for %%P in (
|
||||
"C:\msys64"
|
||||
"C:\msys2"
|
||||
"%USERPROFILE%\msys64"
|
||||
"%USERPROFILE%\msys2"
|
||||
"D:\msys64"
|
||||
"D:\msys2"
|
||||
) do (
|
||||
if exist "%%~P\usr\bin\bash.exe" (
|
||||
set "MSYS2_PATH=%%~P"
|
||||
goto :found_msys2
|
||||
)
|
||||
)
|
||||
|
||||
REM Try the PATH
|
||||
where bash.exe >nul 2>&1
|
||||
if %ERRORLEVEL% equ 0 (
|
||||
for /f "delims=" %%I in ('where bash.exe') do (
|
||||
set "BASH_LOC=%%~dpI"
|
||||
if exist "!BASH_LOC!..\..\..\usr\bin\bash.exe" (
|
||||
for %%J in ("!BASH_LOC!..\..\..") do set "MSYS2_PATH=%%~fJ"
|
||||
goto :found_msys2
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
echo.
|
||||
echo ERROR: Could not find an MSYS2 installation.
|
||||
echo.
|
||||
echo Please install MSYS2 from https://www.msys2.org/ and try again,
|
||||
echo or set MSYS2_PATH before running this script:
|
||||
echo.
|
||||
echo set MSYS2_PATH=C:\msys64
|
||||
echo build-for-mingw.bat
|
||||
echo.
|
||||
pause
|
||||
exit /b 1
|
||||
|
||||
:found_msys2
|
||||
echo Found MSYS2 at: %MSYS2_PATH%
|
||||
|
||||
REM ---------- resolve script path ----------
|
||||
set "SCRIPT_DIR=%~dp0"
|
||||
REM Remove trailing backslash
|
||||
if "%SCRIPT_DIR:~-1%"=="\" set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%"
|
||||
|
||||
REM Convert Windows path to MSYS2 path (C:\foo\bar -> /c/foo/bar)
|
||||
set "MSYS_SOURCE=%SCRIPT_DIR:\=/%"
|
||||
set "MSYS_SOURCE=/%MSYS_SOURCE:~0,1%%MSYS_SOURCE:~2%"
|
||||
REM Lowercase the drive letter
|
||||
for %%a in (a b c d e f g h i j k l m n o p q r s t u v w x y z) do (
|
||||
set "MSYS_SOURCE=!MSYS_SOURCE:/%%a/=/%%a/!"
|
||||
)
|
||||
|
||||
REM ---------- run the build ----------
|
||||
echo.
|
||||
echo Launching MSYS2 UCRT64 build ...
|
||||
echo.
|
||||
|
||||
"%MSYS2_PATH%\usr\bin\env.exe" MSYSTEM=UCRT64 ^
|
||||
"%MSYS2_PATH%\usr\bin\bash.exe" -lc ^
|
||||
"cd '%MSYS_SOURCE%' && bash build-for-mingw.sh %*"
|
||||
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo.
|
||||
echo Build failed with error code %ERRORLEVEL%.
|
||||
pause
|
||||
exit /b %ERRORLEVEL%
|
||||
)
|
||||
|
||||
echo.
|
||||
echo Build succeeded! Executables are in build-mingw\bin\
|
||||
pause
|
||||
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-FileCopyrightText: 2026 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# Automated MinGW (MSYS2 UCRT64) build script for Citron Neo.
|
||||
# Run from the MSYS2 UCRT64 shell, or use build-for-mingw.bat to launch it.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ---------- configuration ----------
|
||||
BUILD_DIR="${1:-build-mingw}"
|
||||
BUILD_TYPE="${2:-Release}"
|
||||
JOBS="${3:-$(nproc)}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SOURCE_DIR="$SCRIPT_DIR"
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
|
||||
ok() { echo -e "${GREEN}[ OK ]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||
fail() { echo -e "${RED}[FAIL]${NC} $*"; exit 1; }
|
||||
|
||||
# ---------- environment check ----------
|
||||
if [[ -z "${MSYSTEM:-}" ]]; then
|
||||
fail "Not running inside an MSYS2 shell. Use the UCRT64 terminal or build-for-mingw.bat."
|
||||
fi
|
||||
if [[ "$MSYSTEM" != "UCRT64" ]]; then
|
||||
warn "MSYSTEM is '$MSYSTEM', expected 'UCRT64'. Builds may use the wrong toolchain."
|
||||
fi
|
||||
|
||||
command -v pacman >/dev/null 2>&1 || fail "pacman not found — is this really MSYS2?"
|
||||
|
||||
# ---------- dependency installation ----------
|
||||
info "Installing / updating build tools and dependencies via pacman …"
|
||||
|
||||
PACKAGES=(
|
||||
# build tools
|
||||
mingw-w64-ucrt-x86_64-cmake
|
||||
mingw-w64-ucrt-x86_64-ninja
|
||||
mingw-w64-ucrt-x86_64-toolchain
|
||||
|
||||
# required libraries
|
||||
mingw-w64-ucrt-x86_64-boost
|
||||
mingw-w64-ucrt-x86_64-fmt
|
||||
mingw-w64-ucrt-x86_64-nlohmann-json
|
||||
mingw-w64-ucrt-x86_64-opus
|
||||
mingw-w64-ucrt-x86_64-SDL2
|
||||
mingw-w64-ucrt-x86_64-qt6-base
|
||||
mingw-w64-ucrt-x86_64-qt6-multimedia
|
||||
mingw-w64-ucrt-x86_64-qt6-svg
|
||||
mingw-w64-ucrt-x86_64-qt6-tools
|
||||
mingw-w64-ucrt-x86_64-ffmpeg
|
||||
mingw-w64-ucrt-x86_64-openal
|
||||
mingw-w64-ucrt-x86_64-vulkan-headers
|
||||
mingw-w64-ucrt-x86_64-vulkan-utility-libraries
|
||||
mingw-w64-ucrt-x86_64-vulkan-memory-allocator
|
||||
mingw-w64-ucrt-x86_64-libusb
|
||||
mingw-w64-ucrt-x86_64-enet
|
||||
mingw-w64-ucrt-x86_64-stb
|
||||
)
|
||||
|
||||
pacman -S --needed --noconfirm "${PACKAGES[@]}" || fail "pacman install failed"
|
||||
ok "All dependencies installed."
|
||||
|
||||
# ---------- configure ----------
|
||||
info "Configuring CMake (${BUILD_TYPE}) in ${BUILD_DIR} …"
|
||||
mkdir -p "${SOURCE_DIR}/${BUILD_DIR}"
|
||||
cd "${SOURCE_DIR}/${BUILD_DIR}"
|
||||
|
||||
cmake "$SOURCE_DIR" -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE="$BUILD_TYPE" \
|
||||
-DCITRON_USE_BUNDLED_VCPKG=OFF \
|
||||
-DCITRON_USE_BUNDLED_SDL2=OFF \
|
||||
-DCITRON_USE_BUNDLED_QT=OFF \
|
||||
-DCITRON_USE_BUNDLED_FFMPEG=OFF \
|
||||
-DCITRON_USE_EXTERNAL_VULKAN_HEADERS=OFF \
|
||||
-DCITRON_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES=OFF \
|
||||
-DENABLE_QT_TRANSLATION=OFF \
|
||||
-DUSE_DISCORD_PRESENCE=OFF \
|
||||
-DCITRON_TESTS=OFF \
|
||||
-DENABLE_WEB_SERVICE=OFF \
|
||||
-DCITRON_USE_FASTER_LD=OFF \
|
||||
|| fail "CMake configuration failed"
|
||||
|
||||
ok "CMake configuration succeeded."
|
||||
|
||||
# ---------- build ----------
|
||||
info "Building with ${JOBS} parallel jobs …"
|
||||
ninja -j"$JOBS" || fail "Build failed"
|
||||
|
||||
ok "Build complete!"
|
||||
echo ""
|
||||
info "Executables are in: ${SOURCE_DIR}/${BUILD_DIR}/bin/"
|
||||
ls -lh "${SOURCE_DIR}/${BUILD_DIR}/bin/"*.exe 2>/dev/null || true
|
||||
echo ""
|
||||
ok "Done. You can run citron from: ${BUILD_DIR}/bin/citron.exe"
|
||||
Vendored
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 27 KiB |
Vendored
+1
-10
@@ -20,16 +20,7 @@ if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE)
|
||||
endif()
|
||||
|
||||
set(LIBUSB_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/libusb")
|
||||
# Source comes exclusively from CPM (libusb_src declared in dependencies.cmake).
|
||||
# LIBUSB_CPM_SOURCE_DIR is the CACHE INTERNAL bridge set there; libusb_src_SOURCE_DIR
|
||||
# is CPM's own auto-set and is always present even on a cache hit where _ADDED is false.
|
||||
if (DEFINED LIBUSB_CPM_SOURCE_DIR)
|
||||
set(LIBUSB_SRC_DIR "${LIBUSB_CPM_SOURCE_DIR}")
|
||||
elseif (DEFINED libusb_src_SOURCE_DIR)
|
||||
set(LIBUSB_SRC_DIR "${libusb_src_SOURCE_DIR}")
|
||||
else()
|
||||
set(LIBUSB_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libusb")
|
||||
endif()
|
||||
set(LIBUSB_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libusb")
|
||||
|
||||
# Workarounds for MSYS/MinGW
|
||||
if (MSYS)
|
||||
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
# SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Fix PGO DLL Script for Citron (Windows)
|
||||
# This script helps locate and copy the required pgort140.dll for MSVC PGO builds
|
||||
|
||||
param(
|
||||
[Parameter()]
|
||||
[string]$OutputDir = "build\bin\Release",
|
||||
|
||||
[Parameter()]
|
||||
[switch]$Help
|
||||
)
|
||||
|
||||
function Show-Usage {
|
||||
Write-Host @"
|
||||
Usage: .\fix-pgo-dll.ps1 [OPTIONS]
|
||||
|
||||
This script helps fix the "pgort140.DLL was not found" error by locating
|
||||
and copying the required PGO runtime DLL to your build output directory.
|
||||
|
||||
Options:
|
||||
-OutputDir PATH Target directory to copy DLL (default: build\bin\Release)
|
||||
-Help Show this help message
|
||||
|
||||
Example:
|
||||
.\fix-pgo-dll.ps1
|
||||
.\fix-pgo-dll.ps1 -OutputDir "C:\MyBuild\bin"
|
||||
"@
|
||||
}
|
||||
|
||||
function Write-Header {
|
||||
param([string]$Message)
|
||||
Write-Host "`n=================================================================" -ForegroundColor Cyan
|
||||
Write-Host $Message -ForegroundColor Cyan
|
||||
Write-Host "=================================================================`n" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
function Write-Info {
|
||||
param([string]$Message)
|
||||
Write-Host "[INFO] $Message" -ForegroundColor Green
|
||||
}
|
||||
|
||||
function Write-Warning {
|
||||
param([string]$Message)
|
||||
Write-Host "[WARNING] $Message" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
function Write-Error-Custom {
|
||||
param([string]$Message)
|
||||
Write-Host "[ERROR] $Message" -ForegroundColor Red
|
||||
}
|
||||
|
||||
if ($Help) {
|
||||
Show-Usage
|
||||
exit 0
|
||||
}
|
||||
|
||||
Write-Header "PGO DLL Fixer for Citron"
|
||||
|
||||
# Find Visual Studio installation
|
||||
$VSInstallPath = $null
|
||||
$VSWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||
|
||||
if (Test-Path $VSWhere) {
|
||||
Write-Info "Searching for Visual Studio installations..."
|
||||
$VSInstallPath = & $VSWhere -latest -property installationPath
|
||||
if ($VSInstallPath) {
|
||||
Write-Info "Found Visual Studio at: $VSInstallPath"
|
||||
}
|
||||
} else {
|
||||
Write-Warning "vswhere.exe not found. Trying common installation paths..."
|
||||
}
|
||||
|
||||
# Common Visual Studio installation paths
|
||||
$CommonPaths = @(
|
||||
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Community",
|
||||
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Professional",
|
||||
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Enterprise",
|
||||
"${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\Community",
|
||||
"${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\Professional",
|
||||
"${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\Enterprise"
|
||||
)
|
||||
|
||||
if (-not $VSInstallPath) {
|
||||
foreach ($path in $CommonPaths) {
|
||||
if (Test-Path $path) {
|
||||
$VSInstallPath = $path
|
||||
Write-Info "Found Visual Studio at: $VSInstallPath"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $VSInstallPath) {
|
||||
Write-Error-Custom "Visual Studio installation not found!"
|
||||
Write-Host "Please ensure Visual Studio 2019 or 2022 is installed with C++ support."
|
||||
Write-Host "You can download it from: https://visualstudio.microsoft.com/downloads/"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Search for pgort140.dll
|
||||
Write-Info "Searching for pgort140.dll..."
|
||||
|
||||
$DllPaths = @(
|
||||
"$VSInstallPath\VC\Redist\MSVC\*\x64\Microsoft.VC*.CRT\pgort140.dll",
|
||||
"$VSInstallPath\VC\Redist\MSVC\*\x86\Microsoft.VC*.CRT\pgort140.dll",
|
||||
"$VSInstallPath\VC\Tools\MSVC\*\bin\Hostx64\x64\pgort140.dll",
|
||||
"$VSInstallPath\VC\Tools\MSVC\*\bin\Hostx64\x86\pgort140.dll",
|
||||
"$VSInstallPath\VC\Tools\MSVC\*\bin\Hostx86\x64\pgort140.dll",
|
||||
"$VSInstallPath\VC\Tools\MSVC\*\bin\Hostx86\x86\pgort140.dll"
|
||||
)
|
||||
|
||||
$FoundDll = $null
|
||||
foreach ($pattern in $DllPaths) {
|
||||
$matches = Get-ChildItem -Path $pattern -ErrorAction SilentlyContinue
|
||||
if ($matches) {
|
||||
$FoundDll = $matches[0].FullName
|
||||
Write-Info "Found pgort140.dll at: $FoundDll"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $FoundDll) {
|
||||
Write-Error-Custom "pgort140.dll not found in Visual Studio installation!"
|
||||
Write-Host "This usually means:"
|
||||
Write-Host "1. Visual Studio was installed without PGO support"
|
||||
Write-Host "2. You need to install the 'MSVC v143 - VS 2022 C++ x64/x86 build tools' component"
|
||||
Write-Host "3. Try repairing your Visual Studio installation"
|
||||
Write-Host ""
|
||||
Write-Host "To fix this:"
|
||||
Write-Host "1. Open Visual Studio Installer"
|
||||
Write-Host "2. Click 'Modify' on your Visual Studio installation"
|
||||
Write-Host "3. Go to 'Individual components' tab"
|
||||
Write-Host "4. Search for 'PGO' and ensure it's checked"
|
||||
Write-Host "5. Click 'Modify' to install the component"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Create output directory if it doesn't exist
|
||||
if (-not (Test-Path $OutputDir)) {
|
||||
Write-Info "Creating output directory: $OutputDir"
|
||||
New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null
|
||||
}
|
||||
|
||||
# Copy the DLL
|
||||
try {
|
||||
Write-Info "Copying pgort140.dll to: $OutputDir"
|
||||
Copy-Item -Path $FoundDll -Destination $OutputDir -Force
|
||||
Write-Info "Successfully copied pgort140.dll!"
|
||||
|
||||
# Verify the copy
|
||||
$CopiedDll = Join-Path $OutputDir "pgort140.dll"
|
||||
if (Test-Path $CopiedDll) {
|
||||
Write-Info "Verification: pgort140.dll is now available in $OutputDir"
|
||||
Write-Host ""
|
||||
Write-Host "You can now run your PGO instrumented Citron build!"
|
||||
} else {
|
||||
Write-Error-Custom "Failed to copy pgort140.dll"
|
||||
exit 1
|
||||
}
|
||||
} catch {
|
||||
Write-Error-Custom "Error copying pgort140.dll: $($_.Exception.Message)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Header "PGO DLL Fix Complete!"
|
||||
Write-Info "The pgort140.dll has been copied to your build output directory."
|
||||
Write-Info "Your PGO instrumented Citron build should now run without the DLL error."
|
||||
Write-Host ""
|
||||
Write-Info "Next steps:"
|
||||
Write-Host "1. Run your PGO instrumented build"
|
||||
Write-Host "2. Play games to collect profile data"
|
||||
Write-Host "3. Rebuild with PGO USE stage for optimization"
|
||||
+344
@@ -0,0 +1,344 @@
|
||||
# SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# PGO Build Script for Citron (Windows/PowerShell)
|
||||
# This script automates the Profile-Guided Optimization build process
|
||||
|
||||
param(
|
||||
[Parameter(Position=0, Mandatory=$true)]
|
||||
[ValidateSet('generate', 'use', 'clean', 'merge', 'summary')]
|
||||
[string]$Stage,
|
||||
|
||||
[Parameter()]
|
||||
[int]$Jobs = 0,
|
||||
|
||||
[Parameter()]
|
||||
[switch]$EnableLTO,
|
||||
|
||||
[Parameter()]
|
||||
[switch]$Help
|
||||
)
|
||||
|
||||
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$BuildDir = Join-Path $ScriptDir "build"
|
||||
$PgoProfilesDir = Join-Path $BuildDir "pgo-profiles"
|
||||
$BackupProfilesDir = Join-Path $ScriptDir "pgo-profiles-backup"
|
||||
|
||||
function Show-Usage {
|
||||
Write-Host @"
|
||||
Usage: .\pgo-build.ps1 [STAGE] [OPTIONS]
|
||||
|
||||
STAGE can be:
|
||||
generate - Build with PGO instrumentation (Stage 1)
|
||||
use - Build using PGO profile data (Stage 2)
|
||||
clean - Clean build directory but preserve profiles
|
||||
merge - Merge PGC files into PGD without rebuilding
|
||||
summary - Show profile coverage statistics
|
||||
|
||||
Example workflow:
|
||||
.\pgo-build.ps1 generate # Build instrumented version
|
||||
# Run citron.exe, play 2-3 games for 5-10 min each, exit cleanly
|
||||
.\pgo-build.ps1 merge # Merge collected profiles
|
||||
.\pgo-build.ps1 summary # Check coverage
|
||||
.\pgo-build.ps1 use # Build optimized version
|
||||
|
||||
Options:
|
||||
-Jobs N Number of parallel jobs (default: auto-detect)
|
||||
-EnableLTO Enable Link-Time Optimization
|
||||
-Help Show this help message
|
||||
"@
|
||||
}
|
||||
|
||||
function Write-Header {
|
||||
param([string]$Message)
|
||||
Write-Host "`n=================================================================" -ForegroundColor Cyan
|
||||
Write-Host $Message -ForegroundColor Cyan
|
||||
Write-Host "=================================================================`n" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
function Write-Info {
|
||||
param([string]$Message)
|
||||
Write-Host "[INFO] $Message" -ForegroundColor Green
|
||||
}
|
||||
|
||||
function Write-Warning {
|
||||
param([string]$Message)
|
||||
Write-Host "[WARNING] $Message" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
function Write-Error-Custom {
|
||||
param([string]$Message)
|
||||
Write-Host "[ERROR] $Message" -ForegroundColor Red
|
||||
}
|
||||
|
||||
function Find-Pgomgr {
|
||||
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||
if (Test-Path $vswhere) {
|
||||
$vsPath = & $vswhere -latest -property installationPath
|
||||
$pgomgr = Get-ChildItem -Path "$vsPath\VC\Tools\MSVC" -Recurse -Filter "pgomgr.exe" -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.FullName -match "Hostx64\\x64" } |
|
||||
Select-Object -First 1
|
||||
if ($pgomgr) { return $pgomgr.FullName }
|
||||
}
|
||||
$found = Get-Command pgomgr.exe -ErrorAction SilentlyContinue
|
||||
if ($found) { return $found.Source }
|
||||
return $null
|
||||
}
|
||||
|
||||
function Clean-StaleProfiles {
|
||||
if (-not (Test-Path $PgoProfilesDir)) { return }
|
||||
|
||||
$pgcFiles = Get-ChildItem -Path $PgoProfilesDir -Filter "*.pgc" -ErrorAction SilentlyContinue
|
||||
if ($pgcFiles -and $pgcFiles.Count -gt 0) {
|
||||
Write-Info "Removing $($pgcFiles.Count) stale PGC file(s)..."
|
||||
$pgcFiles | Remove-Item -Force
|
||||
}
|
||||
}
|
||||
|
||||
function Merge-Profiles {
|
||||
$pgomgr = Find-Pgomgr
|
||||
if (-not $pgomgr) {
|
||||
Write-Warning "pgomgr.exe not found. Cannot merge profiles."
|
||||
Write-Info "Ensure Visual Studio Build Tools are installed and on PATH."
|
||||
return $false
|
||||
}
|
||||
|
||||
$pgdFiles = Get-ChildItem -Path $PgoProfilesDir -Filter "*.pgd" -ErrorAction SilentlyContinue
|
||||
if (-not $pgdFiles -or $pgdFiles.Count -eq 0) {
|
||||
Write-Warning "No PGD files found in $PgoProfilesDir"
|
||||
return $false
|
||||
}
|
||||
|
||||
$pgcFiles = Get-ChildItem -Path $PgoProfilesDir -Filter "*.pgc" -ErrorAction SilentlyContinue
|
||||
if (-not $pgcFiles -or $pgcFiles.Count -eq 0) {
|
||||
Write-Info "No PGC files to merge."
|
||||
return $true
|
||||
}
|
||||
|
||||
foreach ($pgd in $pgdFiles) {
|
||||
Write-Info "Merging PGC files into $($pgd.Name)..."
|
||||
& $pgomgr /merge $pgd.FullName
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Warning "pgomgr /merge returned exit code $LASTEXITCODE for $($pgd.Name)"
|
||||
}
|
||||
}
|
||||
|
||||
# Clean PGC files after successful merge
|
||||
Write-Info "Cleaning merged PGC files..."
|
||||
Get-ChildItem -Path $PgoProfilesDir -Filter "*.pgc" -ErrorAction SilentlyContinue | Remove-Item -Force
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
function Show-ProfileSummary {
|
||||
$pgomgr = Find-Pgomgr
|
||||
if (-not $pgomgr) {
|
||||
Write-Warning "pgomgr.exe not found. Cannot show summary."
|
||||
return
|
||||
}
|
||||
|
||||
$pgdFiles = Get-ChildItem -Path $PgoProfilesDir -Filter "*.pgd" -ErrorAction SilentlyContinue
|
||||
if (-not $pgdFiles -or $pgdFiles.Count -eq 0) {
|
||||
Write-Warning "No PGD files found in $PgoProfilesDir"
|
||||
return
|
||||
}
|
||||
|
||||
foreach ($pgd in $pgdFiles) {
|
||||
Write-Header "Profile Summary: $($pgd.Name)"
|
||||
& $pgomgr /summary $pgd.FullName
|
||||
Write-Host ""
|
||||
}
|
||||
}
|
||||
|
||||
if ($Help) {
|
||||
Show-Usage
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Auto-detect number of processors
|
||||
if ($Jobs -eq 0) {
|
||||
$Jobs = $env:NUMBER_OF_PROCESSORS
|
||||
if (-not $Jobs) { $Jobs = 4 }
|
||||
}
|
||||
|
||||
$LtoFlag = if ($EnableLTO) { "ON" } else { "OFF" }
|
||||
|
||||
# --- Clean stage ---
|
||||
if ($Stage -eq "clean") {
|
||||
Write-Header "Cleaning Build Directory"
|
||||
|
||||
if (Test-Path $PgoProfilesDir) {
|
||||
Write-Info "Backing up PGO profiles..."
|
||||
New-Item -ItemType Directory -Force -Path $BackupProfilesDir | Out-Null
|
||||
Copy-Item -Path "$PgoProfilesDir\*" -Destination $BackupProfilesDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
if (Test-Path $BuildDir) {
|
||||
Write-Info "Removing build directory..."
|
||||
Remove-Item -Path $BuildDir -Recurse -Force
|
||||
}
|
||||
|
||||
if (Test-Path $BackupProfilesDir) {
|
||||
Write-Info "Restoring PGO profiles..."
|
||||
New-Item -ItemType Directory -Force -Path $PgoProfilesDir | Out-Null
|
||||
Move-Item -Path "$BackupProfilesDir\*" -Destination $PgoProfilesDir -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path $BackupProfilesDir -Recurse -Force
|
||||
}
|
||||
|
||||
Write-Info "Clean complete!"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# --- Merge stage ---
|
||||
if ($Stage -eq "merge") {
|
||||
Write-Header "Merging PGO Profiles"
|
||||
|
||||
if (-not (Test-Path $PgoProfilesDir)) {
|
||||
Write-Error-Custom "Profile directory not found: $PgoProfilesDir"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$result = Merge-Profiles
|
||||
if ($result) {
|
||||
Write-Info "Merge complete! Run '.\pgo-build.ps1 summary' to check coverage."
|
||||
} else {
|
||||
Write-Error-Custom "Merge failed."
|
||||
exit 1
|
||||
}
|
||||
exit 0
|
||||
}
|
||||
|
||||
# --- Summary stage ---
|
||||
if ($Stage -eq "summary") {
|
||||
Write-Header "PGO Profile Summary"
|
||||
|
||||
if (-not (Test-Path $PgoProfilesDir)) {
|
||||
Write-Error-Custom "Profile directory not found: $PgoProfilesDir"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Show-ProfileSummary
|
||||
exit 0
|
||||
}
|
||||
|
||||
# --- Generate stage ---
|
||||
if ($Stage -eq "generate") {
|
||||
Write-Header "PGO Stage 1: Generate Profile Data"
|
||||
|
||||
# Clean stale PGC files from any previous run
|
||||
if (Test-Path $PgoProfilesDir) {
|
||||
Clean-StaleProfiles
|
||||
}
|
||||
|
||||
# Create build directory
|
||||
New-Item -ItemType Directory -Force -Path $BuildDir | Out-Null
|
||||
Set-Location $BuildDir
|
||||
|
||||
# Configure
|
||||
Write-Info "Configuring CMake..."
|
||||
cmake .. `
|
||||
-DCITRON_ENABLE_PGO_GENERATE=ON `
|
||||
-DCITRON_ENABLE_LTO=$LtoFlag `
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error-Custom "CMake configuration failed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Build
|
||||
Write-Info "Building instrumented Citron (this may take a while)..."
|
||||
cmake --build . --config Release -j $Jobs
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error-Custom "Build failed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Header "Build Complete!"
|
||||
Write-Host ""
|
||||
Write-Host " Training guide for best PGO results:" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Write-Host " 1. Run: .\bin\Release\citron.exe"
|
||||
Write-Host " 2. Launch a game and play PAST initial loading"
|
||||
Write-Host " (first-time shader compilation is a critical hot path)"
|
||||
Write-Host " 3. Play for at least 5-10 minutes per game"
|
||||
Write-Host " 4. Test 2-3 different games for broader code coverage"
|
||||
Write-Host " 5. Navigate menus, settings, and game list to profile the UI"
|
||||
Write-Host " 6. Exit citron cleanly (File -> Exit or Ctrl+Q)"
|
||||
Write-Host ""
|
||||
Write-Host " After each session, you can run:" -ForegroundColor Yellow
|
||||
Write-Host " .\pgo-build.ps1 merge # Consolidate collected profiles"
|
||||
Write-Host " .\pgo-build.ps1 summary # Check profile coverage"
|
||||
Write-Host ""
|
||||
Write-Host " When satisfied with coverage, build the optimized binary:" -ForegroundColor Yellow
|
||||
Write-Host " .\pgo-build.ps1 use"
|
||||
Write-Host ""
|
||||
|
||||
Set-Location $ScriptDir
|
||||
}
|
||||
|
||||
# --- Use stage ---
|
||||
if ($Stage -eq "use") {
|
||||
Write-Header "PGO Stage 2: Build Optimized Binary"
|
||||
|
||||
# Check if profile data exists
|
||||
if (-not (Test-Path $PgoProfilesDir) -or -not (Get-ChildItem $PgoProfilesDir -ErrorAction SilentlyContinue)) {
|
||||
Write-Error-Custom "No profile data found in $PgoProfilesDir"
|
||||
Write-Info "Please run the generate stage first and collect profile data"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Merge any outstanding PGC files before building
|
||||
Write-Info "Merging any outstanding PGC files..."
|
||||
Merge-Profiles | Out-Null
|
||||
|
||||
# Backup profiles if build directory exists
|
||||
if (Test-Path $BuildDir) {
|
||||
Write-Info "Backing up PGO profiles..."
|
||||
New-Item -ItemType Directory -Force -Path $BackupProfilesDir | Out-Null
|
||||
Copy-Item -Path "$PgoProfilesDir\*" -Destination $BackupProfilesDir -Recurse -Force
|
||||
Remove-Item -Path $BuildDir -Recurse -Force
|
||||
}
|
||||
|
||||
# Create build directory and restore profiles
|
||||
New-Item -ItemType Directory -Force -Path $BuildDir | Out-Null
|
||||
if (Test-Path $BackupProfilesDir) {
|
||||
New-Item -ItemType Directory -Force -Path $PgoProfilesDir | Out-Null
|
||||
Move-Item -Path "$BackupProfilesDir\*" -Destination $PgoProfilesDir -Force
|
||||
Remove-Item -Path $BackupProfilesDir -Recurse -Force
|
||||
}
|
||||
|
||||
Set-Location $BuildDir
|
||||
|
||||
# Configure
|
||||
Write-Info "Configuring CMake..."
|
||||
cmake .. `
|
||||
-DCITRON_ENABLE_PGO_USE=ON `
|
||||
-DCITRON_ENABLE_LTO=$LtoFlag `
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error-Custom "CMake configuration failed"
|
||||
Set-Location $ScriptDir
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Build
|
||||
Write-Info "Building optimized Citron (this may take a while)..."
|
||||
cmake --build . --config Release -j $Jobs
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error-Custom "Build failed"
|
||||
Set-Location $ScriptDir
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Header "Build Complete!"
|
||||
Write-Info "Your optimized Citron binary is ready!"
|
||||
Write-Info "Location: $BuildDir\bin\Release\citron.exe"
|
||||
Write-Host ""
|
||||
Write-Info "This build is optimized for your specific usage patterns."
|
||||
|
||||
Set-Location $ScriptDir
|
||||
}
|
||||
+328
@@ -0,0 +1,328 @@
|
||||
#!/bin/bash
|
||||
# SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# PGO Build Script for Citron (Linux/macOS)
|
||||
# This script automates the Profile-Guided Optimization build process
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BUILD_DIR="${SCRIPT_DIR}/build"
|
||||
PGO_PROFILES_DIR="${BUILD_DIR}/pgo-profiles"
|
||||
BACKUP_PROFILES_DIR="${SCRIPT_DIR}/pgo-profiles-backup"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
print_header() {
|
||||
echo -e "${BLUE}=================================================================${NC}"
|
||||
echo -e "${BLUE}$1${NC}"
|
||||
echo -e "${BLUE}=================================================================${NC}"
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
show_usage() {
|
||||
echo "Usage: $0 [STAGE] [OPTIONS]"
|
||||
echo ""
|
||||
echo "STAGE can be:"
|
||||
echo " generate - Build with PGO instrumentation (Stage 1)"
|
||||
echo " use - Build using PGO profile data (Stage 2)"
|
||||
echo " clean - Clean build directory but preserve profiles"
|
||||
echo " merge - Merge profile data (Clang: .profraw -> .profdata)"
|
||||
echo " summary - Show profile file statistics"
|
||||
echo ""
|
||||
echo "Example workflow:"
|
||||
echo " $0 generate # Build instrumented version"
|
||||
echo " # Run citron, play 2-3 games for 5-10 min each, exit cleanly"
|
||||
echo " $0 merge # Merge collected profiles"
|
||||
echo " $0 summary # Check coverage"
|
||||
echo " $0 use # Build optimized version"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -j N Number of parallel jobs (default: auto-detect)"
|
||||
echo " -lto Enable Link-Time Optimization"
|
||||
echo " -h Show this help message"
|
||||
}
|
||||
|
||||
clean_stale_profiles() {
|
||||
if [ ! -d "$PGO_PROFILES_DIR" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local gcda_count
|
||||
gcda_count=$(find "$PGO_PROFILES_DIR" -name "*.gcda" 2>/dev/null | wc -l)
|
||||
if [ "$gcda_count" -gt 0 ]; then
|
||||
print_info "Removing $gcda_count stale .gcda file(s)..."
|
||||
find "$PGO_PROFILES_DIR" -name "*.gcda" -delete
|
||||
fi
|
||||
|
||||
local profraw_count
|
||||
profraw_count=$(find "$PGO_PROFILES_DIR" -name "*.profraw" 2>/dev/null | wc -l)
|
||||
if [ "$profraw_count" -gt 0 ]; then
|
||||
print_info "Removing $profraw_count stale .profraw file(s)..."
|
||||
find "$PGO_PROFILES_DIR" -name "*.profraw" -delete
|
||||
fi
|
||||
}
|
||||
|
||||
merge_clang_profiles() {
|
||||
if ! command -v llvm-profdata &>/dev/null; then
|
||||
print_warning "llvm-profdata not found. Cannot merge Clang profiles."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local profraw_files
|
||||
profraw_files=$(find "$PGO_PROFILES_DIR" -name "*.profraw" 2>/dev/null)
|
||||
if [ -z "$profraw_files" ]; then
|
||||
print_info "No .profraw files to merge."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local count
|
||||
count=$(echo "$profraw_files" | wc -l)
|
||||
print_info "Merging $count .profraw file(s)..."
|
||||
|
||||
llvm-profdata merge \
|
||||
-output="${PGO_PROFILES_DIR}/default.profdata" \
|
||||
${profraw_files}
|
||||
|
||||
print_info "Merged into ${PGO_PROFILES_DIR}/default.profdata"
|
||||
|
||||
# Clean raw files after merge
|
||||
print_info "Cleaning merged .profraw files..."
|
||||
find "$PGO_PROFILES_DIR" -name "*.profraw" -delete
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
show_profile_summary() {
|
||||
if [ ! -d "$PGO_PROFILES_DIR" ]; then
|
||||
print_warning "Profile directory not found: $PGO_PROFILES_DIR"
|
||||
return
|
||||
fi
|
||||
|
||||
print_header "Profile Summary"
|
||||
|
||||
local profdata="${PGO_PROFILES_DIR}/default.profdata"
|
||||
if [ -f "$profdata" ]; then
|
||||
local size
|
||||
size=$(du -h "$profdata" | cut -f1)
|
||||
print_info "Clang profile: $profdata ($size)"
|
||||
if command -v llvm-profdata &>/dev/null; then
|
||||
llvm-profdata show --counts --all-functions "$profdata" 2>/dev/null | tail -5
|
||||
fi
|
||||
fi
|
||||
|
||||
local gcda_count
|
||||
gcda_count=$(find "$PGO_PROFILES_DIR" -name "*.gcda" 2>/dev/null | wc -l)
|
||||
if [ "$gcda_count" -gt 0 ]; then
|
||||
print_info "GCC profile files: $gcda_count .gcda files"
|
||||
local total_size
|
||||
total_size=$(find "$PGO_PROFILES_DIR" -name "*.gcda" -exec du -ch {} + 2>/dev/null | tail -1 | cut -f1)
|
||||
print_info "Total .gcda size: $total_size"
|
||||
fi
|
||||
|
||||
local profraw_count
|
||||
profraw_count=$(find "$PGO_PROFILES_DIR" -name "*.profraw" 2>/dev/null | wc -l)
|
||||
if [ "$profraw_count" -gt 0 ]; then
|
||||
print_warning "$profraw_count unmerged .profraw file(s) found. Run '$0 merge' first."
|
||||
fi
|
||||
|
||||
if [ "$gcda_count" -eq 0 ] && [ ! -f "$profdata" ] && [ "$profraw_count" -eq 0 ]; then
|
||||
print_warning "No profile data found in $PGO_PROFILES_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
STAGE=""
|
||||
JOBS=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo "4")
|
||||
ENABLE_LTO="OFF"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
generate|use|clean|merge|summary)
|
||||
STAGE="$1"
|
||||
shift
|
||||
;;
|
||||
-j)
|
||||
JOBS="$2"
|
||||
shift 2
|
||||
;;
|
||||
-lto)
|
||||
ENABLE_LTO="ON"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
show_usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
print_error "Unknown option: $1"
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$STAGE" ]; then
|
||||
print_error "No stage specified"
|
||||
show_usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Clean stage ---
|
||||
if [ "$STAGE" == "clean" ]; then
|
||||
print_header "Cleaning Build Directory"
|
||||
|
||||
if [ -d "$PGO_PROFILES_DIR" ]; then
|
||||
print_info "Backing up PGO profiles..."
|
||||
mkdir -p "$BACKUP_PROFILES_DIR"
|
||||
cp -r "$PGO_PROFILES_DIR"/* "$BACKUP_PROFILES_DIR/" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if [ -d "$BUILD_DIR" ]; then
|
||||
print_info "Removing build directory..."
|
||||
rm -rf "$BUILD_DIR"
|
||||
fi
|
||||
|
||||
if [ -d "$BACKUP_PROFILES_DIR" ]; then
|
||||
print_info "Restoring PGO profiles..."
|
||||
mkdir -p "$PGO_PROFILES_DIR"
|
||||
mv "$BACKUP_PROFILES_DIR"/* "$PGO_PROFILES_DIR/" 2>/dev/null || true
|
||||
rm -rf "$BACKUP_PROFILES_DIR"
|
||||
fi
|
||||
|
||||
print_info "Clean complete!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# --- Merge stage ---
|
||||
if [ "$STAGE" == "merge" ]; then
|
||||
print_header "Merging PGO Profiles"
|
||||
|
||||
if [ ! -d "$PGO_PROFILES_DIR" ]; then
|
||||
print_error "Profile directory not found: $PGO_PROFILES_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
merge_clang_profiles
|
||||
print_info "Done! Run '$0 summary' to check coverage."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# --- Summary stage ---
|
||||
if [ "$STAGE" == "summary" ]; then
|
||||
show_profile_summary
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# --- Generate stage ---
|
||||
if [ "$STAGE" == "generate" ]; then
|
||||
print_header "PGO Stage 1: Generate Profile Data"
|
||||
|
||||
# Clean stale profile data from any previous run
|
||||
if [ -d "$PGO_PROFILES_DIR" ]; then
|
||||
clean_stale_profiles
|
||||
fi
|
||||
|
||||
# Create build directory
|
||||
mkdir -p "$BUILD_DIR"
|
||||
cd "$BUILD_DIR"
|
||||
|
||||
# Configure
|
||||
print_info "Configuring CMake..."
|
||||
cmake .. \
|
||||
-DCITRON_ENABLE_PGO_GENERATE=ON \
|
||||
-DCITRON_ENABLE_LTO=$ENABLE_LTO \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
# Build
|
||||
print_info "Building instrumented Citron (this may take a while)..."
|
||||
cmake --build . -j"$JOBS"
|
||||
|
||||
print_header "Build Complete!"
|
||||
echo ""
|
||||
echo -e "${YELLOW} Training guide for best PGO results:${NC}"
|
||||
echo ""
|
||||
echo " 1. Run: ./bin/citron"
|
||||
echo " 2. Launch a game and play PAST initial loading"
|
||||
echo " (first-time shader compilation is a critical hot path)"
|
||||
echo " 3. Play for at least 5-10 minutes per game"
|
||||
echo " 4. Test 2-3 different games for broader code coverage"
|
||||
echo " 5. Navigate menus, settings, and game list to profile the UI"
|
||||
echo " 6. Exit citron cleanly (File -> Exit or Ctrl+Q)"
|
||||
echo ""
|
||||
echo -e "${YELLOW} After each session, you can run:${NC}"
|
||||
echo " $0 merge # Consolidate collected profiles"
|
||||
echo " $0 summary # Check profile coverage"
|
||||
echo ""
|
||||
echo -e "${YELLOW} When satisfied with coverage, build the optimized binary:${NC}"
|
||||
echo " $0 use"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# --- Use stage ---
|
||||
if [ "$STAGE" == "use" ]; then
|
||||
print_header "PGO Stage 2: Build Optimized Binary"
|
||||
|
||||
# Check if profile data exists
|
||||
if [ ! -d "$PGO_PROFILES_DIR" ] || [ -z "$(ls -A $PGO_PROFILES_DIR 2>/dev/null)" ]; then
|
||||
print_error "No profile data found in $PGO_PROFILES_DIR"
|
||||
print_info "Please run the generate stage first and collect profile data"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Merge any outstanding raw profiles before building
|
||||
print_info "Merging any outstanding profile data..."
|
||||
merge_clang_profiles || true
|
||||
|
||||
# Backup profiles if build directory exists
|
||||
if [ -d "$BUILD_DIR" ]; then
|
||||
print_info "Backing up PGO profiles..."
|
||||
mkdir -p "$BACKUP_PROFILES_DIR"
|
||||
cp -r "$PGO_PROFILES_DIR"/* "$BACKUP_PROFILES_DIR/"
|
||||
rm -rf "$BUILD_DIR"
|
||||
fi
|
||||
|
||||
# Create build directory and restore profiles
|
||||
mkdir -p "$BUILD_DIR"
|
||||
if [ -d "$BACKUP_PROFILES_DIR" ]; then
|
||||
mkdir -p "$PGO_PROFILES_DIR"
|
||||
mv "$BACKUP_PROFILES_DIR"/* "$PGO_PROFILES_DIR/"
|
||||
rm -rf "$BACKUP_PROFILES_DIR"
|
||||
fi
|
||||
|
||||
cd "$BUILD_DIR"
|
||||
|
||||
# Configure
|
||||
print_info "Configuring CMake..."
|
||||
cmake .. \
|
||||
-DCITRON_ENABLE_PGO_USE=ON \
|
||||
-DCITRON_ENABLE_LTO=$ENABLE_LTO \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
# Build
|
||||
print_info "Building optimized Citron (this may take a while)..."
|
||||
cmake --build . -j"$JOBS"
|
||||
|
||||
print_header "Build Complete!"
|
||||
print_info "Your optimized Citron binary is ready!"
|
||||
print_info "Location: $BUILD_DIR/bin/citron"
|
||||
echo ""
|
||||
print_info "This build is optimized for your specific usage patterns."
|
||||
fi
|
||||
Reference in New Issue
Block a user