mirror of
https://github.com/citron-neo/emulator.git
synced 2026-07-05 15:21:57 -07:00
updated the AppImage builder and attached it to the build script
More dependency handling fixes
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
Type=Application
|
||||
Name=citron
|
||||
Icon=citron
|
||||
Exec=AppRun
|
||||
Exec=citron
|
||||
Categories=Game;
|
||||
|
||||
@@ -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" "$@"
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 12 KiB |
Executable
+102
@@ -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"
|
||||
@@ -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@"
|
||||
+22
-20
@@ -582,23 +582,23 @@ if (ENABLE_QT AND NOT CITRON_USE_CPM)
|
||||
# Try to find Qt6 installation path (aqtinstall layout: externals/qt/<version>/<arch>)
|
||||
# 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()
|
||||
|
||||
@@ -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}")
|
||||
@@ -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")
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
+83
-192
@@ -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 <stage> [options]\nStages: setup generate csgenerate merge summary use bolt propeller clean"
|
||||
|| error "No stage specified.\nUsage: $0 <stage> [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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user