From 8cddd542b6e5b6271f11c74d83e1fc06e6e0d045 Mon Sep 17 00:00:00 2001 From: cheezwiz7899 Date: Thu, 7 May 2026 16:41:59 +1200 Subject: [PATCH] deploy qt and its dependencies with cpm/cmake --- CMakeLists.txt | 80 ++++------- CMakeModules/CopyCitronQt6Deps.cmake | 170 ++++++++++++++++++++---- CMakeModules/FixLinuxBundleRpaths.cmake | 152 +++++++++++++++++++++ CMakeModules/dependencies.cmake | 29 ++++ CMakeModules/icu_build.cmake | 67 ++++++++++ CMakeModules/qt_download.cmake | 22 ++- CMakeModules/xcb_build.cmake | 108 +++++++++++++++ build-citron-linux.sh | 33 +++-- externals/ffmpeg/CMakeLists.txt | 4 +- src/citron/CMakeLists.txt | 14 +- 10 files changed, 583 insertions(+), 96 deletions(-) create mode 100644 CMakeModules/FixLinuxBundleRpaths.cmake create mode 100644 CMakeModules/icu_build.cmake create mode 100644 CMakeModules/xcb_build.cmake mode change 100644 => 100755 build-citron-linux.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index ddd3113d63..0c91897ec1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,11 +89,21 @@ CMAKE_DEPENDENT_OPTION(CITRON_ROOM "Compile LDN room server" ON "NOT ANDROID" OF CMAKE_DEPENDENT_OPTION(CITRON_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF) +# CITRON_USE_CPM is the primary way to fetch dependencies on Linux and for Clangtron. +# vcpkg is supported primarily for native MSVC and Android builds. option(CITRON_USE_BUNDLED_VCPKG "Use vcpkg for citron dependencies" "${MSVC}") +option(CITRON_USE_CPM "Use CPM.cmake to fetch dependencies" OFF) -option(CITRON_CHECK_SUBMODULES "Check if submodules are present" ON) +# Auto-enable CPM for Linux/MinGW if not using vcpkg +if (NOT CITRON_USE_BUNDLED_VCPKG AND (UNIX OR MINGW)) + set(CITRON_USE_CPM ON CACHE BOOL "" FORCE) +endif() -option(CITRON_USE_CPM "Use CPM.cmake to fetch dependencies instead of git submodules (Clangtron builds)" OFF) +if (CITRON_USE_CPM) + set(CITRON_CHECK_SUBMODULES OFF CACHE BOOL "" FORCE) +else() + option(CITRON_CHECK_SUBMODULES "Check if submodules are present" ON) +endif() option(CITRON_ENABLE_LTO "Enable link-time optimization" OFF) @@ -113,52 +123,6 @@ if (ANDROID OR WIN32 OR APPLE) endif() option(ENABLE_OPENSSL "Enable OpenSSL backend for ISslConnection" ${DEFAULT_ENABLE_OPENSSL}) -if (ANDROID AND CITRON_DOWNLOAD_ANDROID_VVL) - set(vvl_version "vulkan-sdk-1.4.328.1") - set(vvl_version_plain "1.4.328.1") - set(vvl_zip_file "${CMAKE_BINARY_DIR}/externals/vvl-android.zip") - set(vvl_extract_dir "${CMAKE_BINARY_DIR}/externals") - - if (NOT EXISTS "${vvl_zip_file}") - # Download and extract validation layer release to externals directory - set(vvl_base_url "https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download") - file(DOWNLOAD "${vvl_base_url}/${vvl_version}/android-binaries-${vvl_version_plain}.zip" - "${vvl_zip_file}" SHOW_PROGRESS) - execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${vvl_zip_file}" - WORKING_DIRECTORY "${vvl_extract_dir}") - endif() - - # Copy the arm64 binary to src/android/app/main/jniLibs - # New structure: android-binaries-X.X.X.X/android-binaries-X.X.X.X/arm64-v8a/libVkLayer_khronos_validation.so - set(vvl_lib_path "${CMAKE_CURRENT_SOURCE_DIR}/src/android/app/src/main/jniLibs/arm64-v8a/") - set(vvl_source_file "${vvl_extract_dir}/android-binaries-${vvl_version_plain}/android-binaries-${vvl_version_plain}/arm64-v8a/libVkLayer_khronos_validation.so") - - if (EXISTS "${vvl_source_file}") - file(COPY "${vvl_source_file}" DESTINATION "${vvl_lib_path}") - message(STATUS "Successfully copied VVL ${vvl_version_plain} to ${vvl_lib_path}") - else() - message(WARNING "Could not find Vulkan Validation Layer at: ${vvl_source_file}") - message(STATUS "Please manually download from: ${vvl_base_url}/${vvl_version}/android-binaries-${vvl_version_plain}.zip") - endif() -endif() - -# EXPERIMENTAL LIBC++ -if ((ANDROID OR APPLE OR UNIX) AND (NOT (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") OR ANDROID) AND NOT WIN32) - if(CXX_APPLE OR CXX_CLANG) - # libc++ has stop_token and jthread as experimental - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexperimental-library") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fexperimental-library") - else() - # Uses glibc, mostly? - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_LIBCPP_ENABLE_EXPERIMENTAL=1") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_LIBCPP_ENABLE_EXPERIMENTAL=1") - endif() -endif() - -if (ANDROID) - set(CMAKE_SKIP_INSTALL_RULES ON) -endif() - if (CITRON_USE_BUNDLED_VCPKG) if (ANDROID) set(ENV{ANDROID_NDK_HOME} "${ANDROID_NDK}") @@ -166,8 +130,6 @@ if (CITRON_USE_BUNDLED_VCPKG) if (CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a") set(VCPKG_TARGET_TRIPLET "arm64-android") - # this is to avoid CMake using the host pkg-config to find the host - # libraries when building for Android targets set(PKG_CONFIG_EXECUTABLE "aarch64-none-linux-android-pkg-config" CACHE FILEPATH "" FORCE) elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL "x86_64") set(VCPKG_TARGET_TRIPLET "x64-android") @@ -185,9 +147,7 @@ if (CITRON_USE_BUNDLED_VCPKG) if (NOT EXISTS ${NASM_DESTINATION_PATH}) file(DOWNLOAD ${NASM_DOWNLOAD_URL} ${NASM_DESTINATION_PATH} SHOW_PROGRESS STATUS NASM_STATUS) - if (NOT NASM_STATUS EQUAL 0) - # Warn and not fail since vcpkg is supposed to download this package for us in the first place message(WARNING "External nasm vcpkg package download from ${NASM_DOWNLOAD_URL} failed with status ${NASM_STATUS}") endif() endif() @@ -199,17 +159,15 @@ if (CITRON_USE_BUNDLED_VCPKG) if (ENABLE_WEB_SERVICE) list(APPEND VCPKG_MANIFEST_FEATURES "web-service") endif() - if (ANDROID) - list(APPEND VCPKG_MANIFEST_FEATURES "android") - endif() include(${CMAKE_SOURCE_DIR}/externals/vcpkg/scripts/buildsystems/vcpkg.cmake) elseif(NOT "$ENV{VCPKG_TOOLCHAIN_FILE}" STREQUAL "") - # Disable manifest mode (use vcpkg classic mode) when using a custom vcpkg installation option(VCPKG_MANIFEST_MODE "") include("$ENV{VCPKG_TOOLCHAIN_FILE}") endif() +option(CITRON_USE_PRECOMPILED_HEADERS "Use Precompiled Headers" ON) + if (CITRON_USE_PRECOMPILED_HEADERS) if (MSVC AND CCACHE) # buildcache does not properly cache PCH files, leading to compilation errors. @@ -345,6 +303,16 @@ endif() # Output binaries to bin/ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) +if (CITRON_USE_CPM AND CITRON_USE_BUNDLED_QT AND CMAKE_SYSTEM_NAME STREQUAL "Linux") + # Build-tree binaries are the portable deliverable for local Linux CPM builds. + # Keep their loader search path relative to bin/ instead of embedding Qt or + # dependency cache directories from the build host. + set(CMAKE_BUILD_RPATH "$ORIGIN/lib") + set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) + add_link_options("-Wl,--disable-new-dtags") +endif() + # System imported libraries # ======================================================================= diff --git a/CMakeModules/CopyCitronQt6Deps.cmake b/CMakeModules/CopyCitronQt6Deps.cmake index 9fa4762c08..3679962e9b 100644 --- a/CMakeModules/CopyCitronQt6Deps.cmake +++ b/CMakeModules/CopyCitronQt6Deps.cmake @@ -67,44 +67,166 @@ function(copy_citron_Qt6_deps target_dir) qschannelbackend$<$:d>.* qopensslbackend$<$:d>.* ) - elseif(MINGW) - # For MinGW builds with CITRON_USE_BUNDLED_QT=ON the bundled Qt tree is - # at build/externals/qt//mingw_64/. CopyMinGWDeps.cmake handles the - # runtime DLLs; this function handles the plugin subdirectories that - # windeployqt would normally deploy but which are not on the DLL search path. + elseif(MINGW OR CMAKE_SYSTEM_NAME STREQUAL "Linux") + # For MinGW and Native Linux builds with CITRON_USE_BUNDLED_QT=ON, + # copy the plugins and libraries to subdirectories to keep bin/ clean. + if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(LIB_DEST "${DLL_DEST}lib/") + set(PLUGINS_DEST "${DLL_DEST}plugins/") + set(USE_LIB_FOLDER ON) + else() + # Windows/MinGW: DLLs must be next to the EXE + set(LIB_DEST "${DLL_DEST}") + set(PLUGINS_DEST "${DLL_DEST}") + set(USE_LIB_FOLDER OFF) + endif() - # Resolve the plugin root from Qt6_DIR (which points to lib/cmake/Qt6). set(Qt6_BUNDLED_PLUGINS "${Qt6_DIR}/../../../plugins") - foreach(plugin_dir platforms styles imageformats iconengines) + foreach(plugin_dir platforms styles imageformats iconengines tls wayland-graphics-integration wayland-shell-integration) if (EXISTS "${Qt6_BUNDLED_PLUGINS}/${plugin_dir}") add_custom_command(TARGET ${target_dir} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory "${DLL_DEST}${plugin_dir}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${PLUGINS_DEST}${plugin_dir}" COMMAND ${CMAKE_COMMAND} -E copy_directory "${Qt6_BUNDLED_PLUGINS}/${plugin_dir}" - "${DLL_DEST}${plugin_dir}" - COMMENT "Copying Qt6 ${plugin_dir} plugins (MinGW bundled)" + "${PLUGINS_DEST}${plugin_dir}" + COMMENT "Bundling Qt6 ${plugin_dir} plugins" ) endif() endforeach() - # TLS plugins — critical for HTTPS/SSL. - if (EXISTS "${Qt6_BUNDLED_PLUGINS}/tls") + if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + # For Linux, also copy the shared libraries. + set(_qt_libs Core Gui Widgets Network Svg OpenGL DBus WaylandClient WaylandEglClient WaylandEglClientHwIntegration XcbQpa) + if (CITRON_USE_QT_MULTIMEDIA) + list(APPEND _qt_libs Multimedia MultimediaWidgets) + endif() + + foreach(_lib ${_qt_libs}) + if (EXISTS "${Qt6_DLL_DIR}libQt6${_lib}.so.6") + add_custom_command(TARGET ${target_dir} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "${LIB_DEST}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${Qt6_DLL_DIR}libQt6${_lib}.so.6" + "${LIB_DEST}" + COMMENT "Bundling libQt6${_lib}.so.6" + ) + endif() + endforeach() + + find_program(PATCHELF_EXE patchelf) + if (NOT PATCHELF_EXE) + message(FATAL_ERROR + "patchelf is required for portable Linux CPM bundles. " + "Run ./build-citron-linux.sh setup or install patchelf with your package manager.") + endif() + + # Bundle ICU libraries (required by QtCore) + if (DEFINED ICU_BINARY_DIR) + # If we built it ourselves, we know what we expect. + # Note: We can't use GLOB at configuration time if it's a fresh build. + set(_icu_libs data i18n io test tu uc) + foreach(_icu_comp ${_icu_libs}) + set(_icu_lib "${ICU_BINARY_DIR}/libicu${_icu_comp}.so.73") + add_custom_command(TARGET ${target_dir} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "${LIB_DEST}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_icu_lib}" "${LIB_DEST}" + COMMENT "Bundling ICU library: ${_icu_lib}" + ) + endforeach() + else() + file(GLOB _icu_libs "${Qt6_DLL_DIR}libicu*.so.[0-9]*") + foreach(_icu_lib ${_icu_libs}) + add_custom_command(TARGET ${target_dir} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "${LIB_DEST}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_icu_lib}" "${LIB_DEST}" + COMMENT "Bundling ICU library: ${_icu_lib}" + ) + endforeach() + endif() + + # Bundle additional XCB support libraries + set(_xcb_deps + xcb.so.1 Xau.so.6 xcb-xkb.so.1 + xcb-cursor.so.0 xcb-icccm.so.4 xcb-image.so.0 xcb-keysyms.so.1 + xcb-util.so.1 + xcb-render-util.so.0 xcb-xinerama.so.0 xcb-xinput.so.0 + xcb-shm.so.0 xcb-render.so.0 xcb-randr.so.0 xcb-shape.so.0 + xcb-xfixes.so.0 xcb-sync.so.1 xcb-dri3.so.0 + # Additional system libs needed by Qt XCB plugin + xkbcommon.so.0 xkbcommon-x11.so.0 X11-xcb.so.1 Xdmcp.so.6 + ) + + if (DEFINED XCB_BINARY_DIR) + set(_xcb_search_dir "${XCB_BINARY_DIR}/") + else() + set(_xcb_search_dir "SYSTEM") + endif() + + foreach(_xcb_lib ${_xcb_deps}) + if (_xcb_search_dir STREQUAL "SYSTEM" OR _xcb_lib MATCHES "xkbcommon|X11-xcb") + execute_process(COMMAND ${CMAKE_C_COMPILER} -print-file-name=lib${_xcb_lib} OUTPUT_VARIABLE _LIB_PATH OUTPUT_STRIP_TRAILING_WHITESPACE) + if (NOT EXISTS "${_LIB_PATH}") + # Fallback for some systems where print-file-name doesn't work well + set(_LIB_PATH "/usr/lib/x86_64-linux-gnu/lib${_xcb_lib}") + endif() + else() + set(_LIB_PATH "${_xcb_search_dir}lib${_xcb_lib}") + endif() + + # Always add the bundling command for built libs, or if system lib exists + add_custom_command(TARGET ${target_dir} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "${LIB_DEST}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_LIB_PATH}" "${LIB_DEST}" + COMMENT "Bundling XCB support library: ${_xcb_lib}" + ) + endforeach() + + # Bundle libstdc++ and libgcc_s + execute_process(COMMAND ${CMAKE_C_COMPILER} -print-file-name=libstdc++.so.6 OUTPUT_VARIABLE _STDCXX_PATH OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${CMAKE_C_COMPILER} -print-file-name=libgcc_s.so.1 OUTPUT_VARIABLE _GCC_S_PATH OUTPUT_STRIP_TRAILING_WHITESPACE) + + if (EXISTS "${_STDCXX_PATH}") + add_custom_command(TARGET ${target_dir} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "${LIB_DEST}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_STDCXX_PATH}" "${LIB_DEST}" + COMMENT "Bundling libstdc++.so.6" + ) + endif() + if (EXISTS "${_GCC_S_PATH}") + add_custom_command(TARGET ${target_dir} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "${LIB_DEST}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_GCC_S_PATH}" "${LIB_DEST}" + COMMENT "Bundling libgcc_s.so.1" + ) + endif() + + set(_runtime_dependency_dirs "${LIB_DEST}" "${Qt6_DLL_DIR}") + if (DEFINED ICU_BINARY_DIR) + list(APPEND _runtime_dependency_dirs "${ICU_BINARY_DIR}") + endif() + if (DEFINED XCB_BINARY_DIR) + list(APPEND _runtime_dependency_dirs "${XCB_BINARY_DIR}") + endif() + list(REMOVE_DUPLICATES _runtime_dependency_dirs) + string(REPLACE ";" "|" _runtime_dependency_dirs_arg "${_runtime_dependency_dirs}") + add_custom_command(TARGET ${target_dir} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory "${TLS}" - COMMAND ${CMAKE_COMMAND} -E copy_directory - "${Qt6_BUNDLED_PLUGINS}/tls" - "${TLS}" - COMMENT "Copying Qt6 TLS plugins (MinGW bundled)" + COMMAND ${CMAKE_COMMAND} + -DBUNDLE_BIN_DIR="${DLL_DEST}" + -DPATCHELF_EXE="${PATCHELF_EXE}" + -DRUNTIME_DEPENDENCY_DIRS="${_runtime_dependency_dirs_arg}" + -P "${CMAKE_SOURCE_DIR}/CMakeModules/FixLinuxBundleRpaths.cmake" + COMMENT "Copying Linux runtime dependencies and normalizing RPATHs" ) - else() - message(WARNING "Qt6 TLS plugin directory not found at ${Qt6_BUNDLED_PLUGINS}/tls") - message(WARNING "SSL/HTTPS will not work in this build.") endif() endif() - # Create an empty qt.conf so Qt locates its plugins relative to the executable. - add_custom_command(TARGET citron POST_BUILD - COMMAND ${CMAKE_COMMAND} -E touch ${DLL_DEST}qt.conf - ) + # Create a qt.conf next to the executable. + file(MAKE_DIRECTORY "${DLL_DEST}") + if (USE_LIB_FOLDER) + file(WRITE "${DLL_DEST}qt.conf" "[Paths]\nPlugins = plugins\n") + else() + file(WRITE "${DLL_DEST}qt.conf" "") + endif() endfunction(copy_citron_Qt6_deps) diff --git a/CMakeModules/FixLinuxBundleRpaths.cmake b/CMakeModules/FixLinuxBundleRpaths.cmake new file mode 100644 index 0000000000..ca87a917a6 --- /dev/null +++ b/CMakeModules/FixLinuxBundleRpaths.cmake @@ -0,0 +1,152 @@ +# 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 b2e68521a8..c4a94f1e32 100644 --- a/CMakeModules/dependencies.cmake +++ b/CMakeModules/dependencies.cmake @@ -17,6 +17,14 @@ # Static linking: BUILD_SHARED_LIBS is forced OFF in externals/CMakeLists.txt; # all CPM packages inherit this setting. + +# ── Submodule & vcpkg Policy ────────────────────────────────────────────────── +# Force-disable reliance on git submodules and vcpkg. All external dependencies +# must be fetched and managed via CPM to ensure portability and build-time +# environment isolation. +set(CITRON_CHECK_SUBMODULES OFF CACHE BOOL "Force disable submodule presence checks" FORCE) +set(CITRON_USE_BUNDLED_VCPKG OFF CACHE BOOL "Force disable vcpkg usage" FORCE) + if (NOT COMMAND CPMAddPackage) message(FATAL_ERROR "CPM.cmake not loaded — include CMakeModules/CPM.cmake before this file") endif() @@ -574,6 +582,23 @@ elseif (CITRON_USE_BUNDLED_FFMPEG) endif() endif() +# ── Dependency Versions (Qt, ICU, XCB) ───────────────────────────────────────── +set(CITRON_QT_VERSION "6.9.3" CACHE STRING "Qt version to download via aqt") + +set(CITRON_ICU_REPO "unicode-org/icu" CACHE STRING "ICU GitHub repository") +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_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") +set(CITRON_XCB_IMAGE_VER "0.4.1" CACHE STRING "XCB image version") +set(CITRON_XCB_KEYSYMS_VER "0.4.1" CACHE STRING "XCB keysyms version") +set(CITRON_XCB_RENDERUTIL_VER "0.3.10" CACHE STRING "XCB renderutil version") +set(CITRON_XCB_WM_VER "0.4.2" CACHE STRING "XCB wm version") + # ═══════════════════════════════════════════════════════════════════════════════ # Qt # ═══════════════════════════════════════════════════════════════════════════════ @@ -582,6 +607,10 @@ endif() # See CMakeModules/qt_download.cmake for details. if (ENABLE_QT AND NOT USE_SYSTEM_QT) include(${CMAKE_SOURCE_DIR}/CMakeModules/qt_download.cmake) + if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + include(${CMAKE_SOURCE_DIR}/CMakeModules/icu_build.cmake) + include(${CMAKE_SOURCE_DIR}/CMakeModules/xcb_build.cmake) + endif() endif() message(STATUS "[CPM] All dependency packages configured") diff --git a/CMakeModules/icu_build.cmake b/CMakeModules/icu_build.cmake new file mode 100644 index 0000000000..73fa1e04c1 --- /dev/null +++ b/CMakeModules/icu_build.cmake @@ -0,0 +1,67 @@ +# SPDX-FileCopyrightText: 2026 citron Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + +# ICU build script for Linux portability. +# Fetches ICU via CPM and builds it as shared libraries using Autotools. + +if (NOT UNIX OR APPLE) + return() +endif() + +if (NOT DEFINED CITRON_ICU_REPO) + set(CITRON_ICU_REPO "unicode-org/icu") +endif() +if (NOT DEFINED CITRON_ICU_TAG) + set(CITRON_ICU_TAG "release-73-2") +endif() + +CPMAddPackage( + NAME icu_src + GITHUB_REPOSITORY "${CITRON_ICU_REPO}" + GIT_TAG "${CITRON_ICU_TAG}" + DOWNLOAD_ONLY YES +) + +if (icu_src_ADDED) + set(ICU_PREFIX "${icu_src_SOURCE_DIR}/icu4c") + set(ICU_BUILD_DIR "${CMAKE_BINARY_DIR}/externals/icu-build") + file(MAKE_DIRECTORY "${ICU_BUILD_DIR}") + + # Identify the shared libraries we expect to produce + set(_icu_components data i18n io test tu uc) + set(ICU_LIBRARIES) + foreach(_comp ${_icu_components}) + list(APPEND ICU_LIBRARIES "${ICU_BUILD_DIR}/lib/libicu${_comp}.so.73") + endforeach() + + execute_process(COMMAND nproc OUTPUT_VARIABLE SYSTEM_THREADS OUTPUT_STRIP_TRAILING_WHITESPACE) + + set(_icu_work_dir "${ICU_BUILD_DIR}/build") + file(MAKE_DIRECTORY "${_icu_work_dir}") + + add_custom_command( + OUTPUT "${_icu_work_dir}/Makefile" + COMMAND "${ICU_PREFIX}/source/configure" + --prefix="${ICU_BUILD_DIR}" + --enable-shared + --disable-static + --disable-samples + --disable-tests + WORKING_DIRECTORY "${_icu_work_dir}" + COMMENT "Configuring ICU via Autotools" + ) + + add_custom_command( + OUTPUT ${ICU_LIBRARIES} + COMMAND make -j${SYSTEM_THREADS} + COMMAND make install + DEPENDS "${_icu_work_dir}/Makefile" + WORKING_DIRECTORY "${_icu_work_dir}" + COMMENT "Building and installing ICU" + ) + + add_custom_target(icu-build ALL DEPENDS ${ICU_LIBRARIES}) + + # Export for CopyCitronQt6Deps + set(ICU_BINARY_DIR "${ICU_BUILD_DIR}/lib" CACHE INTERNAL "Location of CPM-built ICU libs") +endif() diff --git a/CMakeModules/qt_download.cmake b/CMakeModules/qt_download.cmake index 82f21645e5..d072f88f41 100644 --- a/CMakeModules/qt_download.cmake +++ b/CMakeModules/qt_download.cmake @@ -17,8 +17,20 @@ # # Prerequisites: Python3 + aqt must be installed (build script's ensure_aqt() handles this). -set(CITRON_QT_VERSION "6.9.3" CACHE STRING "Qt version to download via aqt") -set(CITRON_QT_BASE_DIR "${CMAKE_BINARY_DIR}/externals/qt-cpm" CACHE PATH +# The version is defined in dependencies.cmake as a CACHE variable. +if (NOT DEFINED CITRON_QT_VERSION) + set(CITRON_QT_VERSION "6.9.3") +endif() + +if (DEFINED ENV{CPM_SOURCE_CACHE}) + set(_DEFAULT_QT_BASE_DIR "$ENV{CPM_SOURCE_CACHE}/qt-bin") +elseif (DEFINED CPM_SOURCE_CACHE) + set(_DEFAULT_QT_BASE_DIR "${CPM_SOURCE_CACHE}/qt-bin") +else() + set(_DEFAULT_QT_BASE_DIR "${CMAKE_BINARY_DIR}/externals/qt-cpm") +endif() + +set(CITRON_QT_BASE_DIR "${_DEFAULT_QT_BASE_DIR}" CACHE PATH "Base directory for aqt-managed Qt downloads") # ── Find aqt ────────────────────────────────────────────────────────────────── @@ -44,12 +56,14 @@ if (WIN32) set(_QT_OS "windows") set(_QT_TARGET "desktop") set(_QT_ARCH "win64_llvm_mingw") + set(_QT_DIR_NAME "llvm-mingw_64") set(_QT_CMAKE_SUB "lib/cmake/Qt6") else() # Native Linux (or macOS — extend here if needed) 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") endif() @@ -57,7 +71,7 @@ endif() if (Qt6_DIR AND EXISTS "${Qt6_DIR}/Qt6Config.cmake") message(STATUS "[Qt] Using target Qt from Qt6_DIR: ${Qt6_DIR}") else() - set(_QT_TARGET_DIR "${CITRON_QT_BASE_DIR}/${CITRON_QT_VERSION}/${_QT_ARCH}") + 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") if (NOT EXISTS "${_QT_TARGET_CMAKE}") @@ -92,7 +106,7 @@ else() ${_QT_OS} ${_QT_TARGET} ${CITRON_QT_VERSION} ${_QT_ARCH} --outputdir "${CITRON_QT_BASE_DIR}" - --modules qtmultimedia qtimageformats qtsvg + --modules qtmultimedia qtimageformats RESULT_VARIABLE _qt_mm_result OUTPUT_QUIET ERROR_QUIET ) diff --git a/CMakeModules/xcb_build.cmake b/CMakeModules/xcb_build.cmake new file mode 100644 index 0000000000..c9d667f942 --- /dev/null +++ b/CMakeModules/xcb_build.cmake @@ -0,0 +1,108 @@ +# SPDX-FileCopyrightText: 2026 citron Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + +# XCB utility build script for Linux portability. +# Fetches XCB components via CPM and builds them as shared libraries. + +if (NOT UNIX OR APPLE) + return() +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_BUILD_ROOT "${CMAKE_BINARY_DIR}/externals/xcb-build") +file(MAKE_DIRECTORY "${XCB_BUILD_ROOT}") +set(XCB_BINARY_DIR "${XCB_BUILD_ROOT}/lib" CACHE INTERNAL "Location of CPM-built XCB libs") + +# Define versions for each component to fetch the correct tarball +# These are defined in dependencies.cmake as CACHE variables. +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_libxcb_ver "${CITRON_XCB_LIBXCB_VER}") +set(_xcb_cursor_ver "${CITRON_XCB_CURSOR_VER}") +set(_xcb_util_ver "${CITRON_XCB_UTIL_VER}") +set(_xcb_image_ver "${CITRON_XCB_IMAGE_VER}") +set(_xcb_keysyms_ver "${CITRON_XCB_KEYSYMS_VER}") +set(_xcb_renderutil_ver "${CITRON_XCB_RENDERUTIL_VER}") +set(_xcb_wm_ver "${CITRON_XCB_WM_VER}") + +set(CITRON_XCB_TARGETS "" CACHE INTERNAL "List of XCB build targets") +foreach(_comp ${_xcb_components}) + # (re-pasting the existing logic to avoid deletion) + if (_comp STREQUAL "macros") + set(_url "https://xorg.freedesktop.org/archive/individual/util/util-macros-${_xcb_macros_ver}.tar.xz") + set(_name "util-macros") + 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 "libxcb") + set(_url "https://xcb.freedesktop.org/dist/libxcb-${_xcb_libxcb_ver}.tar.xz") + set(_name "libxcb") + elseif(_comp STREQUAL "proto") + set(_url "https://xcb.freedesktop.org/dist/xcb-proto-${_xcb_proto_ver}.tar.xz") + set(_name "xcb-proto") + else() + set(_name "xcb-util") + if (NOT _comp STREQUAL "util") + set(_name "xcb-util-${_comp}") + endif() + set(_ver "${_xcb_${_comp}_ver}") + set(_url "https://xcb.freedesktop.org/dist/${_name}-${_ver}.tar.xz") + endif() + + CPMAddPackage( + NAME xcb_${_comp}_src + URL "${_url}" + DOWNLOAD_ONLY YES + ) + + if (xcb_${_comp}_src_ADDED) + set(_src_dir "${xcb_${_comp}_src_SOURCE_DIR}") + set(_build_dir "${XCB_BUILD_ROOT}/${_comp}") + file(MAKE_DIRECTORY "${_build_dir}") + + # Check if it has a CMakeLists.txt (newer versions do) or needs Autotools + if (EXISTS "${_src_dir}/CMakeLists.txt") + # Build via CMake + add_subdirectory("${_src_dir}" "${_build_dir}") + else() + # Build via Autotools (standard fallback) + set(_env_vars + "ACLOCAL_PATH=${XCB_BUILD_ROOT}/share/aclocal" + "PKG_CONFIG_PATH=${XCB_BUILD_ROOT}/lib/pkgconfig:${XCB_BUILD_ROOT}/share/pkgconfig" + "PYTHON=python3" + "LD_RUN_PATH=\$\$ORIGIN" + ) + + set(_target_name "xcb-${_comp}-build") + list(APPEND CITRON_XCB_TARGETS ${_target_name}) + set(CITRON_XCB_TARGETS ${CITRON_XCB_TARGETS} CACHE INTERNAL "List of XCB build targets") + + # We use autoreconf -fi to refresh libtool/aclocal files from the host system + # to avoid mismatch errors (e.g. 'ltmain.sh is newer'). + add_custom_command( + OUTPUT "${_build_dir}/Makefile" + COMMAND ${CMAKE_COMMAND} -E env ${_env_vars} autoreconf -fi "${_src_dir}" + COMMAND ${CMAKE_COMMAND} -E env ${_env_vars} "${_src_dir}/configure" --prefix="${XCB_BUILD_ROOT}" --enable-shared + WORKING_DIRECTORY "${_build_dir}" + COMMENT "Configuring ${_name} via Autotools" + ) + add_custom_target(${_target_name} ALL + COMMAND ${CMAKE_COMMAND} -E env ${_env_vars} make install + DEPENDS "${_build_dir}/Makefile" + WORKING_DIRECTORY "${_build_dir}" + COMMENT "Building ${_name}" + ) + + # Enforce sequential build ordering to ensure all dependencies are installed + # in XCB_BUILD_ROOT before downstream components are configured. + if (_last_xcb_target) + add_dependencies(${_target_name} ${_last_xcb_target}) + endif() + set(_last_xcb_target ${_target_name}) + endif() + endif() +endforeach() diff --git a/build-citron-linux.sh b/build-citron-linux.sh old mode 100644 new mode 100755 index 2f47da905b..835348b0c2 --- a/build-citron-linux.sh +++ b/build-citron-linux.sh @@ -129,6 +129,7 @@ # python3 + aqtinstall Qt binary download (invoked by cmake) # autoconf + automake + make FFmpeg autotools build # glslang (glslc) Vulkan shader compilation +# patchelf bundle RPATH normalization # perf propeller stage only # ============================================================================= @@ -350,7 +351,7 @@ stage_setup() { *) warn "Unrecognised package manager. Install manually:" warn " clang (${CLANG_VERSION}+), lld, llvm-profdata, cmake, ninja, git," - warn " nasm, perl, python3, python3-pip, autoconf, automake, make, glslang-tools" + warn " nasm, perl, python3, python3-pip, autoconf, automake, make, glslang-tools, patchelf" ;; esac @@ -370,6 +371,7 @@ _setup_apt() { nasm yasm perl \ autoconf automake make \ glslang-tools \ + patchelf \ lsb-release software-properties-common gnupg \ libelf-dev libssl-dev libzstd-dev \ linux-tools-common linux-tools-generic 2>/dev/null || true @@ -384,7 +386,7 @@ _setup_pacman() { nasm yasm perl \ autoconf automake make \ glslang clang lld llvm \ - perf 2>/dev/null || true + patchelf perf 2>/dev/null || true # Arch ships unversioned tools — symlink to versioned names for tool in clang clang++ lld llvm-profdata llvm-bolt merge-fdata; do local versioned="/usr/local/bin/${tool}-${CLANG_VERSION}" @@ -402,7 +404,7 @@ _setup_dnf() { python3 python3-pip curl wget xz \ nasm yasm perl \ autoconf automake make \ - glslang clang lld \ + glslang clang lld patchelf \ elfutils-libelf-devel openssl-devel \ perf 2>/dev/null || true sudo dnf install -y "clang${CLANG_VERSION}" "llvm${CLANG_VERSION}" 2>/dev/null \ @@ -417,7 +419,7 @@ _setup_yum() { python3 python3-pip curl wget xz \ nasm yasm perl \ autoconf automake make \ - clang lld \ + clang lld patchelf \ elfutils-libelf-devel openssl-devel \ perf 2>/dev/null || true warn "yum/CentOS: LLVM ${CLANG_VERSION} may not be in repos. Check SCL or llvm.org." @@ -430,7 +432,7 @@ _setup_zypper() { python3 python3-pip curl wget xz \ nasm yasm perl \ autoconf automake make \ - glslang clang lld llvm \ + glslang clang lld llvm patchelf \ libelf-devel libopenssl-devel \ perf 2>/dev/null || true } @@ -444,6 +446,7 @@ _setup_emerge() { sys-devel/clang sys-devel/lld \ dev-build/autoconf dev-build/automake \ media-libs/glslang \ + dev-util/patchelf \ dev-libs/elfutils dev-libs/openssl 2>/dev/null || true } @@ -503,7 +506,7 @@ _verify_tools() { info "Verifying installation..." local ok=1 for tool in "${CLANG}" "${CLANGPP}" "${LLD}" "${LLVM_PROFDATA}" \ - cmake ninja git nasm perl python3; do + cmake ninja git nasm perl python3 patchelf; do if command -v "${tool}" &>/dev/null; then success " ${tool} -> $(command -v "${tool}")" else @@ -663,7 +666,9 @@ common_cmake_args() { "-DUSE_SYSTEM_QT=OFF" \ "-DENABLE_QT6=ON" \ "-DCITRON_USE_BUNDLED_FFMPEG=ON" \ + "-DBUILD_TESTING=OFF" \ "-DCITRON_TESTS=OFF" \ + "-DCITRON_DOWNLOAD_TIME_ZONE_DATA=ON" \ "-DCITRON_CHECK_SUBMODULES=OFF" \ "-DCITRON_USE_LLVM_DEMANGLE=OFF" \ "-DCITRON_USE_QT_MULTIMEDIA=ON" \ @@ -822,6 +827,7 @@ stage_generate() { "-DCMAKE_EXE_LINKER_FLAGS_${bt_upper}=${linker_flags}" write_gen_sentinel + mkdir -p "${BUILD_GENERATE}/bin/user" success "Instrumented build: ${BUILD_GENERATE}/bin/citron" print_profiling_instructions "${BUILD_GENERATE}/bin/citron" } @@ -894,6 +900,7 @@ stage_csgenerate() { "-DCMAKE_CXX_FLAGS_${bt_upper}=${compile_flags}" \ "-DCMAKE_EXE_LINKER_FLAGS_${bt_upper}=${linker_flags}" + mkdir -p "${BUILD_CSGENERATE}/bin/user" success "CS-IRPGO instrumented build: ${BUILD_CSGENERATE}/bin/citron" echo "" echo -e "${YELLOW}════════════════════════════════════════════════════════════════${RESET}" @@ -963,6 +970,7 @@ stage_use() { header "Build: Baseline Release (no PGO, LTO=${LTO_MODE})" apply_source_patches _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" return 0 fi @@ -1004,6 +1012,7 @@ stage_use() { echo "" success "════════════════════════════════════════════════════════════════" success " Stage use complete" + mkdir -p "${BUILD_USE}/bin/user" success " Binary: ${BUILD_USE}/bin/citron" success " PGO: ${pgo_label}" success " LTO: ${LTO_MODE}${LTO_MODE:+ ($(lto_clang_flag))}" @@ -1123,8 +1132,8 @@ stage_bolt() { fi local elf_binary="${relocs_dir}/bin/citron" - mkdir -p "${BOLT_PROFILE_DIR}" "${BUILD_BOLT}" - local instrumented="${BUILD_BOLT}/citron-bolt-instrumented" + mkdir -p "${BOLT_PROFILE_DIR}" "${BUILD_BOLT}/bin" + local instrumented="${BUILD_BOLT}/bin/citron-bolt-instrumented" local fdata_pattern="${BOLT_PROFILE_DIR}/citron-%p.fdata" local merged_fdata="${BOLT_PROFILE_DIR}/citron-merged.fdata" @@ -1167,15 +1176,17 @@ stage_bolt() { --split-all-cold \ --split-eh \ --dyno-stats \ - -o "${BUILD_BOLT}/citron" \ + -o "${BUILD_BOLT}/bin/citron" \ || error "BOLT optimization failed." echo "" success "════════════════════════════════════════════════════════════════" success " Stage bolt complete" - success " Binary: ${BUILD_BOLT}/citron" + mkdir -p "${BUILD_BOLT}/user" + success " Binary: ${BUILD_BOLT}/bin/citron" success " Optimizations: PGO + LTO + BOLT basic-block reordering" success "════════════════════════════════════════════════════════════════" + echo "" } # ============================================================================= @@ -1341,9 +1352,11 @@ stage_propeller() { 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 "" } # ============================================================================= diff --git a/externals/ffmpeg/CMakeLists.txt b/externals/ffmpeg/CMakeLists.txt index 7bb0d147cf..cc6d0a6695 100644 --- a/externals/ffmpeg/CMakeLists.txt +++ b/externals/ffmpeg/CMakeLists.txt @@ -40,6 +40,9 @@ if (NOT WIN32 AND NOT ANDROID) set(FFmpeg_FOUND YES) endif() + if (NOT DEFINED FFmpeg_COMPONENTS) + set(FFmpeg_COMPONENTS avcodec avfilter avutil swscale) + endif() unset(FFmpeg_LIBRARIES CACHE) foreach(COMPONENT ${FFmpeg_COMPONENTS}) set(FFmpeg_${COMPONENT}_PREFIX "${FFmpeg_BUILD_DIR}/lib${COMPONENT}") @@ -168,7 +171,6 @@ if (NOT WIN32 AND NOT ANDROID) --disable-ffmpeg --disable-ffprobe --disable-network - --disable-postproc --disable-swresample --enable-decoder=h264 --enable-decoder=vp8 diff --git a/src/citron/CMakeLists.txt b/src/citron/CMakeLists.txt index 4cdd28f07e..f1c91db39a 100644 --- a/src/citron/CMakeLists.txt +++ b/src/citron/CMakeLists.txt @@ -14,7 +14,13 @@ 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")) - set(CMAKE_BUILD_RPATH "${CMAKE_BINARY_DIR}/bin/lib/") + # 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() add_executable(citron @@ -652,6 +658,12 @@ endif() if (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()