diff --git a/AppImageBuilder/assets/citron.desktop b/AppImageBuilder/assets/citron.desktop index 1822831df0..b42cc1962b 100644 --- a/AppImageBuilder/assets/citron.desktop +++ b/AppImageBuilder/assets/citron.desktop @@ -2,5 +2,5 @@ Type=Application Name=citron Icon=citron -Exec=AppRun +Exec=citron Categories=Game; diff --git a/AppImageBuilder/assets/citron.sh b/AppImageBuilder/assets/citron.sh index 93a6057926..56ee226242 100755 --- a/AppImageBuilder/assets/citron.sh +++ b/AppImageBuilder/assets/citron.sh @@ -1,3 +1,24 @@ -#! /bin/sh +#!/bin/sh +# citron AppImage wrapper script -LD_LIBRARY_PATH=/usr/lib/$(uname -m)-linux-gnu:/usr/lib:. exec ./citron "$@" +# Find the directory where the AppImage is located +if [ -n "$APPIMAGE" ]; then + APPIMAGE_DIR="$(dirname "$APPIMAGE")" +else + # Fallback for non-AppImage execution + APPIMAGE_DIR="$(cd "$(dirname "$0")" && pwd)" +fi + +# Set PGO profile output to be next to the AppImage +# This ensures that instrumented builds write their results where the user expects. +if [ -z "$LLVM_PROFILE_FILE" ]; then + export LLVM_PROFILE_FILE="$APPIMAGE_DIR/default-%p.profraw" +fi + +# We don't 'cd' to APPIMAGE_DIR to avoid breaking relative paths for games, +# but Citron will find the 'user' folder if it's in the current working directory +# from which the AppImage was launched. + +# Run the real binary +# $APPDIR is the mount point set by the AppRun loader +exec "$APPDIR/usr/bin/citron.bin" "$@" diff --git a/AppImageBuilder/assets/citron.svg b/AppImageBuilder/assets/citron.svg index 3db81d5d7d..36ee4151d5 100644 --- a/AppImageBuilder/assets/citron.svg +++ b/AppImageBuilder/assets/citron.svg @@ -1,8 +1,58 @@ - - + + + + + - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AppImageBuilder/build-v2.sh b/AppImageBuilder/build-v2.sh new file mode 100755 index 0000000000..056c36d3bd --- /dev/null +++ b/AppImageBuilder/build-v2.sh @@ -0,0 +1,102 @@ +#!/bin/bash +set -e + +# Get script directory +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +BUILD_DIR="${1:-$SCRIPT_DIR/../build/generate}" + +# 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 + +# 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" diff --git a/AppImageBuilder/config.sh.in b/AppImageBuilder/config.sh.in new file mode 100644 index 0000000000..971e02fd51 --- /dev/null +++ b/AppImageBuilder/config.sh.in @@ -0,0 +1,6 @@ +# Build configuration for AppImageBuilder +export CITRON_QT_PATH="@QT_TARGET_PATH@" +export CITRON_ICU_PATH="@ICU_BINARY_DIR@" +export CITRON_XCB_PATH="@XCB_BUILD_ROOT@" +export CITRON_BINARY_DIR="@CMAKE_BINARY_DIR@/bin" +export ARCH="@CMAKE_SYSTEM_PROCESSOR@" diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c91897ec1..43ff27599d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -582,23 +582,23 @@ if (ENABLE_QT AND NOT CITRON_USE_CPM) # Try to find Qt6 installation path (aqtinstall layout: externals/qt//) # We prioritize llvm-mingw_64 for Clang builds to ensure libc++ compatibility. if (EXISTS "${CMAKE_BINARY_DIR}/externals/qt/6.9.3/llvm-mingw_64") - set(QT_TARGET_PATH "${CMAKE_BINARY_DIR}/externals/qt/6.9.3/llvm-mingw_64") + set(QT_TARGET_PATH "${CMAKE_BINARY_DIR}/externals/qt/6.9.3/llvm-mingw_64" CACHE PATH "Path to Qt6" FORCE) elseif (EXISTS "${CMAKE_BINARY_DIR}/externals/qt/6.9.3/mingw_64") - set(QT_TARGET_PATH "${CMAKE_BINARY_DIR}/externals/qt/6.9.3/mingw_64") + set(QT_TARGET_PATH "${CMAKE_BINARY_DIR}/externals/qt/6.9.3/mingw_64" CACHE PATH "Path to Qt6" FORCE) elseif (EXISTS "${CMAKE_BINARY_DIR}/externals/qt/6.9.3/msvc2022_64") - set(QT_TARGET_PATH "${CMAKE_BINARY_DIR}/externals/qt/6.9.3/msvc2022_64") + set(QT_TARGET_PATH "${CMAKE_BINARY_DIR}/externals/qt/6.9.3/msvc2022_64" CACHE PATH "Path to Qt6" FORCE) elseif (EXISTS "${CMAKE_BINARY_DIR}/externals/qt/6.9.3/msvc2022_arm64") - set(QT_TARGET_PATH "${CMAKE_BINARY_DIR}/externals/qt/6.9.3/msvc2022_arm64") + set(QT_TARGET_PATH "${CMAKE_BINARY_DIR}/externals/qt/6.9.3/msvc2022_arm64" CACHE PATH "Path to Qt6" FORCE) elseif (EXISTS "${CMAKE_BINARY_DIR}/externals/qt/6.9.3/msvc2019_64") - set(QT_TARGET_PATH "${CMAKE_BINARY_DIR}/externals/qt/6.9.3/msvc2019_64") + set(QT_TARGET_PATH "${CMAKE_BINARY_DIR}/externals/qt/6.9.3/msvc2019_64" CACHE PATH "Path to Qt6" FORCE) elseif (EXISTS "${CMAKE_BINARY_DIR}/externals/qt/6.8.3/llvm-mingw_64") - set(QT_TARGET_PATH "${CMAKE_BINARY_DIR}/externals/qt/6.8.3/llvm-mingw_64") + set(QT_TARGET_PATH "${CMAKE_BINARY_DIR}/externals/qt/6.8.3/llvm-mingw_64" CACHE PATH "Path to Qt6" FORCE) elseif (EXISTS "${CMAKE_BINARY_DIR}/externals/qt/6.8.3/msvc2022_64") - set(QT_TARGET_PATH "${CMAKE_BINARY_DIR}/externals/qt/6.8.3/msvc2022_64") + set(QT_TARGET_PATH "${CMAKE_BINARY_DIR}/externals/qt/6.8.3/msvc2022_64" CACHE PATH "Path to Qt6" FORCE) elseif (EXISTS "${CMAKE_BINARY_DIR}/externals/qt/6.8.3/msvc2019_64") - set(QT_TARGET_PATH "${CMAKE_BINARY_DIR}/externals/qt/6.8.3/msvc2019_64") + set(QT_TARGET_PATH "${CMAKE_BINARY_DIR}/externals/qt/6.8.3/msvc2019_64" CACHE PATH "Path to Qt6" FORCE) elseif (EXISTS "${CMAKE_BINARY_DIR}/externals/qt/6.7.3/msvc2019_64") - set(QT_TARGET_PATH "${CMAKE_BINARY_DIR}/externals/qt/6.7.3/msvc2019_64") + set(QT_TARGET_PATH "${CMAKE_BINARY_DIR}/externals/qt/6.7.3/msvc2019_64" CACHE PATH "Path to Qt6" FORCE) endif() endif() @@ -626,18 +626,12 @@ if (ENABLE_QT AND NOT CITRON_USE_CPM) endif() # Fallback detection if QT_TARGET_PATH was not found in common bundled locations - if (NOT QT_TARGET_PATH) - get_target_property(qtcore_path Qt6::Core LOCATION_Release) - if (qtcore_path) - string(FIND "${qtcore_path}" "/bin/" qtcore_path_bin_pos REVERSE) - string(FIND "${qtcore_path}" "/lib/" qtcore_path_lib_pos REVERSE) - if (qtcore_path_bin_pos GREATER qtcore_path_lib_pos) - string(SUBSTRING "${qtcore_path}" 0 ${qtcore_path_bin_pos} QT_TARGET_PATH) - else() - string(SUBSTRING "${qtcore_path}" 0 ${qtcore_path_lib_pos} QT_TARGET_PATH) - endif() - endif() + if (NOT QT_TARGET_PATH AND Qt6_DIR) + get_filename_component(_tmp_path "${Qt6_DIR}/../../.." ABSOLUTE) + set(QT_TARGET_PATH "${_tmp_path}" CACHE PATH "Path to Qt6" FORCE) + message(STATUS "Resolved QT_TARGET_PATH from Qt6_DIR: ${QT_TARGET_PATH}") endif() + message(STATUS "Final QT_TARGET_PATH: ${QT_TARGET_PATH}") endif() function(set_citron_qt_components) @@ -974,3 +968,11 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CITRON_ENABLE_LTO) endif() endforeach() endif() +# Generate AppImage build configuration +if (UNIX AND NOT APPLE) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/AppImageBuilder/config.sh.in" + "${CMAKE_CURRENT_BINARY_DIR}/AppImageBuilder/config.sh" + @ONLY + ) +endif() diff --git a/CMakeModules/FixLinuxBundleRpaths.cmake b/CMakeModules/FixLinuxBundleRpaths.cmake deleted file mode 100644 index ca87a917a6..0000000000 --- a/CMakeModules/FixLinuxBundleRpaths.cmake +++ /dev/null @@ -1,152 +0,0 @@ -# SPDX-FileCopyrightText: 2026 citron Emulator Project -# SPDX-License-Identifier: GPL-3.0-or-later - -if (NOT DEFINED BUNDLE_BIN_DIR) - message(FATAL_ERROR "BUNDLE_BIN_DIR is required") -endif() -if (NOT DEFINED PATCHELF_EXE OR NOT EXISTS "${PATCHELF_EXE}") - message(FATAL_ERROR "patchelf is required to make the Linux bundle portable") -endif() - -set(_bin_dir "${BUNDLE_BIN_DIR}") -set(_lib_dir "${_bin_dir}/lib") -set(_plugins_dir "${_bin_dir}/plugins") -file(MAKE_DIRECTORY "${_lib_dir}") - -if (DEFINED RUNTIME_DEPENDENCY_DIRS) - string(REPLACE "|" ";" _runtime_dependency_dirs "${RUNTIME_DEPENDENCY_DIRS}") -endif() -list(APPEND _runtime_dependency_dirs "${_lib_dir}") -list(REMOVE_DUPLICATES _runtime_dependency_dirs) - -foreach(_candidate citron citron-cmd citron-room shader_tool) - if (EXISTS "${_bin_dir}/${_candidate}") - list(APPEND _executables "${_bin_dir}/${_candidate}") - endif() -endforeach() - -file(GLOB _bundled_libraries "${_lib_dir}/*.so" "${_lib_dir}/*.so.*") -if (EXISTS "${_plugins_dir}") - file(GLOB_RECURSE _bundled_modules "${_plugins_dir}/*.so") -endif() - -if (_executables OR _bundled_libraries OR _bundled_modules) - file(GET_RUNTIME_DEPENDENCIES - RESOLVED_DEPENDENCIES_VAR _resolved_dependencies - UNRESOLVED_DEPENDENCIES_VAR _unresolved_dependencies - CONFLICTING_DEPENDENCIES_PREFIX _conflicting - EXECUTABLES ${_executables} - LIBRARIES ${_bundled_libraries} - MODULES ${_bundled_modules} - DIRECTORIES ${_runtime_dependency_dirs} - PRE_EXCLUDE_REGEXES - [[^libgcc_s\.so\..*]] - [[^libstdc\+\+\.so\..*]] - [[^libglapi\.so\..*]] - [[^libGL\.so\..*]] - [[^libEGL\.so\..*]] - [[^libvulkan\.so\..*]] - [[^libXau\.so\..*]] - [[^libXdmcp\.so\..*]] - [[^libxcb\.so\..*]] - POST_EXCLUDE_REGEXES - [[/ld-linux[^/]*\.so(\..*)?$]] - [[/libc\.so(\..*)?$]] - [[/libm\.so(\..*)?$]] - [[/libdl\.so(\..*)?$]] - [[/libpthread\.so(\..*)?$]] - [[/librt\.so(\..*)?$]] - [[/libresolv\.so(\..*)?$]] - [[/libutil\.so(\..*)?$]] - [[/libnss_[^/]*\.so(\..*)?$]] - [[/libanl\.so(\..*)?$]] - [[/libgcc_s\.so(\..*)?$]] - [[/libstdc\+\+\.so(\..*)?$]] - ) -endif() - -foreach(_dependency ${_resolved_dependencies}) - get_filename_component(_dependency_name "${_dependency}" NAME) - if (NOT EXISTS "${_lib_dir}/${_dependency_name}") - file(COPY "${_dependency}" DESTINATION "${_lib_dir}" FOLLOW_SYMLINK_CHAIN) - endif() -endforeach() - -if (_unresolved_dependencies) - list(REMOVE_DUPLICATES _unresolved_dependencies) - message(WARNING "Unresolved Linux bundle dependencies: ${_unresolved_dependencies}") -endif() - -function(_citron_is_elf output_var file_path) - execute_process( - COMMAND readelf -h "${file_path}" - RESULT_VARIABLE _readelf_result - OUTPUT_QUIET - ERROR_QUIET - ) - if (_readelf_result EQUAL 0) - set(${output_var} TRUE PARENT_SCOPE) - else() - set(${output_var} FALSE PARENT_SCOPE) - endif() -endfunction() - -function(_citron_set_rpath file_path rpath) - _citron_is_elf(_is_elf "${file_path}") - if (NOT _is_elf) - return() - endif() - - execute_process( - COMMAND "${PATCHELF_EXE}" --force-rpath --set-rpath "${rpath}" "${file_path}" - RESULT_VARIABLE _patchelf_result - OUTPUT_VARIABLE _patchelf_stdout - ERROR_VARIABLE _patchelf_stderr - ) - if (NOT _patchelf_result EQUAL 0) - message(FATAL_ERROR - "Failed to set RPATH on ${file_path}\n" - "${_patchelf_stdout}\n${_patchelf_stderr}") - endif() -endfunction() - -foreach(_executable ${_executables}) - _citron_set_rpath("${_executable}" "$ORIGIN/lib") -endforeach() - -file(GLOB _final_libraries "${_lib_dir}/*.so" "${_lib_dir}/*.so.*") -foreach(_library ${_final_libraries}) - _citron_set_rpath("${_library}" "$ORIGIN") -endforeach() - -if (EXISTS "${_plugins_dir}") - file(GLOB_RECURSE _final_modules "${_plugins_dir}/*.so") - foreach(_module ${_final_modules}) - _citron_set_rpath("${_module}" "$ORIGIN/../../lib") - endforeach() -endif() - -set(_rpath_failures) -foreach(_file ${_executables} ${_final_libraries} ${_final_modules}) - _citron_is_elf(_is_elf "${_file}") - if (NOT _is_elf) - continue() - endif() - - execute_process( - COMMAND readelf -d "${_file}" - OUTPUT_VARIABLE _dynamic_section - ERROR_QUIET - ) - if (_dynamic_section MATCHES "(RPATH|RUNPATH)[^\n]*\\[/" OR - _dynamic_section MATCHES "(RPATH|RUNPATH)[^\n]*:/") - list(APPEND _rpath_failures "${_file}") - endif() -endforeach() - -if (_rpath_failures) - list(REMOVE_DUPLICATES _rpath_failures) - message(FATAL_ERROR "Absolute RPATH/RUNPATH remains in Linux bundle: ${_rpath_failures}") -endif() - -message(STATUS "Linux bundle dependencies copied and RPATHs normalized in ${_bin_dir}") diff --git a/CMakeModules/dependencies.cmake b/CMakeModules/dependencies.cmake index c4a94f1e32..27b286b427 100644 --- a/CMakeModules/dependencies.cmake +++ b/CMakeModules/dependencies.cmake @@ -591,6 +591,7 @@ set(CITRON_ICU_TAG "release-73-2" CACHE STRING "ICU git tag/version") set(CITRON_XCB_MACROS_VER "1.20.2" CACHE STRING "XCB util-macros version") set(CITRON_XCB_PROTO_VER "1.17.0" CACHE STRING "XCB proto version") set(CITRON_XCB_XAU_VER "1.0.11" CACHE STRING "XCB libXau version") +set(CITRON_XCB_XDMCP_VER "1.1.5" CACHE STRING "XCB libXdmcp version") set(CITRON_XCB_LIBXCB_VER "1.16" CACHE STRING "XCB libxcb version") set(CITRON_XCB_UTIL_VER "0.4.1" CACHE STRING "XCB util version") set(CITRON_XCB_CURSOR_VER "0.1.6" CACHE STRING "XCB cursor version") diff --git a/CMakeModules/qt_download.cmake b/CMakeModules/qt_download.cmake index d072f88f41..81e4fae1bd 100644 --- a/CMakeModules/qt_download.cmake +++ b/CMakeModules/qt_download.cmake @@ -70,6 +70,10 @@ endif() # ── Download target Qt ──────────────────────────────────────────────────────── if (Qt6_DIR AND EXISTS "${Qt6_DIR}/Qt6Config.cmake") message(STATUS "[Qt] Using target Qt from Qt6_DIR: ${Qt6_DIR}") + if (NOT QT_TARGET_PATH) + get_filename_component(_tmp_path "${Qt6_DIR}/../../.." ABSOLUTE) + set(QT_TARGET_PATH "${_tmp_path}" CACHE PATH "Path to Qt6 target root" FORCE) + endif() else() set(_QT_TARGET_DIR "${CITRON_QT_BASE_DIR}/${CITRON_QT_VERSION}/${_QT_DIR_NAME}") set(_QT_TARGET_CMAKE "${_QT_TARGET_DIR}/${_QT_CMAKE_SUB}/Qt6Config.cmake") @@ -118,7 +122,9 @@ else() if (EXISTS "${_QT_TARGET_CMAKE}") get_filename_component(_qt6_dir "${_QT_TARGET_CMAKE}" DIRECTORY) set(Qt6_DIR "${_qt6_dir}" CACHE PATH "Path to Qt6Config.cmake (from aqt)" FORCE) + set(QT_TARGET_PATH "${_QT_TARGET_DIR}" CACHE PATH "Path to Qt6 target root" FORCE) message(STATUS "[Qt] Qt6_DIR = ${Qt6_DIR}") + message(STATUS "[Qt] QT_TARGET_PATH = ${QT_TARGET_PATH}") endif() endif() diff --git a/CMakeModules/xcb_build.cmake b/CMakeModules/xcb_build.cmake index c9d667f942..26099e1356 100644 --- a/CMakeModules/xcb_build.cmake +++ b/CMakeModules/xcb_build.cmake @@ -10,7 +10,7 @@ endif() # We need a list of components to fetch and build. # 'macros' and 'proto' must come first as others depend on them. -set(_xcb_components macros proto xau libxcb util image keysyms renderutil wm cursor) +set(_xcb_components macros proto xau xdmcp libxcb util image keysyms renderutil wm cursor) set(XCB_BUILD_ROOT "${CMAKE_BINARY_DIR}/externals/xcb-build") file(MAKE_DIRECTORY "${XCB_BUILD_ROOT}") @@ -21,6 +21,7 @@ set(XCB_BINARY_DIR "${XCB_BUILD_ROOT}/lib" CACHE INTERNAL "Location of CPM-built set(_xcb_macros_ver "${CITRON_XCB_MACROS_VER}") set(_xcb_proto_ver "${CITRON_XCB_PROTO_VER}") set(_xcb_xau_ver "${CITRON_XCB_XAU_VER}") +set(_xcb_xdmcp_ver "${CITRON_XCB_XDMCP_VER}") set(_xcb_libxcb_ver "${CITRON_XCB_LIBXCB_VER}") set(_xcb_cursor_ver "${CITRON_XCB_CURSOR_VER}") set(_xcb_util_ver "${CITRON_XCB_UTIL_VER}") @@ -38,6 +39,9 @@ foreach(_comp ${_xcb_components}) elseif(_comp STREQUAL "xau") set(_url "https://xorg.freedesktop.org/archive/individual/lib/libXau-${_xcb_xau_ver}.tar.xz") set(_name "libXau") + elseif(_comp STREQUAL "xdmcp") + set(_url "https://xorg.freedesktop.org/archive/individual/lib/libXdmcp-${_xcb_xdmcp_ver}.tar.xz") + set(_name "libXdmcp") elseif(_comp STREQUAL "libxcb") set(_url "https://xcb.freedesktop.org/dist/libxcb-${_xcb_libxcb_ver}.tar.xz") set(_name "libxcb") diff --git a/build-citron-linux.sh b/build-citron-linux.sh index 835348b0c2..0a50ff0a70 100755 --- a/build-citron-linux.sh +++ b/build-citron-linux.sh @@ -26,7 +26,7 @@ # - The LTO linker backend receives -fprofile-use for LTCG with profile data # - Profile runtime symbols are force-kept on the linker command line # -# LTO + PGO LINKER FLAGS (use/csgenerate/bolt/propeller stages): +# LTO + PGO LINKER FLAGS (use/csgenerate/bolt stages): # For full LTO (-flto), the -fprofile-use flag must appear on # CMAKE_EXE_LINKER_FLAGS as well as the compile flags, so the LTO backend # applies the profile during link-time code generation. @@ -47,8 +47,7 @@ # merge Merge collected .profraw files into .profdata. # summary Show profile statistics. # use Stage 2: PGO + LTO optimized build (or baseline if --pgo none). -# bolt Stage 3A: BOLT binary layout optimization. -# propeller Stage 3B: Propeller BB+function layout (requires perf + LBR). +# bolt Stage 3: BOLT binary layout optimization. # clean Remove build directories (profile data preserved). # # USAGE: @@ -107,12 +106,6 @@ # # BOLT pauses — run instrumented binary, exit, press Enter. # # Binary: ./build/bolt/citron # -# IR PGO + Propeller (requires perf + LBR-capable CPU): -# (after completing 'use' above) -# ./build-citron-linux.sh propeller --pgo ir --lto full -# # Propeller pauses — run perf command shown, exit, press Enter. -# # Binary: ./build/propeller/bin/citron -# # DEPENDENCIES MANAGED BY CMAKE (not this script): # All C++ library dependencies are fetched from source by CPM at cmake # configure time. No system library packages are required. @@ -130,7 +123,6 @@ # autoconf + automake + make FFmpeg autotools build # glslang (glslc) Vulkan shader compilation # patchelf bundle RPATH normalization -# perf propeller stage only # ============================================================================= set -euo pipefail @@ -167,10 +159,8 @@ _set_derived_paths() { BUILD_CSGENERATE="${BUILD_ROOT}/cs-generate" BUILD_USE="${BUILD_ROOT}/use" BUILD_BOLT="${BUILD_ROOT}/bolt" - BUILD_PROPELLER="${BUILD_ROOT}/propeller" PROFILE_DIR="${BUILD_ROOT}/pgo-profiles" BOLT_PROFILE_DIR="${BUILD_ROOT}/bolt-profiles" - PROPELLER_PROFILE_DIR="${BUILD_ROOT}/propeller-profiles" } _set_derived_paths @@ -191,14 +181,14 @@ _set_clang_tools RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m' -info() { echo -e "${CYAN}[INFO]${RESET} $*"; } -success() { echo -e "${GREEN}[OK]${RESET} $*"; } -warn() { echo -e "${YELLOW}[WARN]${RESET} $*"; } +info() { echo -e "${CYAN}[INFO]${RESET} $*" >&2; } +success() { echo -e "${GREEN}[OK]${RESET} $*" >&2; } +warn() { echo -e "${YELLOW}[WARN]${RESET} $*" >&2; } error() { echo -e "${RED}[ERROR]${RESET} $*" >&2; exit 1; } header() { - echo -e "\n${BOLD}${GREEN}=================================================================${RESET}" - echo -e "${BOLD}${GREEN} $*${RESET}" - echo -e "${BOLD}${GREEN}=================================================================${RESET}" + echo -e "\n${BOLD}${GREEN}=================================================================${RESET}" >&2 + echo -e "${BOLD}${GREEN} $*${RESET}" >&2 + echo -e "${BOLD}${GREEN}=================================================================${RESET}" >&2 } # ============================================================================= @@ -590,6 +580,31 @@ normalize_profraw_dirs() { done < <(find "${base_dir}" -maxdepth 1 -type d -name '*.profraw' -print0) } +_collect_appimage_profiles() { + # Profiles generated by running the AppImage are written next to the AppImage file + # by the citron.sh wrapper. We collect them back to the main profile directory + # so they can be merged and used by the build script. + + # Standard PGO profiles + if [[ -d "${BUILD_GENERATE}/AppImage" ]]; then + local count; count="$(find "${BUILD_GENERATE}/AppImage" -maxdepth 1 -name "*.profraw" 2>/dev/null | wc -l)" + if [[ "${count}" -gt 0 ]]; then + info "Collecting ${count} profile(s) from ${BUILD_GENERATE}/AppImage..." + mv "${BUILD_GENERATE}/AppImage"/*.profraw "${PROFILE_DIR}/" 2>/dev/null || true + fi + fi + + # CS-PGO profiles + if [[ -d "${BUILD_CSGENERATE}/AppImage" ]]; then + local count; count="$(find "${BUILD_CSGENERATE}/AppImage" -maxdepth 1 -name "*.profraw" 2>/dev/null | wc -l)" + if [[ "${count}" -gt 0 ]]; then + info "Collecting ${count} CS profile(s) from ${BUILD_CSGENERATE}/AppImage..." + mkdir -p "${PROFILE_DIR}/cs" + mv "${BUILD_CSGENERATE}/AppImage"/*.profraw "${PROFILE_DIR}/cs/" 2>/dev/null || true + fi + fi +} + _merge_profraw_to_profdata() { local profile_dir="$1"; local output_file="$2" normalize_profraw_dirs "${profile_dir}" @@ -604,6 +619,7 @@ _merge_profraw_to_profdata() { } resolve_use_profdata() { + _collect_appimage_profiles # Determine which profdata to hand to the use stage. # Preference order: merged.profdata (stage1+CS) > default.profdata (stage1) > merge from profraw. local merged="${PROFILE_DIR}/merged.profdata" @@ -783,6 +799,33 @@ _build_with_flags() { cmake --build . --config "${BUILD_TYPE}" -j "${JOBS}" } +# ============================================================================= +# AppImage building +# ============================================================================= + +build_appimage_stage() { + local build_dir="$1" + local stage_name="$2" + local binary_override="${3:-}" + header "Building AppImage for ${stage_name}" + + if [[ -n "${binary_override}" ]]; then + # Override CITRON_BINARY_DIR so build-v2.sh finds the correct binary + export CITRON_BINARY_DIR="$(dirname "${binary_override}")" + else + unset CITRON_BINARY_DIR + fi + + # Run the AppImage build script + bash "${SCRIPT_DIR}/AppImageBuilder/build-v2.sh" "${build_dir}" + + # Create the output directory and move the AppImage + mkdir -p "${build_dir}/AppImage/user" + mv "${build_dir}/citron-x86_64.AppImage" "${build_dir}/AppImage/" + + success "AppImage for ${stage_name} placed in ${build_dir}/AppImage/citron-x86_64.AppImage" +} + # ============================================================================= # Stage: generate # ============================================================================= @@ -829,6 +872,7 @@ stage_generate() { write_gen_sentinel mkdir -p "${BUILD_GENERATE}/bin/user" success "Instrumented build: ${BUILD_GENERATE}/bin/citron" + build_appimage_stage "${BUILD_GENERATE}" "generate" print_profiling_instructions "${BUILD_GENERATE}/bin/citron" } @@ -902,6 +946,7 @@ stage_csgenerate() { mkdir -p "${BUILD_CSGENERATE}/bin/user" success "CS-IRPGO instrumented build: ${BUILD_CSGENERATE}/bin/citron" + build_appimage_stage "${BUILD_CSGENERATE}" "csgenerate" echo "" echo -e "${YELLOW}════════════════════════════════════════════════════════════════${RESET}" echo -e "${YELLOW} NEXT STEP: Collect CS Profile Data (Session 2)${RESET}" @@ -925,6 +970,7 @@ stage_csgenerate() { stage_merge() { header "Merging PGO Profiles" + _collect_appimage_profiles [[ -d "${PROFILE_DIR}" ]] || error "Profile directory not found: ${PROFILE_DIR}" _merge_profraw_to_profdata "${PROFILE_DIR}" "${PROFILE_DIR}/default.profdata" find "${PROFILE_DIR}" -maxdepth 1 -name "*.profraw" -delete @@ -972,6 +1018,7 @@ stage_use() { _build_with_flags "${BUILD_ROOT}/use-nopgo" nopgo mkdir -p "${BUILD_ROOT}/use-nopgo/bin/user" success "Baseline build: ${BUILD_ROOT}/use-nopgo/bin/citron" + build_appimage_stage "${BUILD_ROOT}/use-nopgo" "use-nopgo" return 0 fi @@ -1017,10 +1064,10 @@ stage_use() { success " PGO: ${pgo_label}" success " LTO: ${LTO_MODE}${LTO_MODE:+ ($(lto_clang_flag))}" success "════════════════════════════════════════════════════════════════" + build_appimage_stage "${BUILD_USE}" "use" echo "" info "Next steps (optional):" echo " BOLT: ./build-citron-linux.sh bolt --pgo ${PGO_MODE} --lto ${LTO_MODE}" - echo " Propeller: ./build-citron-linux.sh propeller --pgo ${PGO_MODE} --lto ${LTO_MODE}" if [[ "${profdata}" != "${PROFILE_DIR}/merged.profdata" && "${PGO_MODE}" == "ir" ]]; then echo "" echo " CS-IRPGO (add a second profiling layer):" @@ -1137,20 +1184,32 @@ stage_bolt() { local fdata_pattern="${BOLT_PROFILE_DIR}/citron-%p.fdata" local merged_fdata="${BOLT_PROFILE_DIR}/citron-merged.fdata" + local skipped_funcs="RC4_options" + info "Instrumenting ELF with BOLT..." "${LLVM_BOLT}" "${elf_binary}" \ --instrument \ + --skip-funcs="${skipped_funcs}" \ --instrumentation-file="${fdata_pattern}" \ --instrumentation-file-append-pid \ -o "${instrumented}" success "Instrumented: ${instrumented}" + # Load custom library paths from config.sh for the run instructions + local lp_env="" + local config_file="${relocs_dir}/AppImageBuilder/config.sh" + if [[ -f "${config_file}" ]]; then + # Use a subshell to avoid polluting our current environment + lp_env="$( (source "${config_file}"; echo "LD_LIBRARY_PATH=\"${CITRON_QT_PATH}/lib:${CITRON_ICU_PATH}:${CITRON_XCB_PATH}/lib\" ") )" + fi + echo "" echo -e "${YELLOW}════════════════════════════════════════════════════════════════${RESET}" echo -e "${YELLOW} Run the BOLT-instrumented binary${RESET}" echo -e "${YELLOW}════════════════════════════════════════════════════════════════${RESET}" echo "" - echo " ${instrumented}" + echo " Run the instrumented binary with custom library paths:" + echo " ${lp_env}${instrumented}" echo "" echo " Play for 15-30 min. Exit cleanly." echo " fdata files will appear in: ${BOLT_PROFILE_DIR}/" @@ -1170,6 +1229,7 @@ stage_bolt() { info "Optimizing ELF with BOLT..." "${LLVM_BOLT}" "${elf_binary}" \ -p "${merged_fdata}" \ + --skip-funcs="${skipped_funcs}" \ --reorder-blocks=ext-tsp \ --reorder-functions=cdsort \ --split-functions \ @@ -1186,178 +1246,10 @@ stage_bolt() { success " Binary: ${BUILD_BOLT}/bin/citron" success " Optimizations: PGO + LTO + BOLT basic-block reordering" success "════════════════════════════════════════════════════════════════" + build_appimage_stage "${relocs_dir}" "bolt" "${BUILD_BOLT}/bin/citron" echo "" } -# ============================================================================= -# Propeller helpers -# ============================================================================= - -_ensure_create_llvm_prof() { - local ver_sentinel="/usr/local/bin/.create_llvm_prof_ver" - local clang_ver; clang_ver="$("${CLANG}" --version 2>&1 | head -1 || echo unknown)" - - if command -v create_llvm_prof &>/dev/null; then - local stored=""; [[ -f "${ver_sentinel}" ]] && stored="$(cat "${ver_sentinel}")" - if [[ "${clang_ver}" == "${stored}" ]]; then - success "create_llvm_prof available"; return 0 - fi - warn "create_llvm_prof version mismatch — rebuilding." - sudo rm -f /usr/local/bin/create_llvm_prof "${ver_sentinel}" - fi - - header "Building create_llvm_prof (google/llvm-propeller)" - local pkg_mgr; pkg_mgr="$(detect_pkg_manager)" - case "${pkg_mgr}" in - apt) sudo apt-get install -y libelf-dev libssl-dev libzstd-dev 2>/dev/null || true ;; - pacman) sudo pacman -S --needed --noconfirm elfutils openssl zstd 2>/dev/null || true ;; - dnf) sudo dnf install -y elfutils-libelf-devel openssl-devel libzstd-devel 2>/dev/null || true ;; - zypper) sudo zypper install -y libelf-devel libopenssl-devel libzstd-devel 2>/dev/null || true ;; - esac - - local src="/tmp/propeller-src" bld="/tmp/propeller-build" - [[ -d "${src}/.git" ]] \ - || git clone --depth=1 https://github.com/google/llvm-propeller.git "${src}" - rm -rf "${bld}" - CC="${CLANG}" CXX="${CLANGPP}" \ - cmake -S "${src}" -B "${bld}" -G Ninja -DCMAKE_BUILD_TYPE=Release \ - || error "llvm-propeller cmake configure failed." - cmake --build "${bld}" --target generate_propeller_profiles -j "${JOBS}" \ - || error "llvm-propeller build failed." - sudo cp "${bld}/propeller/generate_propeller_profiles" /usr/local/bin/create_llvm_prof - sudo chmod +x /usr/local/bin/create_llvm_prof - printf '%s' "${clang_ver}" | sudo tee "${ver_sentinel}" > /dev/null - success "create_llvm_prof installed." -} - -# ============================================================================= -# Stage: propeller -# ============================================================================= - -stage_propeller() { - header "Stage 3B: Propeller BB+Function Layout Optimization" - command -v perf &>/dev/null \ - || error "perf not found. Install linux-tools-$(uname -r) and re-run." - _ensure_create_llvm_prof - - # Build a binary with -fbasic-block-address-map for perf profiling. - # LTO is intentionally OFF: the ThinLTO backend in lld does not propagate - # -fbasic-block-address-map through the link step, so .llvm_bb_addr_map - # sections would be absent in the final binary. PGO data alone provides - # sufficient representative coverage for the Propeller conversion step. - local bb_dir="${BUILD_ROOT}/use-bb" - if [[ ! -f "${bb_dir}/bin/citron" ]]; then - info "Building BB-annotated binary (-fbasic-block-address-map, no LTO)..." - apply_source_patches - local bt_upper; bt_upper="$(echo "${BUILD_TYPE}" | tr '[:lower:]' '[:upper:]')" - local debug_flag=""; [[ "${BUILD_TYPE}" == "RelWithDebInfo" ]] && debug_flag="-g" - local pgo_part="" pgo_enable="OFF" - if [[ "${PGO_MODE}" != "none" ]]; then - check_stage_compatibility - local profdata; profdata="$(resolve_use_profdata)" - pgo_part="$(pgo_use_compile_flag "${profdata}")" - pgo_enable="ON" - fi - local bb_cflags="-O3 -DNDEBUG ${debug_flag} -USuccess -UNone ${ARCH_FLAGS:-} -Wno-error -w ${pgo_part} -fbasic-block-address-map" - local bb_linker="-fuse-ld=${LLD} -Wl,--emit-relocs" - _build_with_flags "${bb_dir}" use \ - "-DCITRON_ENABLE_PGO_USE=${pgo_enable}" \ - "-DCITRON_ENABLE_PGO_GENERATE=OFF" \ - "-DCITRON_PGO_FLAGS_MANAGED_BY_SCRIPT=ON" \ - "-DCITRON_ENABLE_LTO=OFF" \ - "-DCITRON_PGO_PROFILE_DIR=${PROFILE_DIR}" \ - "-DCMAKE_C_FLAGS_${bt_upper}=${bb_cflags}" \ - "-DCMAKE_CXX_FLAGS_${bt_upper}=${bb_cflags}" \ - "-DCMAKE_EXE_LINKER_FLAGS_${bt_upper}=${bb_linker}" - success "BB-annotated binary: ${bb_dir}/bin/citron" - fi - - local elf_binary="${bb_dir}/bin/citron" - mkdir -p "${PROPELLER_PROFILE_DIR}" "${BUILD_PROPELLER}/bin" - local perf_data="${PROPELLER_PROFILE_DIR}/perf.data" - local cc_profile="${PROPELLER_PROFILE_DIR}/propeller_cc.prof" - local symorder="${PROPELLER_PROFILE_DIR}/propeller_symorder.txt" - - # Ensure perf can record branch stacks - local paranoid; paranoid="$(cat /proc/sys/kernel/perf_event_paranoid 2>/dev/null || echo 1)" - if [[ "${paranoid}" -gt 1 ]]; then - warn "perf_event_paranoid=${paranoid} — branch stacks require <= 1. Setting now..." - sudo sysctl -w kernel.perf_event_paranoid=1 \ - || error "Could not set perf_event_paranoid. Run: sudo sysctl kernel.perf_event_paranoid=1" - fi - - if [[ ! -f "${perf_data}" ]]; then - echo "" - echo -e "${YELLOW}═══════════════════════════════════════════════════════════════${RESET}" - echo -e "${YELLOW} Propeller — Branch Profile Collection${RESET}" - echo -e "${YELLOW}═══════════════════════════════════════════════════════════════${RESET}" - echo "" - echo " Run the following to collect a branch-stack profile:" - echo "" - echo " perf record -b -e cycles:u \\" - echo " -o ${perf_data} \\" - echo " -- ${elf_binary}" - echo "" - echo " Play for 15-30 minutes. Exit citron cleanly." - echo "" - echo -e "${YELLOW}═══════════════════════════════════════════════════════════════${RESET}" - read -rp " Press Enter once perf has finished and perf.data is written... " - echo "" - [[ -f "${perf_data}" ]] \ - || error "perf.data not found at ${perf_data}. Run the perf command above first." - fi - - info "Converting perf branch data to Propeller profiles..." - create_llvm_prof \ - --binary="${elf_binary}" \ - --profile="${perf_data}" \ - --cc_profile="${cc_profile}" \ - --ld_profile="${symorder}" \ - 2>&1 || error "generate_propeller_profiles failed — check perf.data and ELF compatibility." - - [[ -f "${cc_profile}" ]] && success "CC profile: ${cc_profile} ($(wc -l < "${cc_profile}") entries)" - [[ -f "${symorder}" ]] && success "LD profile: ${symorder} ($(wc -l < "${symorder}") functions)" - - # Final rebuild: PGO + LTO + Propeller - info "Rebuilding optimized binary with Propeller profiles (PGO + LTO + Propeller)..." - apply_source_patches - local bt_upper; bt_upper="$(echo "${BUILD_TYPE}" | tr '[:lower:]' '[:upper:]')" - local debug_flag=""; [[ "${BUILD_TYPE}" == "RelWithDebInfo" ]] && debug_flag="-g" - local lto_flag; lto_flag="$(lto_clang_flag)" - local pgo_part="" pgo_enable="OFF" - if [[ "${PGO_MODE}" != "none" ]]; then - check_stage_compatibility - local profdata; profdata="$(resolve_use_profdata)" - pgo_part="$(pgo_use_compile_flag "${profdata}")" - pgo_enable="ON" - fi - - local prop_cflags="-O3 -DNDEBUG ${debug_flag} -USuccess -UNone ${ARCH_FLAGS:-} -Wno-error -w ${lto_flag:+${lto_flag} }${pgo_part}" - [[ -f "${cc_profile}" ]] && prop_cflags="${prop_cflags} -fbasic-block-sections=list=${cc_profile}" - - # -fprofile-use on the linker so LTO backend gets profile guidance - local prop_linker="-fuse-ld=${LLD} ${debug_flag:+${debug_flag} }${lto_flag:+${lto_flag} }${pgo_part}" - [[ -f "${symorder}" ]] && prop_linker="${prop_linker} -Wl,--symbol-ordering-file=${symorder}" - - _build_with_flags "${BUILD_PROPELLER}" use \ - "-DCITRON_ENABLE_PGO_USE=${pgo_enable}" \ - "-DCITRON_ENABLE_PGO_GENERATE=OFF" \ - "-DCITRON_PGO_FLAGS_MANAGED_BY_SCRIPT=ON" \ - "-DCITRON_ENABLE_LTO=$(lto_cmake_flag)" \ - "-DCITRON_PGO_PROFILE_DIR=${PROFILE_DIR}" \ - "-DCMAKE_C_FLAGS_${bt_upper}=${prop_cflags}" \ - "-DCMAKE_CXX_FLAGS_${bt_upper}=${prop_cflags}" \ - "-DCMAKE_EXE_LINKER_FLAGS_${bt_upper}=${prop_linker}" - - echo "" - success "════════════════════════════════════════════════════════════════" - success " Stage propeller complete" - mkdir -p "${BUILD_PROPELLER}/bin/user" - success " Binary: ${BUILD_PROPELLER}/bin/citron" - success " Optimizations: PGO + LTO + Propeller BB+function layout" - success "════════════════════════════════════════════════════════════════" - echo "" -} # ============================================================================= # Stage: clean @@ -1385,7 +1277,7 @@ STAGE="" while [[ $# -gt 0 ]]; do case "$1" in - setup|generate|csgenerate|merge|summary|use|bolt|propeller|clean) + setup|generate|csgenerate|merge|summary|use|bolt|clean) STAGE="$1"; shift ;; --build) BUILD_ROOT="$2"; _set_derived_paths; shift 2 ;; @@ -1421,7 +1313,7 @@ while [[ $# -gt 0 ]]; do done [[ -n "${STAGE}" ]] \ - || error "No stage specified.\nUsage: $0 [options]\nStages: setup generate csgenerate merge summary use bolt propeller clean" + || error "No stage specified.\nUsage: $0 [options]\nStages: setup generate csgenerate merge summary use bolt clean" resolve_arch_flags @@ -1433,6 +1325,5 @@ case "${STAGE}" in summary) stage_summary ;; use) stage_use ;; bolt) stage_bolt ;; - propeller) stage_propeller ;; clean) stage_clean ;; esac diff --git a/src/citron/CMakeLists.txt b/src/citron/CMakeLists.txt index f1c91db39a..569365e181 100644 --- a/src/citron/CMakeLists.txt +++ b/src/citron/CMakeLists.txt @@ -11,18 +11,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) set_property(GLOBAL PROPERTY JOB_POOLS link_pool=1) set(CMAKE_JOB_POOL_LINK link_pool) -# Set the RPATH for Qt Libraries -# This must be done before the `citron` target is created -if (CITRON_USE_BUNDLED_QT AND (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")) - # Use relative RPATH so the binary finds libraries in the 'lib' subfolder. - # $ORIGIN refers to the directory containing the executable. - # We use --disable-new-dtags to force RPATH instead of RUNPATH, - # ensuring recursive dependency resolution for bundled libraries. - set(CMAKE_BUILD_RPATH "$ORIGIN/lib/") - set(CMAKE_INSTALL_RPATH "$ORIGIN/lib/") - add_link_options("-Wl,--disable-new-dtags") -endif() - +# citron target creation add_executable(citron Info.plist about_dialog.cpp @@ -656,14 +645,8 @@ if (WIN32 AND QT_VERSION VERSION_GREATER_EQUAL 6) ) endif() -if (CITRON_USE_BUNDLED_QT) +if (WIN32 AND CITRON_USE_BUNDLED_QT) include(CopyCitronQt6Deps) - if (TARGET icu-build) - add_dependencies(citron icu-build) - endif() - if (CITRON_XCB_TARGETS) - add_dependencies(citron ${CITRON_XCB_TARGETS}) - endif() copy_citron_Qt6_deps(citron) endif()