7ae9646968
redbear_qt_ensure_dep_sysroots repairs OTHER recipes' sysroots, so with
COOKBOOK_COOK_JOBS>1 it runs while a parallel cook is compiling against the
very directory it is relinking. It did `rm -f` then `ln -sf`, leaving a window
where the path did not exist. kirigami died in that window:
cstdio:47: fatal error:
.../qtdeclarative/target/.../sysroot/include/stdio.h: No such file
with another cook's `rm -f`/`ln -sf` on qtdeclarative's sysroot interleaved in
the same log.
Now: skip entirely when the link already points at the right target (the
common case, and the cheapest way to stop the churn), otherwise replace via
ln -s to a temp name + `mv -T`, a single rename(2) -- readers see the old link
or the new one, never nothing.
Verified with 300 concurrent relinks against 3000 probes: 0 missing
observations, where the old sequence reproduced the gap.
236 lines
8.9 KiB
Bash
236 lines
8.9 KiB
Bash
#!/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
|
|
# <sysroot>/plugins (the _IMPORT_PREFIX is computed from the cmake files under
|
|
# <sysroot>/lib/cmake, i.e. the sysroot root, NOT <sysroot>/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 "<sysroot>/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 -> <prefix>/qml (QT_INSTALL_QML)
|
|
# KDE -> <prefix>/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
|
|
# <qt-host-build>/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
|
|
}
|