#!/usr/bin/env bash redbear_qt_link_sysroot_dir() { local sysroot="$1" local dir_name="$2" if [ -d "${sysroot}/usr/${dir_name}" ] && [ ! -e "${sysroot}/${dir_name}" ]; then ln -s "usr/${dir_name}" "${sysroot}/${dir_name}" fi } redbear_qt_link_sysroot_dirs() { local sysroot="$1" shift local dir_name for dir_name in "$@"; do redbear_qt_link_sysroot_dir "$sysroot" "$dir_name" done } redbear_qt_link_plugins_dir() { local sysroot="$1" if [ -d "${sysroot}/usr/plugins" ] && [ ! -e "${sysroot}/plugins" ]; then ln -s "usr/plugins" "${sysroot}/plugins" fi } # Qt's generated cmake plugin targets resolve each plugin .so via # /plugins (the _IMPORT_PREFIX is computed from the cmake files under # /lib/cmake, i.e. the sysroot root, NOT /usr). But # `cmake --install --prefix .../usr` puts plugins at usr/plugins. qtbase works # around this by staging plugins to BOTH usr/plugins and plugins; any Qt module # that ships plugins (qtwayland decorations, qtsvg imageformats, qtdeclarative # qmltooling, ...) must do the same or a consumer's find_package fails: imported # target references "/plugins/.../foo.so" but this file does not exist. # Call this after `cmake --install` with the stage root ($COOKBOOK_STAGE). redbear_qt_dual_stage_plugins() { local stage="$1" if [ -d "${stage}/usr/plugins" ]; then mkdir -p "${stage}/plugins" cp -a "${stage}/usr/plugins/." "${stage}/plugins/" 2>/dev/null || true fi } # Qt and KDE install QML modules under DIFFERENT roots: # Qt -> /qml (QT_INSTALL_QML) # KDE -> /lib/qml (KDE_INSTALL_QMLDIR) # ECM's ecm_find_qmlmodule() only looks in the KDE root (plus QT_HOST_PATH/qml, # which holds host-tool imports, not target ones). So a REQUIRED *Qt* QML module # is reported missing even when its recipe staged it perfectly. plasma-framework # hit this on Qt5Compat.GraphicalEffects: # qmldir not found in /usr/lib/qml/Qt5Compat/GraphicalEffects or # /qml/Qt5Compat/GraphicalEffects/qmldir # -> REQUIRED package(s) are missing, aborting CMake run # while org.kde.* modules resolved fine precisely because they live in the KDE # root. Bridge the roots with relative symlinks so either spelling resolves. redbear_qt_bridge_qml_roots() { local sysroot="$1" local qt_qml="${sysroot}/usr/qml" local kde_qml="${sysroot}/usr/lib/qml" [ -d "${qt_qml}" ] || return 0 mkdir -p "${kde_qml}" local entry name for entry in "${qt_qml}"/*; do [ -e "${entry}" ] || continue name="$(basename "${entry}")" # Never clobber a real KDE module of the same name. [ -e "${kde_qml}/${name}" ] && continue ln -s "../../qml/${name}" "${kde_qml}/${name}" done } redbear_qt_prepare_common_sysroot() { local sysroot="$1" redbear_qt_link_sysroot_dirs "$sysroot" plugins mkspecs metatypes modules qml redbear_qt_link_plugins_dir "$sysroot" redbear_qt_bridge_qml_roots "$sysroot" } redbear_qt_reset_cmake_cache_dir() { rm -f CMakeCache.txt if [ -d CMakeFiles ]; then python3 - <<'PY' from pathlib import Path import os import shutil path = Path("CMakeFiles") for node in path.rglob('*'): try: os.chmod(node, 0o700) except OSError: pass shutil.rmtree(path, ignore_errors=False) PY fi } redbear_qt_rewrite_stage_build_paths() { local stage_usr="$1" local build_dir="$2" find "${stage_usr}/lib/cmake" -name '*.cmake' -exec sed -i \ "s|${build_dir}|/usr|g" {} + 2>/dev/null || true } redbear_qt_copy_common_stage_to_sysroot() { local stage_usr="$1" local sysroot="$2" mkdir -p "${sysroot}/include" "${sysroot}/lib" cp -a "${stage_usr}/include/"* "${sysroot}/include/" 2>/dev/null || true cp -a "${stage_usr}/lib/libQt6"* "${sysroot}/lib/" 2>/dev/null || true } redbear_qt_copy_stage_cmake_subdir_to_sysroot() { local stage_usr="$1" local sysroot="$2" local subdir="$3" mkdir -p "${sysroot}/lib/cmake/${subdir}" cp -a "${stage_usr}/lib/cmake/${subdir}/"* "${sysroot}/lib/cmake/${subdir}/" 2>/dev/null || true } redbear_qt_rewrite_stage_include_paths() { local stage_cmake_dir="$1" local sysroot="$2" find "${stage_cmake_dir}" -name '*.cmake' -exec sed -i \ "s|/usr/include|${sysroot}/include|g" {} + 2>/dev/null || true } redbear_qt_rewrite_stage_lib_paths() { local stage_cmake_dir="$1" local sysroot="$2" find "${stage_cmake_dir}" -name '*.cmake' -exec sed -i \ "s|/usr/lib|${sysroot}/lib|g" {} + 2>/dev/null || true } redbear_qt_rewrite_stage_source_metatype_paths() { local stage_cmake_dir="$1" local sysroot="$2" local source_root="$3" find "${stage_cmake_dir}" -name '*.cmake' -exec sed -i \ "s|/usr/src|${source_root}/src|g" {} + 2>/dev/null || true find "${stage_cmake_dir}" -name '*.cmake' -exec sed -i \ "s|${source_root}/src/.*/meta_types/|${sysroot}/metatypes/|g" {} + 2>/dev/null || true } redbear_qt_rewrite_usr_src_metatype_paths() { local stage_cmake_dir="$1" local sysroot="$2" find "${stage_cmake_dir}" -name '*.cmake' -exec sed -i \ "s|/usr/src/.*/meta_types/|${sysroot}/metatypes/|g" {} + 2>/dev/null || true } redbear_qt_rewrite_stage_path_literal() { local stage_cmake_dir="$1" local from_path="$2" local to_path="$3" find "${stage_cmake_dir}" -name '*.cmake' -exec sed -i \ "s|${from_path}|${to_path}|g" {} + 2>/dev/null || true } redbear_qt_copy_stage_qt6_cmake_to_sysroot() { local stage_usr="$1" local sysroot="$2" mkdir -p "${sysroot}/lib/cmake" cp -a "${stage_usr}/lib/cmake/Qt6"* "${sysroot}/lib/cmake/" 2>/dev/null || true } redbear_qt_copy_optional_stage_dir_to_sysroot() { local stage_usr="$1" local sysroot="$2" local dir_name="$3" if [ -d "${stage_usr}/${dir_name}" ]; then mkdir -p "${sysroot}/${dir_name}" cp -a "${stage_usr}/${dir_name}/"* "${sysroot}/${dir_name}/" 2>/dev/null || true fi } redbear_qt_resolve_forwarding_headers() { local stage_usr="$1" local build_dir="$2" find "${stage_usr}/include" -name '*.h' -exec grep -l "${build_dir}" {} \; 2>/dev/null | while read -r fwd_header; do local build_path build_path=$(grep '#include "' "${fwd_header}" | head -1 | sed 's/#include "\([^"]*\)".*/\1/') if [ -n "${build_path}" ] && [ -f "${build_path}" ]; then cp "${build_path}" "${fwd_header}" fi done } redbear_qt_ensure_dep_sysroots() { local cookbook_root="$1" local sysroot="$2" local cmake_dir="${sysroot}/usr/lib/cmake" [ -d "${cmake_dir}" ] || cmake_dir="${sysroot}/lib/cmake" [ -d "${cmake_dir}" ] || return 0 # Qt cmake macros record absolute sysroot paths during build. When the # cookbook rebuilds a later recipe, the earlier recipe's sysroot may have # been cleaned, leaving dangling references. We recreate each missing # directory as a symlink to the dependency's own stage. grep -roh "${cookbook_root}/local/recipes/[^ \";]*target/x86_64-unknown-redox/sysroot" "${cmake_dir}" 2>/dev/null | sort -u | while read -r dep_sysroot; do local dep_target_dir dep_stage_usr dep_target_dir=$(dirname "${dep_sysroot}") dep_stage_usr="${dep_target_dir}/stage/usr" [ -d "${dep_stage_usr}" ] || continue for qt_dir in include lib metatypes plugins mkspecs modules qml; do if [ -d "${dep_sysroot}/${qt_dir}" ] && [ ! -L "${dep_sysroot}/${qt_dir}" ]; then continue fi [ -d "${dep_stage_usr}/${qt_dir}" ] || continue # Already correct? Do nothing. This is the common case and it must # NOT churn the link: this function repairs OTHER recipes' sysroots, # and with COOKBOOK_COOK_JOBS>1 a concurrent cook may be compiling # against that very directory right now. if [ -L "${dep_sysroot}/${qt_dir}" ] \ && [ "$(readlink "${dep_sysroot}/${qt_dir}")" = "${dep_stage_usr}/${qt_dir}" ]; then continue fi # Replace ATOMICALLY. The previous code did `rm -f` then `ln -sf`, # leaving a window in which the path did not exist at all. A # parallel cook compiling against it died with, e.g. # cstdio:47: fatal error: .../qtdeclarative/.../sysroot/include/ # stdio.h: No such file or directory # (kirigami, while another recipe relinked qtdeclarative's sysroot). # `mv -T` over an existing symlink is a single rename(2): readers # see either the old link or the new one, never nothing. mkdir -p "${dep_sysroot}" local _tmp_link="${dep_sysroot}/.${qt_dir}.relink.$$" rm -f "${_tmp_link}" if ln -s "${dep_stage_usr}/${qt_dir}" "${_tmp_link}" 2>/dev/null; then mv -Tf "${_tmp_link}" "${dep_sysroot}/${qt_dir}" 2>/dev/null || rm -f "${_tmp_link}" fi done done }