From fd11aa716c4e1928ef2381d086eb0f94ec9e9ca6 Mon Sep 17 00:00:00 2001 From: vasilito Date: Tue, 4 Aug 2026 10:06:39 +0300 Subject: [PATCH] feat: port the missing KDE Plasma dependency chain and harden the auth stack Unblocks plasma-workspace/plasma-desktop, which required 14 packages that had no in-tree recipe. Sources are reproducible from each recipe's tar= + blake3; the vendored source/ trees are deliberately not committed here (212M). New recipes: KF6 6.28.0 kf6-kholidays, kf6-krunner, kf6-kstatusnotifieritem, kf6-kunitconversion Plasma 6.7.2 knighttime, layer-shell-qt, libkscreen, libksysguard, plasma-activities-stats, plasma5support, kscreenlocker Qt 6.11.1 qtpositioning, qtspeech, qttools libksysguard carries P0-redox-process-backend.patch: processes_local_p.cpp dispatches on platform macros and had no __redox__ arm, so ProcessesLocal was entirely undefined. Adds a real backend reading /scheme/proc/ps (pid, ppid, real+effective ids, thread count, state) and /scheme/sys/mem, with kill() for signals. Upstream's generic fallback is a pure stub and was not used. Absent facilities (no setpriority/sched_setscheduler/ioprio on a microkernel) report NotSupported rather than pretending. Restored, no longer disabled: - night colour: kcms/nighttime + kwin's nightlight plugin, now that KNightTime/Qt6Positioning/KF6Holidays exist - KIO FileWidgets (file dialog, places model), wrongly swept in with the unportable kiod/kssld/kioworkers subdirs - kf6-ktexteditor text-to-speech: removes a disguised stub that rewrote speechEngine() to return nullptr and mangled call sites into invalid C++ - kwin KWIN_BUILD_SCREENLOCKER=ON (needs kscreenlocker; see below) Toolchain and recipe fixes: - redox-toolchain.cmake: append -lgcc. __extendhfsf2/__extendbfsf2 are global in libgcc.a but hidden in libgcc_s.so (the Redox libgcc version map stops at GCC_7.0.0 and never emits the GCC_12/13 nodes upstream exports), and GCC omits -lgcc for -shared, so no C++23 shared library using std::format<_Float16|bfloat16_t> could link. - qtmultimedia: rewrite /usr/src/.../meta_types paths and stage metatypes, so consumers using qt_internal_add_qml_module can configure. - qtspeech: qtmultimedia is mandatory, not optional -- upstream return()s with only a NOTICE without it, yielding a green build over an empty package. Asserts its own output so that cannot recur. - narrow Linux-only guards with AND NOT REDOX where the toolchain sets CMAKE_SYSTEM_NAME=Linux purely to get UNIX=TRUE (kio LibMount, libksysguard NL/Sensors, plasma-workspace NetworkManagerQt). - drop REQUIRED components that are declared but referenced nowhere (Location, QCoro6, Qt6 Test) -- verified by exhaustive grep. - DESTDIR installs where KDE emits absolute KDE_INSTALL_FULL_* paths that --prefix cannot re-root, which otherwise write into the build host. Auth stack: - pam-redbear: add PAM_MODULE_UNKNOWN (28), missing from both the header and lib.rs; realign 30/31 to Linux-PAM's PAM_CONV_AGAIN/PAM_INCOMPLETE, which previously held Red Bear-only names on standard values, so anything built against stock PAM headers mis-decoded them. - redbear-authd: support yescrypt ($y$ -- the default shadow format on current Debian/Ubuntu/Fedora, previously an unexplained login failure), bcrypt and md5-crypt. Plaintext shadow entries now require the /etc/redbear/allow-plaintext-passwords sentinel and warn on every use, instead of being compared silently. 19/19 host tests pass. Build-system hardening: - verify-tracked-sources.sh, wired into preflight: fails on deletion of any tracked vendored source, and on modifications with no baseline entry. local/sources/ had a dirty gate and version checks; local/recipes/*/source/ had none, which allowed an rm -rf to delete 7958 tracked files and a pristine re-extract to silently overwrite committed fixes (kwin's std::expected/vulkan-hpp and X11 gating, kio's Q_OS_REDOX resolver guards). - validate-source-trees.py: resolve recipe.toml through the overlay symlink; it reported false MISSING for symlinked recipes. - test-sddm-virgl-qemu.sh: the null-proxy check used report(), which PASSES on regex match -- so the known Qt6 Wayland null+8 fault would report PASS when present. Added report_absent(). kscreenlocker builds only its cmake-level Wayland-only changes so far; the C++ X11 removal (61 call sites) is not yet applied, so kwin's screenlocker flag is blocked on it. --- config/redbear-full.toml | 19 +- local/recipes/kde/kf6-kholidays/recipe.toml | 61 +++ local/recipes/kde/kf6-kio/recipe.toml | 62 ++- local/recipes/kde/kf6-krunner/recipe.toml | 56 +++ .../kde/kf6-kstatusnotifieritem/recipe.toml | 60 +++ local/recipes/kde/kf6-ktexteditor/recipe.toml | 95 ++-- .../kde/kf6-kunitconversion/recipe.toml | 56 +++ local/recipes/kde/knighttime/recipe.toml | 62 +++ local/recipes/kde/kscreenlocker/recipe.toml | 138 ++++++ local/recipes/kde/kwin/recipe.toml | 62 ++- local/recipes/kde/layer-shell-qt/recipe.toml | 58 +++ local/recipes/kde/libkscreen/recipe.toml | 119 +++++ .../P0-redox-process-backend.patch | 437 ++++++++++++++++++ local/recipes/kde/libksysguard/recipe.toml | 98 ++++ .../kde/plasma-activities-stats/recipe.toml | 56 +++ local/recipes/kde/plasma-desktop/recipe.toml | 9 + .../recipes/kde/plasma-workspace/recipe.toml | 69 +++ local/recipes/kde/plasma5support/recipe.toml | 72 +++ .../source/include/security/pam_appl.h | 12 +- .../libs/pam-redbear/source/src/lib.rs | 15 +- local/recipes/qt/qtmultimedia/recipe.toml | 16 + local/recipes/qt/qtpositioning/recipe.toml | 101 ++++ local/recipes/qt/qtspeech/recipe.toml | 130 ++++++ local/recipes/qt/qttools/recipe.toml | 108 +++++ local/recipes/qt/redox-toolchain.cmake | 25 + .../system/redbear-authd/source/Cargo.lock | 340 +++++++++++++- .../system/redbear-authd/source/Cargo.toml | 6 + .../system/redbear-authd/source/src/main.rs | 119 ++++- local/scripts/build-preflight.sh | 20 + local/scripts/test-sddm-virgl-qemu.sh | 20 +- local/scripts/validate-source-trees.py | 8 +- local/scripts/verify-tracked-sources.sh | 222 +++++++++ local/tracked-source-baseline.txt | 45 ++ 33 files changed, 2695 insertions(+), 81 deletions(-) create mode 100644 local/recipes/kde/kf6-kholidays/recipe.toml create mode 100644 local/recipes/kde/kf6-krunner/recipe.toml create mode 100644 local/recipes/kde/kf6-kstatusnotifieritem/recipe.toml create mode 100644 local/recipes/kde/kf6-kunitconversion/recipe.toml create mode 100644 local/recipes/kde/knighttime/recipe.toml create mode 100644 local/recipes/kde/kscreenlocker/recipe.toml create mode 100644 local/recipes/kde/layer-shell-qt/recipe.toml create mode 100644 local/recipes/kde/libkscreen/recipe.toml create mode 100644 local/recipes/kde/libksysguard/P0-redox-process-backend.patch create mode 100644 local/recipes/kde/libksysguard/recipe.toml create mode 100644 local/recipes/kde/plasma-activities-stats/recipe.toml create mode 100644 local/recipes/kde/plasma5support/recipe.toml create mode 100644 local/recipes/qt/qtpositioning/recipe.toml create mode 100644 local/recipes/qt/qtspeech/recipe.toml create mode 100644 local/recipes/qt/qttools/recipe.toml create mode 100755 local/scripts/verify-tracked-sources.sh create mode 100644 local/tracked-source-baseline.txt diff --git a/config/redbear-full.toml b/config/redbear-full.toml index ea12d24fc2..6590a9983b 100644 --- a/config/redbear-full.toml +++ b/config/redbear-full.toml @@ -40,7 +40,7 @@ packages = ["qtbase", "qtsvg"] [package_groups.qt6-extras] description = "Qt 6 additional modules (Wayland, sensors)" -packages = ["qtwayland", "qt6-wayland-smoke", "qt6-sensors"] +packages = ["qtwayland", "qt6-wayland-smoke", "qt6-sensors", "qtpositioning", "qtspeech", "qtmultimedia"] [package_groups.kf6-frameworks] description = "KDE Frameworks 6 (full set required for KDE Plasma)" @@ -57,6 +57,8 @@ packages = [ "kf6-prison", "kf6-pty", "kf6-solid", "kf6-sonnet", "kf6-syntaxhighlighting", "kf6-knewstuff", "kf6-kwallet", "kf6-kglobalaccel", + "kf6-kholidays", "kf6-krunner", "kf6-kstatusnotifieritem", + "kf6-ktexteditor", "kf6-kunitconversion", ] [package_groups.desktop-session] @@ -209,6 +211,21 @@ kf6-syntaxhighlighting = {} kf6-knewstuff = {} kf6-kwallet = {} kf6-kglobalaccel = {} +kf6-kholidays = {} +kf6-krunner = {} +kf6-kstatusnotifieritem = {} +kf6-ktexteditor = {} +qtpositioning = {} +qtspeech = {} +qtmultimedia = {} +layer-shell-qt = {} +libkscreen = {} +knighttime = {} +plasma-activities-stats = {} +kf6-kunitconversion = {} +plasma5support = {} +libksysguard = {} +kscreenlocker = {} kirigami = {} breeze = {} diff --git a/local/recipes/kde/kf6-kholidays/recipe.toml b/local/recipes/kde/kf6-kholidays/recipe.toml new file mode 100644 index 0000000000..f2f07a9eb3 --- /dev/null +++ b/local/recipes/kde/kf6-kholidays/recipe.toml @@ -0,0 +1,61 @@ +[package] +name = "kf6-kholidays" +version = "6.28" + +#TODO: KHolidays — holiday/observance calculation framework. Required by +# plasma-workspace (KF6 REQUIRED COMPONENTS ... Holidays) and by knighttime. +# Depends on qtbase, qtdeclarative, kf6-extra-cmake-modules. Its holiday-file +# parser is generated at build time by bison + flex, which run on the HOST. +[source] +tar = "https://download.kde.org/stable/frameworks/6.28/kholidays-6.28.0.tar.xz" +blake3 = "64c0c4f9fa4a0106bc02a3860e4bb184608fb518935b0a4ddd49767ec9afeb7d" + +[build] +template = "custom" +dependencies = [ + "qtbase", + "qtdeclarative", + "kf6-extra-cmake-modules", +] +script = """ +DYNAMIC_INIT + +HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build" + +for qtdir in plugins mkspecs metatypes modules qml; do + if [ -d "${COOKBOOK_SYSROOT}/usr/${qtdir}" ] && [ ! -e "${COOKBOOK_SYSROOT}/${qtdir}" ]; then + ln -s "usr/${qtdir}" "${COOKBOOK_SYSROOT}/${qtdir}" + fi +done + +# bison/flex generate the holiday-file parser. They are BUILD-TIME code +# generators producing arch-independent C++, so they must be the host binaries. +# The cross pkg-config/sysroot must not be consulted for them. +if [ ! -x /usr/bin/bison ] || [ ! -x /usr/bin/flex ]; then + echo "ERROR: kf6-kholidays needs host bison and flex to generate its parser" >&2 + exit 1 +fi + +rm -f CMakeCache.txt +rm -rf CMakeFiles + +cmake "${COOKBOOK_SOURCE}" \ + -DKF_SKIP_PO_PROCESSING=ON \ + -DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \ + -DQT_HOST_PATH="${HOST_BUILD}" \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \ + -DBISON_EXECUTABLE=/usr/bin/bison \ + -DFLEX_EXECUTABLE=/usr/bin/flex \ + -DBUILD_TESTING=OFF \ + -DBUILD_QCH=OFF \ + -Wno-dev +cmake --build . -j${COOKBOOK_MAKE_JOBS} +cmake --install . --prefix "${COOKBOOK_STAGE}/usr" + +for lib in "${COOKBOOK_STAGE}/usr/lib/"libKF6*.so.*; do + [ -f "${lib}" ] || continue + patchelf --remove-rpath "${lib}" 2>/dev/null || true +done +""" diff --git a/local/recipes/kde/kf6-kio/recipe.toml b/local/recipes/kde/kf6-kio/recipe.toml index 5883a03eff..34cb192689 100644 --- a/local/recipes/kde/kf6-kio/recipe.toml +++ b/local/recipes/kde/kf6-kio/recipe.toml @@ -37,6 +37,12 @@ dependencies = [ "kf6-knotifications", "kf6-kcrash", "kf6-solid", + # Required by KF6KIOFileWidgets (src/filewidgets/CMakeLists.txt:103): + # KF6::GuiAddons (KIconUtils) and KF6::ColorScheme. Both are built and in + # the config already, but without an explicit dep they are never staged into + # kio's sysroot. + "kf6-kguiaddons", + "kf6-kcolorscheme", ] script = """ DYNAMIC_INIT @@ -77,12 +83,60 @@ if ! grep -q 'hostinfo.cpp' "${COOKBOOK_SOURCE}/src/core/CMakeLists.txt" 2>/dev/ "${COOKBOOK_SOURCE}/src/core/CMakeLists.txt" 2>/dev/null || true fi -# Redox only needs the core/gui/widgets libraries here; skip worker/daemon and -# extra runtime/plugin subdirectories that still depend on unsupported Qt SSL, -# SysV IPC, and broader runtime surfaces. -sed -i '/add_subdirectory(kioworkers)/d; /add_subdirectory(schemehandlers)/d; /add_subdirectory(kiod)/d; /add_subdirectory(kssld)/d; /add_subdirectory(kpasswdserver)/d; /add_subdirectory(kioexec)/d; /add_subdirectory(filewidgets)/d; /add_subdirectory(urifilters)/d' \ +# Skip the worker/daemon and extra runtime/plugin subdirectories that still +# depend on unsupported Qt SSL, SysV IPC, and broader runtime surfaces. +# +# filewidgets is deliberately NOT in this list any more. It used to be, under +# the assumption that "Redox only needs the core/gui/widgets libraries" -- the +# desktop build disproved that: plasma5support's places dataengine links +# KF6::KIOFileWidgets (src/dataengines/places/CMakeLists.txt:13) and cmake +# aborts without it, and plasma-desktop pulls plasma5support in turn. +# +# Unlike the entries that remain, filewidgets is a plain widget library +# (KFilePlacesModel/KFilePlacesView, the file dialog) with no SSL, daemon or +# SysV IPC surface -- it was simply swept up with the daemon subdirectories. +# Every library it links is already built and staged: KIOWidgets, Bookmarks + +# BookmarksWidgets, ItemViews, Solid, GuiAddons, IconThemes + IconWidgets, +# I18n, ConfigGui, ColorScheme. +sed -i '/add_subdirectory(kioworkers)/d; /add_subdirectory(schemehandlers)/d; /add_subdirectory(kiod)/d; /add_subdirectory(kssld)/d; /add_subdirectory(kpasswdserver)/d; /add_subdirectory(kioexec)/d; /add_subdirectory(urifilters)/d' \ "${COOKBOOK_SOURCE}/src/CMakeLists.txt" 2>/dev/null || true +# Restore add_subdirectory(filewidgets). +# +# Simply dropping filewidgets from the deletion list above is NOT enough: this +# recipe's seds rewrite the VENDORED, GIT-TRACKED source in place, and at some +# point that stripped state was committed. HEAD's src/CMakeLists.txt therefore +# no longer contains the line at all, so there is nothing left for a removed +# sed to spare -- it has to be put back explicitly. +# +# (That is also a good illustration of why builds should not mutate tracked +# source trees; see local/scripts/verify-tracked-sources.sh.) +# +# Upstream places it immediately after add_subdirectory(widgets), inside the +# `if (NOT KIOGUI_ONLY)` block, which is the nesting reproduced here. +# Idempotent via the grep guard, matching the Qt6::Network insert below. +if ! grep -q 'add_subdirectory(filewidgets)' "${COOKBOOK_SOURCE}/src/CMakeLists.txt" 2>/dev/null; then + sed -i '/^ add_subdirectory(widgets)$/a\\ add_subdirectory(filewidgets)' \ + "${COOKBOOK_SOURCE}/src/CMakeLists.txt" 2>/dev/null || true +fi + +# Narrow the Linux-only LibMount requirement (CMakeLists.txt:138-140): +# if (CMAKE_SYSTEM_NAME MATCHES "Linux") +# find_package(LibMount REQUIRED) +# LibMount is util-linux's libmount, used by KMountPoint to read the Linux +# mount table. Redox has no such table -- mounts are schemes -- so the library +# is neither present nor meaningful here, and HAVE_LIB_MOUNT correctly stays +# unset (KMountPoint then falls back to its non-libmount path). +# +# As with libksysguard's NL/Sensors block and plasma-workspace's +# `if (LINUX)` NetworkManagerQt guard, this fires only because our cmake +# toolchain deliberately sets CMAKE_SYSTEM_NAME=Linux to get UNIX=TRUE for Qt's +# Unix source selection (local/recipes/qt/redox-toolchain.cmake). REDOX is a +# CACHE INTERNAL marker set by that same toolchain. +# Idempotent: after rewriting, the original pattern no longer matches. +sed -i 's/^if (CMAKE_SYSTEM_NAME MATCHES "Linux")$/if (CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT REDOX)/' \ + "${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true + # KIOGui uses QHostInfo in full builds, so import QtNetwork on the GUI target. if ! grep -q 'Qt6::Network' "${COOKBOOK_SOURCE}/src/gui/CMakeLists.txt" 2>/dev/null; then sed -i '/Qt6::Gui/a\\ Qt6::Network' \ diff --git a/local/recipes/kde/kf6-krunner/recipe.toml b/local/recipes/kde/kf6-krunner/recipe.toml new file mode 100644 index 0000000000..6d98f777a9 --- /dev/null +++ b/local/recipes/kde/kf6-krunner/recipe.toml @@ -0,0 +1,56 @@ +[package] +name = "kf6-krunner" +version = "6.28" + +#TODO: KRunner — the parallelised query/result framework behind KRunner and the +# Plasma search field. Required by plasma-workspace +# (KF6 REQUIRED COMPONENTS ... Runner). +[source] +tar = "https://download.kde.org/stable/frameworks/6.28/krunner-6.28.0.tar.xz" +blake3 = "1c7f286a29132fb13eb37281820424602d159217b32064f8f09f113cdf4746e0" + +[build] +template = "custom" +dependencies = [ + "qtbase", + "qtdeclarative", + "kf6-extra-cmake-modules", + "kf6-kconfig", + "kf6-kcoreaddons", + "kf6-ki18n", + "kf6-kwindowsystem", + "kf6-kitemmodels", +] +script = """ +DYNAMIC_INIT + +HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build" + +for qtdir in plugins mkspecs metatypes modules qml; do + if [ -d "${COOKBOOK_SYSROOT}/usr/${qtdir}" ] && [ ! -e "${COOKBOOK_SYSROOT}/${qtdir}" ]; then + ln -s "usr/${qtdir}" "${COOKBOOK_SYSROOT}/${qtdir}" + fi +done + +rm -f CMakeCache.txt +rm -rf CMakeFiles + +cmake "${COOKBOOK_SOURCE}" \ + -DKF_SKIP_PO_PROCESSING=ON \ + -DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \ + -DQT_HOST_PATH="${HOST_BUILD}" \ + -DKF6_HOST_TOOLING=/usr/lib/cmake \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \ + -DBUILD_TESTING=OFF \ + -DBUILD_QCH=OFF \ + -Wno-dev +cmake --build . -j${COOKBOOK_MAKE_JOBS} +cmake --install . --prefix "${COOKBOOK_STAGE}/usr" + +for lib in "${COOKBOOK_STAGE}/usr/lib/"libKF6*.so.*; do + [ -f "${lib}" ] || continue + patchelf --remove-rpath "${lib}" 2>/dev/null || true +done +""" diff --git a/local/recipes/kde/kf6-kstatusnotifieritem/recipe.toml b/local/recipes/kde/kf6-kstatusnotifieritem/recipe.toml new file mode 100644 index 0000000000..bd74873454 --- /dev/null +++ b/local/recipes/kde/kf6-kstatusnotifieritem/recipe.toml @@ -0,0 +1,60 @@ +[package] +name = "kf6-kstatusnotifieritem" +version = "6.28" + +#TODO: KStatusNotifierItem — the StatusNotifierItem (system tray) implementation. +# Required by plasma-workspace (KF6 REQUIRED COMPONENTS ... StatusNotifierItem). +[source] +tar = "https://download.kde.org/stable/frameworks/6.28/kstatusnotifieritem-6.28.0.tar.xz" +blake3 = "4496a161f4acc1151616925874957184b1ab48b056a0e007b8f89e3e579b7b27" + +[build] +template = "custom" +dependencies = [ + "qtbase", + "qtdeclarative", + "kf6-extra-cmake-modules", + "kf6-kwindowsystem", + "dbus", +] +script = """ +DYNAMIC_INIT + +HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build" + +for qtdir in plugins mkspecs metatypes modules qml; do + if [ -d "${COOKBOOK_SYSROOT}/usr/${qtdir}" ] && [ ! -e "${COOKBOOK_SYSROOT}/${qtdir}" ]; then + ln -s "usr/${qtdir}" "${COOKBOOK_SYSROOT}/${qtdir}" + fi +done + +rm -f CMakeCache.txt +rm -rf CMakeFiles + +# BUILD_PYTHON_BINDINGS defaults ON (CMakeLists.txt:75) and then hard-REQUIREs +# Python3 + Shiboken6 + PySide6. Those are HOST Python tooling for optional +# language bindings, are not ported to Redox, and produce no C++ library +# artifact that plasma-workspace consumes -- it links only the C++ +# KF6StatusNotifierItem target. Upstream already turns them off for every +# non-Linux/FreeBSD platform (CMakeLists.txt:78); Redox is simply not in that +# list. This is an upstream-supported option, not a source edit. +cmake "${COOKBOOK_SOURCE}" \ + -DKF_SKIP_PO_PROCESSING=ON \ + -DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \ + -DQT_HOST_PATH="${HOST_BUILD}" \ + -DKF6_HOST_TOOLING=/usr/lib/cmake \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \ + -DBUILD_PYTHON_BINDINGS=OFF \ + -DBUILD_TESTING=OFF \ + -DBUILD_QCH=OFF \ + -Wno-dev +cmake --build . -j${COOKBOOK_MAKE_JOBS} +cmake --install . --prefix "${COOKBOOK_STAGE}/usr" + +for lib in "${COOKBOOK_STAGE}/usr/lib/"libKF6*.so.*; do + [ -f "${lib}" ] || continue + patchelf --remove-rpath "${lib}" 2>/dev/null || true +done +""" diff --git a/local/recipes/kde/kf6-ktexteditor/recipe.toml b/local/recipes/kde/kf6-ktexteditor/recipe.toml index c5559e1982..f03fb9188a 100644 --- a/local/recipes/kde/kf6-ktexteditor/recipe.toml +++ b/local/recipes/kde/kf6-ktexteditor/recipe.toml @@ -16,6 +16,9 @@ path = "source" template = "custom" dependencies = [ "qtbase", + "qtdeclarative", + # CMakeLists.txt:57 REQUIREs the TextToSpeech Qt component with no cmake gate. + "qtspeech", "kf6-extra-cmake-modules", "kf6-karchive", "kf6-kconfig", @@ -34,46 +37,54 @@ source "${COOKBOOK_ROOT}/local/scripts/lib/qt-sysroot.sh" redbear_qt_link_sysroot_dirs "${COOKBOOK_SYSROOT}" plugins mkspecs metatypes modules qml -# Disable QTextToSpeech include unconditionally in all files -for f in "${COOKBOOK_SOURCE}/src/view/kateview.cpp" "${COOKBOOK_SOURCE}/src/utils/kateglobal.cpp" "${COOKBOOK_SOURCE}/src/utils/kateglobal.h"; do - [ -f "$f" ] || continue -done -# Disable speechEngine usage in kateview.cpp and kateglobal.cpp -python3 - <<'PY' -import os -# Replace the entire speechEngine function body to return nullptr -p = os.environ["COOKBOOK_SOURCE"] + "/src/utils/kateglobal.cpp" -text = open(p).read() -old_marker = "QTextToSpeech *KTextEditor::EditorPrivate::speechEngine(" -idx = text.find(old_marker) -if idx >= 0: - # Find the opening { and the matching } - brace_count = 0 - started = False - end_idx = idx - for i in range(idx, len(text)): - if text[i] == '{': - brace_count += 1 - started = True - elif text[i] == '}': - brace_count -= 1 - if started and brace_count == 0: - end_idx = i + 1 - break - if end_idx > idx: - replacement = "QTextToSpeech *KTextEditor::EditorPrivate::speechEngine(KTextEditor::ViewPrivate *view) { Q_UNUSED(view); return nullptr; }" - text = text[:idx] + replacement + text[end_idx:] -open(p, "w").write(text) -# In kateview.cpp, also handle lambda functions that use speechEngine -p2 = os.environ["COOKBOOK_SOURCE"] + "/src/view/kateview.cpp" -text2 = open(p2).read() -text2 = text2.replace("EditorPrivate::self()->speechEngine(this)->", "if (false) (QTextToSpeech*)nullptr->") -open(p2, "w").write(text2) -PY +# REMOVED: the QTextToSpeech stubbing block. +# +# This recipe used to neuter text-to-speech because qtspeech had no in-tree +# recipe: it rewrote EditorPrivate::speechEngine() to `return nullptr` and did +# text2.replace("EditorPrivate::self()->speechEngine(this)->", +# "if (false) (QTextToSpeech*)nullptr->") +# in kateview.cpp. That is a disguised stub of exactly the kind +# local/AGENTS.md § "STUB AND WORKAROUND POLICY" forbids, and it was also +# BROKEN: the original text is `KTextEditor::EditorPrivate::self()->...`, so +# replacing only the middle left `KTextEditor::if (false) (QTextToSpeech*)...` +# -- not valid C++. It never showed up because kf6-ktexteditor was not in any +# config until plasma-workspace pulled it in, so the file was never compiled. +# +# qtspeech is now a real port (local/recipes/qt/qtspeech, providing +# libQt6TextToSpeech + Qt6TextToSpeechConfig.cmake), so the stub is obsolete in +# both directions: unnecessary AND wrong. ktexteditor now builds against the +# genuine QTextToSpeech API and the speechEngine() implementation is upstream's. rm -f CMakeCache.txt rm -rf CMakeFiles +# kauth-policy-gen is a BUILD-TIME code generator: it reads an .actions file and +# emits a PolicyKit .policy file. KF6Auth exports it as the imported target +# KF6::kauth-policy-gen whose IMPORTED_LOCATION is the TARGET (Redox) binary in +# the sysroot, so cmake tries to execute a Redox ELF on the build host: +# .../sysroot/lib/libexec/kf6/kauth/kauth-policy-gen: cannot execute: +# required file not found +# +# Same host/target confusion, and the same remedy, as wayland-scanner in +# local/recipes/wayland/xwayland/recipe.toml: overlay the HOST binary onto the +# sysroot path the imported target points at. The generator's output is plain +# XML, so a host build of it is equivalent -- and this keeps KAuth ENABLED +# (privileged "save as root" integration) rather than switching ENABLE_KAUTH +# off and losing the feature. +for _hostgen in /usr/lib/kf6/kauth/kauth-policy-gen /usr/lib/kauth/kauth-policy-gen; do + if [ -x "$_hostgen" ]; then + mkdir -p "${COOKBOOK_SYSROOT}/lib/libexec/kf6/kauth" + cp -f "$_hostgen" "${COOKBOOK_SYSROOT}/lib/libexec/kf6/kauth/kauth-policy-gen" + break + fi +done +if ! [ -x "${COOKBOOK_SYSROOT}/lib/libexec/kf6/kauth/kauth-policy-gen" ]; then + echo "ERROR: no host kauth-policy-gen found; KAuth policy generation cannot run." >&2 + echo " Install the host KF6 Auth dev tooling, or build with -DENABLE_KAUTH=OFF" >&2 + echo " (which drops ktexteditor's privileged-save integration)." >&2 + exit 1 +fi + cmake "${COOKBOOK_SOURCE}" \ -DKF_SKIP_PO_PROCESSING=ON \\ -DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \\ @@ -87,7 +98,17 @@ cmake "${COOKBOOK_SOURCE}" \ -Wno-dev cmake --build . -j${COOKBOOK_MAKE_JOBS} -cmake --install . --prefix "${COOKBOOK_STAGE}/usr" +# DESTDIR, not --prefix. With KAuth enabled, kauth_install_actions() installs the +# PolicyKit action to an ABSOLUTE destination (${POLKIT_ACTIONS_INSTALL_DIR} = +# /usr/share/polkit-1/actions). `--prefix` only re-roots RELATIVE destinations, +# so cmake tried to write into the BUILD HOST's filesystem: +# file INSTALL cannot copy ... to +# "/usr/share/polkit-1/actions/org.kde.ktexteditor6.katetextbuffer.policy": +# Permission denied. +# Only the host's permissions prevented that from polluting the host /usr. +# DESTDIR re-roots absolute destinations too. Same approach as +# local/recipes/kde/kde-cli-tools, sddm, and the other KDE recipes here. +DESTDIR="${COOKBOOK_STAGE}" cmake --install . --prefix /usr for lib in "${COOKBOOK_STAGE}/usr/lib/"libKF6*.so.*; do [ -f "${lib}" ] || continue diff --git a/local/recipes/kde/kf6-kunitconversion/recipe.toml b/local/recipes/kde/kf6-kunitconversion/recipe.toml new file mode 100644 index 0000000000..3d73f8dfd8 --- /dev/null +++ b/local/recipes/kde/kf6-kunitconversion/recipe.toml @@ -0,0 +1,56 @@ +[package] +name = "kf6-kunitconversion" +version = "6.28" + +#TODO: KUnitConversion — physical-unit conversion framework. Required by +# plasma5support (KF6 REQUIRED COMPONENTS ... UnitConversion, for the weather +# data engine), which is in turn REQUIRED by plasma-desktop. +[source] +tar = "https://download.kde.org/stable/frameworks/6.28/kunitconversion-6.28.0.tar.xz" +blake3 = "5a0555b9efaf9ad32472e5169822038f5cea17fbc39f4615e37f022ad9371511" + +[build] +template = "custom" +dependencies = [ + "qtbase", + "qtdeclarative", + "kf6-extra-cmake-modules", + "kf6-ki18n", +] +script = """ +DYNAMIC_INIT + +HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build" + +for qtdir in plugins mkspecs metatypes modules qml; do + if [ -d "${COOKBOOK_SYSROOT}/usr/${qtdir}" ] && [ ! -e "${COOKBOOK_SYSROOT}/${qtdir}" ]; then + ln -s "usr/${qtdir}" "${COOKBOOK_SYSROOT}/${qtdir}" + fi +done + +rm -f CMakeCache.txt +rm -rf CMakeFiles + +# BUILD_PYTHON_BINDINGS pulls host Python3 + Shiboken6 + PySide6 for optional +# language bindings that are not ported and that no C++ consumer links. +# Same rationale as kf6-kstatusnotifieritem. +cmake "${COOKBOOK_SOURCE}" \ + -DKF_SKIP_PO_PROCESSING=ON \ + -DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \ + -DQT_HOST_PATH="${HOST_BUILD}" \ + -DKF6_HOST_TOOLING=/usr/lib/cmake \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \ + -DBUILD_PYTHON_BINDINGS=OFF \ + -DBUILD_TESTING=OFF \ + -DBUILD_QCH=OFF \ + -Wno-dev +cmake --build . -j${COOKBOOK_MAKE_JOBS} +cmake --install . --prefix "${COOKBOOK_STAGE}/usr" + +for lib in "${COOKBOOK_STAGE}/usr/lib/"libKF6*.so.*; do + [ -f "${lib}" ] || continue + patchelf --remove-rpath "${lib}" 2>/dev/null || true +done +""" diff --git a/local/recipes/kde/knighttime/recipe.toml b/local/recipes/kde/knighttime/recipe.toml new file mode 100644 index 0000000000..7503101def --- /dev/null +++ b/local/recipes/kde/knighttime/recipe.toml @@ -0,0 +1,62 @@ +[package] +name = "knighttime" +version = "6.7.2" + +#TODO: KNightTime — solar-schedule library (sunrise/sunset) behind the +# night-colour feature. Consumed by plasma-workspace's kcms/nighttime and by +# kwin's nightlight plugin, both of which were previously deferred precisely +# because this package (and its Qt6Positioning / KF6Holidays dependencies) had +# no in-tree recipe. Those two deferrals can be lifted once this builds. +# +# NOTE (verified): this is a PLASMA component versioned with Plasma (6.7.x), not +# a KDE Framework. The frameworks URL returns HTTP 200 with an HTML error page, +# which silently yields a ~13KB junk "tarball" -- see the warning in +# local/recipes/kde/kwin/recipe.toml. +[source] +tar = "https://download.kde.org/stable/plasma/6.7.2/knighttime-6.7.2.tar.xz" +blake3 = "27b06ec4545f732d0c44934b2a3d6e9359bd5e90e94627377accf2b69725d677" + +[build] +template = "custom" +dependencies = [ + "qtbase", + "qtdeclarative", + "qtpositioning", + "kf6-extra-cmake-modules", + # CMakeLists.txt:46 REQUIRED COMPONENTS: Config CoreAddons DBusAddons Holidays I18n + "kf6-kconfig", + "kf6-kcoreaddons", + "kf6-kdbusaddons", + "kf6-ki18n", + "kf6-kholidays", +] +script = """ +DYNAMIC_INIT + +HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build" + +for qtdir in plugins mkspecs metatypes modules qml; do + if [ -d "${COOKBOOK_SYSROOT}/usr/${qtdir}" ] && [ ! -e "${COOKBOOK_SYSROOT}/${qtdir}" ]; then + ln -s "usr/${qtdir}" "${COOKBOOK_SYSROOT}/${qtdir}" + fi +done + +rm -f CMakeCache.txt +rm -rf CMakeFiles + +cmake "${COOKBOOK_SOURCE}" \ + -DKF_SKIP_PO_PROCESSING=ON \ + -DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \ + -DQT_HOST_PATH="${HOST_BUILD}" \ + -DKF6_HOST_TOOLING=/usr/lib/cmake \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \ + -DBUILD_TESTING=OFF \ + -DBUILD_QCH=OFF \ + -Wno-dev +cmake --build . -j${COOKBOOK_MAKE_JOBS} +cmake --install . --prefix "${COOKBOOK_STAGE}/usr" + +find "${COOKBOOK_STAGE}" -name '*.so*' -exec patchelf --remove-rpath {} \\; 2>/dev/null || true +""" diff --git a/local/recipes/kde/kscreenlocker/recipe.toml b/local/recipes/kde/kscreenlocker/recipe.toml new file mode 100644 index 0000000000..f58ddccdf9 --- /dev/null +++ b/local/recipes/kde/kscreenlocker/recipe.toml @@ -0,0 +1,138 @@ +[package] +name = "kscreenlocker" +version = "6.7.2" + +#TODO: kscreenlocker — the Plasma screen locker (KScreenLocker library, greeter, +# and the ScreenSaver D-Bus interface). +# +# Required by plasma-workspace at TWO points, and only one of them is X11-gated: +# CMakeLists.txt:127 find_package(ScreenSaverDBusInterface CONFIG REQUIRED) <- unconditional +# CMakeLists.txt:204 find_package(KScreenLocker REQUIRED) <- inside if(WITH_X11) +# so even a Wayland-only build needs this package. It also gives the desktop an +# actual lock screen, which is core functionality rather than an optional extra. +# +# X11: Red Bear is Wayland-only, so the X11 locker backend is gated out (see the +# seds below). The Wayland locker (waylandlocker.cpp + the ext-session-lock / +# kde-screen-locker protocols) is the path that actually runs. +[source] +tar = "https://download.kde.org/stable/plasma/6.7.2/kscreenlocker-6.7.2.tar.xz" +blake3 = "90467de75c0fda809aeb43f2b2a743f07177d32d19508bca4f8288621cae50c5" + +[build] +template = "custom" +dependencies = [ + "qtbase", + "qtdeclarative", + "qtwayland", + "kf6-extra-cmake-modules", + # CMakeLists.txt REQUIRED COMPONENTS: + # Crash KCMUtils KIO GlobalAccel I18n IdleTime Notifications Solid + # WindowSystem XmlGui Svg + "kf6-kcrash", + "kf6-kcmutils", + "kf6-kio", + "kf6-kglobalaccel", + "kf6-ki18n", + "kf6-kidletime", + "kf6-knotifications", + "kf6-solid", + "kf6-kwindowsystem", + "kf6-kxmlgui", + "kf6-ksvg", + "kf6-kconfig", + "kf6-kcoreaddons", + "kf6-kdeclarative", + "kf6-kpackage", + # find_package(KF6Screen REQUIRED) / LayerShellQt / PlasmaQuick + "libkscreen", + "layer-shell-qt", + "plasma-framework", + # find_package(PAM REQUIRED) -- Red Bear's PAM comes from pam-redbear + "pam-redbear", + "libwayland", + "wayland-protocols", + "plasma-wayland-protocols", + "libxkbcommon", + "dbus", +] +script = """ +DYNAMIC_INIT + +HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build" + +for qtdir in plugins mkspecs metatypes modules qml; do + if [ -d "${COOKBOOK_SYSROOT}/usr/${qtdir}" ] && [ ! -e "${COOKBOOK_SYSROOT}/${qtdir}" ]; then + ln -s "usr/${qtdir}" "${COOKBOOK_SYSROOT}/${qtdir}" + fi +done + +rm -f CMakeCache.txt +rm -rf CMakeFiles + +# --- Wayland-only: drop the X11 locker backend ----------------------------- +# +# Upstream still hard-requires X11 and XCB at top level even though the Wayland +# session never uses them: +# find_package(X11) + set_package_properties(X11 TYPE REQUIRED) +# find_package(XCB MODULE REQUIRED COMPONENTS XCB KEYSYMS UTIL XTEST) +# XTEST/KEYSYMS are for grabbing X11 pointer/keyboard devices while locked -- +# meaningless without an X server. Red Bear does not support Xorg at all, and +# the in-tree libxcb is static-only with non-PIC relocations so it cannot be +# linked into a shared library anyway (see local/recipes/kde/libkscreen). +# +# So: make X11/XCB optional and drop x11locker from the library. waylandlocker +# remains and is the backend that actually runs. +# All seds are idempotent -- each pattern stops matching once applied. +sed -i 's/^ TYPE REQUIRED$/ TYPE OPTIONAL/' \ + "${COOKBOOK_SOURCE}/CMakeLists.txt" +sed -i 's/^find_package(XCB MODULE REQUIRED COMPONENTS XCB KEYSYMS UTIL XTEST)$/find_package(XCB MODULE COMPONENTS XCB KEYSYMS UTIL XTEST)/' \ + "${COOKBOOK_SOURCE}/CMakeLists.txt" +sed -i 's/^set_package_properties(XCB PROPERTIES TYPE REQUIRED)$/set_package_properties(XCB PROPERTIES TYPE OPTIONAL)/' \ + "${COOKBOOK_SOURCE}/CMakeLists.txt" +sed -i '/^ x11locker\\.cpp$/d; /^ x11locker\\.h$/d' "${COOKBOOK_SOURCE}/CMakeLists.txt" + +# Drop the X11/XCB link entries that go with the removed X11 backend. +# With X11/XCB optional and absent, these imported targets do not exist and +# cmake aborts: +# Target "KScreenLocker" links to: X11::X11 but the target was not found. +# Target "kscreenlocker_greet" links to: X11::X11 ... +# The corresponding code paths (x11locker, X11 device grabbing) are already out. +# Wayland::Server / Wayland::Client / LayerShellQt::Interface are untouched -- +# those are the backends the Wayland session actually uses. +# Idempotent: the lines are deleted, so they cannot match again. +sed -i '/^ X11::X11$/d; /^ XCB::XCB$/d; /^ XCB::KEYSYMS$/d' \ + "${COOKBOOK_SOURCE}/CMakeLists.txt" +sed -i '/^ X11::X11$/d; /^ XCB::UTIL$/d' \ + "${COOKBOOK_SOURCE}/greeter/CMakeLists.txt" + +# Drop the Qt6 "Test" component from the REQUIRED list (CMakeLists.txt:45). +# qtbase is built with -DFEATURE_testlib=OFF (AGENTS.md CONVENTIONS: disable +# test suites when cross-compiling), so Qt6Test does not exist in the sysroot. +# kscreenlocker never links it: `grep -rn Qt6::Test` over the whole source tree +# returns nothing at all. Same unused-REQUIRED pattern as libkscreen; tests are +# already off via BUILD_TESTING=OFF. Idempotent. +sed -i '/^find_package(Qt6 .*REQUIRED COMPONENTS DBus Widgets Quick Test)$/s/ Test)/)/' \ + "${COOKBOOK_SOURCE}/CMakeLists.txt" + +cmake "${COOKBOOK_SOURCE}" \ + -DKF_SKIP_PO_PROCESSING=ON \ + -DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \ + -DQT_HOST_PATH="${HOST_BUILD}" \ + -DKF6_HOST_TOOLING=/usr/lib/cmake \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \ + -DWaylandScanner_EXECUTABLE=/usr/bin/wayland-scanner \ + -DBUILD_TESTING=OFF \ + -DBUILD_QCH=OFF \ + -Wno-dev +cmake --build . -j${COOKBOOK_MAKE_JOBS} + +# DESTDIR, not --prefix: KDE/ECM emits some install destinations as ABSOLUTE +# ${KDE_INSTALL_FULL_*} paths. `--prefix` only re-roots RELATIVE destinations, +# so absolute ones escape the stage and cmake writes into the BUILD HOST +# filesystem. Same approach as kde-cli-tools / sddm / kf6-ktexteditor. +DESTDIR="${COOKBOOK_STAGE}" cmake --install . --prefix /usr + +find "${COOKBOOK_STAGE}" -name '*.so*' -exec patchelf --remove-rpath {} \\; 2>/dev/null || true +""" diff --git a/local/recipes/kde/kwin/recipe.toml b/local/recipes/kde/kwin/recipe.toml index 96c86405e4..03cbca9d76 100644 --- a/local/recipes/kde/kwin/recipe.toml +++ b/local/recipes/kde/kwin/recipe.toml @@ -56,6 +56,8 @@ dependencies = [ "lcms2", "libdisplay-info", "kglobalacceld", + "knighttime", + "kscreenlocker", "libudev", "wayland-protocols", "redbear-compositor", @@ -148,30 +150,39 @@ sed -i 's/^add_subdirectory(systembell)/#add_subdirectory(systembell)/' \ sed -i 's/^find_package(Canberra REQUIRED)/find_package(Canberra)/' \ "${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true -# TEMPORARY DEBT -- same shape as systembell. KNightTime is consumed by exactly -# one plugin (src/plugins/nightlight). kwin's find_package(KNightTime CONFIG) -# is NOT itself REQUIRED, but set_package_properties(TYPE REQUIRED) plus -# feature_summary(FATAL_ON_MISSING_REQUIRED_PACKAGES) aborts configure anyway. +# RESOLVED -- the nightlight plugin is ENABLED and KNightTime is a real dep. # -# Correct source (verified, replaces an earlier wrong guess): -# https://download.kde.org/stable/plasma/6.7.2/knighttime-6.7.2.tar.xz -# blake3 27b06ec4545f732d0c44934b2a3d6e9359bd5e90e94627377accf2b69725d677 -# It is a PLASMA component (versioned 6.7.x with kwin), not a Framework -- the -# frameworks URL returns HTTP 200 with an HTML error page, which silently yields -# a 13KB "tarball". +# This block used to comment out src/plugins/nightlight and downgrade +# KNightTime to TYPE OPTIONAL, with the note "Port those two, then knighttime, +# then drop both seds below". That has now been done -- the whole chain exists +# and exports its cmake packages: +# local/recipes/qt/qtpositioning -> Qt6PositioningConfig.cmake +# local/recipes/kde/kf6-kholidays -> KF6HolidaysConfig.cmake +# local/recipes/kde/knighttime -> KNightTimeConfig.cmake (Plasma 6.7.2, +# blake3 27b06ec4545f732d0c44934b2a3d6e9359bd5e90e94627377accf2b69725d677; +# note it is a PLASMA component, not a Framework -- the frameworks URL +# returns HTTP 200 with an HTML error page and a 13KB junk "tarball") +# so both seds are gone and night-light (colour-temperature shifting) builds. +# plasma-workspace's kcms/nighttime is re-enabled for the same reason. + +# TEMPORARY DEBT -- KWIN_BUILD_GAMECONTROLLER=OFF (see the cmake flags below). +# src/plugins/gamecontroller/gamecontrollermanager.cpp includes +# and drives controller hotplug entirely through inotify (inotify_init1, +# inotify_add_watch/rm_watch, and reads of struct inotify_event). Redox has no +# inotify: relibc defines only the __NR_inotify_* syscall numbers +# (src/header/sys_syscall/x86_64.rs) with no header and no implementation, and +# the kernel's scheme-based filesystem exposes no file-event notification for one +# to be built on. Providing it is a real kernel subsystem, not a header gap. # -# Porting it is NOT done here because KNightTime itself requires Qt6Positioning -# and KF6Holidays, neither of which has a recipe in-tree: a three-package chain -# for a night-light feature. Port those two, then knighttime, then drop both -# seds below. +# Unlike the other disabled plugins here, this needs no sed: upstream already +# gates the subdirectory on KWIN_BUILD_GAMECONTROLLER +# (src/plugins/CMakeLists.txt:136), so this is a supported build option rather +# than a source edit. # -# Scope: night-light (colour-temperature shifting) only. -# Idempotent: once commented, ^add_subdirectory no longer matches; TYPE REQUIRED -# is rewritten to a form that no longer matches. -sed -i 's/^add_subdirectory(nightlight)/#add_subdirectory(nightlight)/' \ - "${COOKBOOK_SOURCE}/src/plugins/CMakeLists.txt" 2>/dev/null || true -sed -i '/set_package_properties(KNightTime PROPERTIES/,/^)/ s/^ TYPE REQUIRED$/ TYPE OPTIONAL/' \ - "${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true +# Scope: game controller input only. Nothing on the SDDM/Wayland login path +# touches inotify -- relibc's utmpx module documents that Redox has no +# utmpx-on-inotify subsystem and that SDDM refreshes by polling getutxent. +# Remove this flag once inotify exists in the kernel + relibc. # Build qtwaylandscanner_kde as a HOST tool, then hand it to kwin. # @@ -246,7 +257,14 @@ cmake "${COOKBOOK_SOURCE}" \ -DBUILD_QCH=OFF \ -DKWIN_BUILD_X11=OFF \ -DKWIN_BUILD_KCMS=ON \ - -DKWIN_BUILD_SCREENLOCKER=OFF \ + -DKWIN_BUILD_GAMECONTROLLER=OFF \ + # Screen locking is core desktop functionality, not an optional extra, so + # this is ON. It requires the KScreenLocker library from + # local/recipes/kde/kscreenlocker (ported for Wayland-only: the X11 locker + # backend is gated out there, the ext-session-lock/Wayland path is kept). + # Without BOTH this flag and that package there is no lock screen at all -- + # building kscreenlocker alone is not sufficient. + -DKWIN_BUILD_SCREENLOCKER=ON \ -DKWIN_BUILD_TABBOX=ON \ -DKWIN_BUILD_GLOBALSHORTCUTS=ON \ -DKWIN_BUILD_RUNNERS=ON \ diff --git a/local/recipes/kde/layer-shell-qt/recipe.toml b/local/recipes/kde/layer-shell-qt/recipe.toml new file mode 100644 index 0000000000..3da5dd0b8a --- /dev/null +++ b/local/recipes/kde/layer-shell-qt/recipe.toml @@ -0,0 +1,58 @@ +[package] +name = "layer-shell-qt" +version = "6.7.2" + +#TODO: LayerShellQt — Qt binding for the wlr-layer-shell Wayland protocol. +# Required by plasma-workspace (panels, OSDs, the desktop layer) and by +# kscreenlocker. Needs Qt6WaylandClientPrivate, so it must build after qtwayland. +[source] +tar = "https://download.kde.org/stable/plasma/6.7.2/layer-shell-qt-6.7.2.tar.xz" +blake3 = "ad3552f3eb9c03cfc52c816d05426860e06481fa31d9effad483b3115ae7db8f" + +[build] +template = "custom" +dependencies = [ + "qtbase", + "qtdeclarative", + "qtwayland", + "kf6-extra-cmake-modules", + "libwayland", + "wayland-protocols", + "libxkbcommon", +] +script = """ +DYNAMIC_INIT + +HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build" + +for qtdir in plugins mkspecs metatypes modules qml; do + if [ -d "${COOKBOOK_SYSROOT}/usr/${qtdir}" ] && [ ! -e "${COOKBOOK_SYSROOT}/${qtdir}" ]; then + ln -s "usr/${qtdir}" "${COOKBOOK_SYSROOT}/${qtdir}" + fi +done + +rm -f CMakeCache.txt +rm -rf CMakeFiles + +# wayland-scanner is a build-time generator (protocol XML -> arch-independent +# C). The cross wayland-scanner.pc reports the Redox binary, which cannot run on +# the build host, so point cmake at the host one -- the same approach kwin and +# xwayland already use. +cmake "${COOKBOOK_SOURCE}" \ + -DKF_SKIP_PO_PROCESSING=ON \ + -DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \ + -DQT_HOST_PATH="${HOST_BUILD}" \ + -DKF6_HOST_TOOLING=/usr/lib/cmake \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \ + -DWaylandScanner_EXECUTABLE=/usr/bin/wayland-scanner \ + -DBUILD_TESTING=OFF \ + -DBUILD_QCH=OFF \ + -DBUILD_EXAMPLES=OFF \ + -Wno-dev +cmake --build . -j${COOKBOOK_MAKE_JOBS} +cmake --install . --prefix "${COOKBOOK_STAGE}/usr" + +find "${COOKBOOK_STAGE}" -name '*.so*' -exec patchelf --remove-rpath {} \\; 2>/dev/null || true +""" diff --git a/local/recipes/kde/libkscreen/recipe.toml b/local/recipes/kde/libkscreen/recipe.toml new file mode 100644 index 0000000000..842e2fdc8c --- /dev/null +++ b/local/recipes/kde/libkscreen/recipe.toml @@ -0,0 +1,119 @@ +[package] +name = "libkscreen" +version = "6.7.2" + +#TODO: libkscreen — KScreen display-configuration library (provides the +# KF6Screen cmake package). Required by plasma-workspace and kscreenlocker. +# The XCB/X11 backend is left off: Red Bear is Wayland-only (KWIN_BUILD_X11=OFF), +# so only the Wayland and "fake" backends are built. +[source] +tar = "https://download.kde.org/stable/plasma/6.7.2/libkscreen-6.7.2.tar.xz" +blake3 = "91675625aaa0debc9ab42284d42e1eff52dcd6a07b23f3a00d716c8464a1fd89" + +[build] +template = "custom" +dependencies = [ + "qtbase", + "qtdeclarative", + "qtwayland", + "kf6-extra-cmake-modules", + "kf6-kconfig", + "kf6-kcoreaddons", + "kf6-ki18n", + "kf6-kwayland", + "libwayland", + "wayland-protocols", + "plasma-wayland-protocols", +] +script = """ +DYNAMIC_INIT + +HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build" + +for qtdir in plugins mkspecs metatypes modules qml; do + if [ -d "${COOKBOOK_SYSROOT}/usr/${qtdir}" ] && [ ! -e "${COOKBOOK_SYSROOT}/${qtdir}" ]; then + ln -s "usr/${qtdir}" "${COOKBOOK_SYSROOT}/${qtdir}" + fi +done + +rm -f CMakeCache.txt +rm -rf CMakeFiles + +# Drop the Qt6 "Test" component from the REQUIRED list (CMakeLists.txt:32). +# qtbase is built with -DFEATURE_testlib=OFF, which is this project's documented +# cross-compilation convention (AGENTS.md CONVENTIONS: "disable test suites when +# cross-compiling"), so Qt6Test genuinely does not exist in the sysroot. +# libkscreen never uses it: `grep -rn Qt6::Test` over the whole source tree +# returns nothing -- not in src/, and not even in its own test directories. +# So this is an unused REQUIRED component that blocks configure for no reason; +# removing it disables nothing. Tests are already off via BUILD_TESTING=OFF. +# Anchored to the exact find_package line and idempotent: once " Test " is gone +# the pattern no longer matches. +sed -i '/^find_package(Qt6 .*REQUIRED Core DBus Gui Test WaylandClient)$/s/ Test / /' \ + "${COOKBOOK_SOURCE}/CMakeLists.txt" + +# TEMPORARY DEBT -- drop the XCB (X11) DPMS helper from KF6ScreenDpms. +# +# Upstream's own top-level find_package(XCB COMPONENTS XCB RANDR DPMS) is +# OPTIONAL (CMakeLists.txt:50, no REQUIRED), i.e. upstream intends XCB to be +# absent-able -- but src/libdpms/CMakeLists.txt:6 then links XCB::XCB / +# XCB::DPMS / XCB::RANDR unconditionally, so configure dies with +# Target "KF6ScreenDpms" links to: XCB::XCB but the target was not found. +# That is an upstream inconsistency for Wayland-only builds, the same class as +# kwin's dialoghelper/killer (X11 code compiled unconditionally behind a runtime +# platform check), which this tree already gates the same way. +# +# Supplying XCB instead is not viable here: the in-tree libxcb builds +# static-only on Redox (libtool has no Redox shared-lib support -- see +# local/recipes/wayland/xwayland/recipe.toml), and its .a carries non-PIC +# R_X86_64_32S relocations, so it cannot be linked into a shared object. +# +# dpms.cpp picks its helper at RUNTIME via QX11Info::isPlatformX11() / +# platformName(), so on a Wayland-only session the Xcb branch is unreachable +# dead code. Removing it changes no reachable behaviour; the Wayland DPMS path +# is untouched and is the one actually used. +# Remove once libxcb is built shared (or PIC) on Redox. +# All three seds are idempotent: each pattern stops matching once applied. +sed -i 's/ xcbdpmshelper\\.cpp//' "${COOKBOOK_SOURCE}/src/libdpms/CMakeLists.txt" +sed -i 's/^ PRIVATE XCB::XCB XCB::DPMS XCB::RANDR$/ PRIVATE/' \ + "${COOKBOOK_SOURCE}/src/libdpms/CMakeLists.txt" + +# Every remaining consumer of the X11 interop header. Both pick their backend +# at RUNTIME (dpms.cpp -> Xcb vs Wayland helper; backendmanager.cpp -> "XRandR" +# vs "KWayland"), so on a Wayland-only session the X11 arm is dead code -- but +# the header is included unconditionally and qtbase does not install the private +# X11 interop header for a Wayland-only Qt, so compilation fails outright: +# fatal error: QtGui/private/qtx11extras_p.h: No such file or directory +# Neutralising the condition keeps both dispatchers intact and lets the Wayland +# arm run exactly as before. +for _f in src/backendmanager.cpp src/libdpms/dpms.cpp; do + sed -i -e '/#include /d' \ + -e 's/QX11Info::isPlatformX11()/false \\/* Redox: no X11 platform plugin *\\//' \ + "${COOKBOOK_SOURCE}/${_f}" +done +sed -i -e '/^#include "xcbdpmshelper_p\\.h"$/d' \ + -e 's/^ m_helper\\.reset(new XcbDpmsHelper);$//' \ + "${COOKBOOK_SOURCE}/src/libdpms/dpms.cpp" + +cmake "${COOKBOOK_SOURCE}" \ + -DKF_SKIP_PO_PROCESSING=ON \ + -DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \ + -DQT_HOST_PATH="${HOST_BUILD}" \ + -DKF6_HOST_TOOLING=/usr/lib/cmake \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \ + -DWaylandScanner_EXECUTABLE=/usr/bin/wayland-scanner \ + -DBUILD_TESTING=OFF \ + -DBUILD_QCH=OFF \ + -Wno-dev +cmake --build . -j${COOKBOOK_MAKE_JOBS} +# DESTDIR, not --prefix: KDE/ECM emits some install destinations as ABSOLUTE +# ${KDE_INSTALL_FULL_*} paths (e.g. /usr/lib/libexec/kf6). `--prefix` only +# re-roots RELATIVE destinations, so absolute ones escape the stage and cmake +# writes straight into the BUILD HOST filesystem. DESTDIR re-roots both. +# Same approach as local/recipes/kde/kde-cli-tools and sddm. +DESTDIR="${COOKBOOK_STAGE}" cmake --install . --prefix /usr + +find "${COOKBOOK_STAGE}" -name '*.so*' -exec patchelf --remove-rpath {} \\; 2>/dev/null || true +""" diff --git a/local/recipes/kde/libksysguard/P0-redox-process-backend.patch b/local/recipes/kde/libksysguard/P0-redox-process-backend.patch new file mode 100644 index 0000000000..08e0e2d6be --- /dev/null +++ b/local/recipes/kde/libksysguard/P0-redox-process-backend.patch @@ -0,0 +1,437 @@ +diff -ruN orig/processcore/plugins/gpu/gpu.cpp new/processcore/plugins/gpu/gpu.cpp +--- orig/processcore/plugins/gpu/gpu.cpp ++++ new/processcore/plugins/gpu/gpu.cpp +@@ -7,6 +7,11 @@ + + #include "gpu.h" + ++// major()/minor() live in . glibc used to leak them via ++// ; relibc does not, so include the header the macros ++// actually come from. relibc provides it (include/sys/sysmacros.h). ++#include ++ + #ifdef HAVE_UDEV + #include + #endif +diff -ruN orig/processcore/processes_local_p.cpp new/processcore/processes_local_p.cpp +--- orig/processcore/processes_local_p.cpp ++++ new/processcore/processes_local_p.cpp +@@ -4,7 +4,9 @@ + SPDX-License-Identifier: LGPL-2.0-or-later + */ + +-#if defined __linux__ ++#if defined __redox__ ++#include "processes_redox_p.cpp" ++#elif defined __linux__ + #include "processes_linux_p.cpp" + #elif defined __FreeBSD__ || defined __FreeBSD_kernel__ + #include "processes_freebsd_p.cpp" +diff -ruN orig/processcore/processes_redox_p.cpp new/processcore/processes_redox_p.cpp +--- orig/processcore/processes_redox_p.cpp ++++ new/processcore/processes_redox_p.cpp +@@ -0,0 +1,404 @@ ++/* ++ SPDX-FileCopyrightText: 2026 Red Bear OS contributors ++ ++ SPDX-License-Identifier: LGPL-2.0-or-later ++ ++ Redox backend for KSysGuard::ProcessesLocal. ++ ++ processes_local_p.cpp dispatches on compiler platform macros and had no ++ __redox__ arm, so on Redox no backend was compiled at all and every ++ ProcessesLocal symbol came out undefined. This file is that arm, written the ++ way processes_local_p.h documents ("make a processes_(osname).cpp file which ++ implements all of the functions below"). ++ ++ Two data sources, both plain-text tables: ++ ++ /scheme/proc/ps -- the process table, owned by the USERSPACE process manager ++ (base/bootstrap/src/procmgr.rs, fn ps_data). Redox is a microkernel: the ++ kernel's Context struct has no parent field at all, and process lineage, ++ sessions and credentials live in procmgr. Layout: ++ "{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{:<16}" ++ PID PGID PPID SID RUID RGID EUID EGID NTHRD STATUS NAME ++ This is where PPID, the real/effective id split, and the thread count come ++ from. ++ ++ /scheme/sys/context -- the kernel context table (kernel/src/scheme/sys/context.rs), ++ used ONLY for per-process memory, which procmgr does not track. Layout: ++ "{:<6}{:<6}{:<6}{:<6}{:<6}{:<11}{:<12}{:<8}{:<8}{}" ++ PID EUID EGID STAT CPU AFFINITY TIME PRIVATE SHARED NAME ++ ++ Caching: processes_local_p.h documents the call order as getAllPids(), then ++ getParentPid() for each pid, then updateProcessInfo() for each pid. Both ++ tables are therefore parsed once per refresh in getAllPids() and reused, so ++ a cycle costs two reads rather than two reads per process. ++ ++ What Redox genuinely lacks is reported honestly rather than faked: there is ++ no setpriority/sched_setscheduler/ioprio_set equivalent, because on a ++ microkernel scheduling policy is a userspace concern that has not been built ++ out yet -- so those return NotSupported, and there is no swap. ++*/ ++ ++#include "processes_local_p.h" ++#include "memoryinfo_p.h" // MemoryInfo/MemoryFields: process.h only forward-declares MemoryInfo ++#include "process.h" ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++namespace ++{ ++// Column start offsets implied by procmgr's ps_data() format string. ++constexpr int kPsPid = 0; ++constexpr int kPsPgid = 6; ++constexpr int kPsPpid = 12; ++constexpr int kPsSid = 18; ++constexpr int kPsRuid = 24; ++constexpr int kPsRgid = 30; ++constexpr int kPsEuid = 36; ++constexpr int kPsEgid = 42; ++constexpr int kPsNthrd = 48; ++constexpr int kPsStatus = 54; ++constexpr int kPsName = 62; ++ ++// Column start offsets implied by the kernel context scheme's format string. ++constexpr int kCtxPid = 0; ++constexpr int kCtxPrivate = 53; ++constexpr int kCtxShared = 61; ++constexpr int kCtxName = 69; ++ ++QString columnAt(const QString &row, int start, int end) ++{ ++ if (start >= row.size()) { ++ return QString(); ++ } ++ const int len = (end < 0 || end > row.size()) ? row.size() - start : end - start; ++ return row.mid(start, len).trimmed(); ++} ++ ++QList readTableRows(const QString &path) ++{ ++ QList rows; ++ QFile f(path); ++ if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { ++ return rows; ++ } ++ const QList lines = f.readAll().split('\n'); ++ for (int i = 1; i < lines.size(); ++i) { // skip the header row ++ const QString row = QString::fromUtf8(lines.at(i)); ++ if (!row.trimmed().isEmpty()) { ++ rows.append(row); ++ } ++ } ++ return rows; ++} ++ ++// The kernel pretty-prints PRIVATE/SHARED via format_bytes() as e.g. "1.23 MB". ++// Convert back to KiB, the unit every Process memory field uses. ++qlonglong parseFormattedBytesToKiB(const QString &text) ++{ ++ if (text.isEmpty()) { ++ return 0; ++ } ++ const QStringList parts = text.split(QLatin1Char(' '), Qt::SkipEmptyParts); ++ bool ok = false; ++ const double value = parts.value(0).toDouble(&ok); ++ if (!ok) { ++ return 0; ++ } ++ const QString unit = parts.value(1).toUpper(); ++ double bytes = value; ++ if (unit == QLatin1String("KB")) { ++ bytes = value * 1024.0; ++ } else if (unit == QLatin1String("MB")) { ++ bytes = value * 1024.0 * 1024.0; ++ } else if (unit == QLatin1String("GB")) { ++ bytes = value * 1024.0 * 1024.0 * 1024.0; ++ } else if (unit == QLatin1String("TB")) { ++ bytes = value * 1024.0 * 1024.0 * 1024.0 * 1024.0; ++ } ++ return static_cast(bytes / 1024.0); ++} ++ ++// "MemTotal: 123 kB" -> 123 ++long long meminfoValueKiB(const char *key) ++{ ++ QFile f(QStringLiteral("/scheme/sys/mem")); ++ if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { ++ return 0; ++ } ++ const QString wanted = QString::fromUtf8(key); ++ const QList lines = f.readAll().split('\n'); ++ for (const QByteArray &raw : lines) { ++ const QString line = QString::fromUtf8(raw).trimmed(); ++ if (!line.startsWith(wanted)) { ++ continue; ++ } ++ const QStringList parts = line.split(QLatin1Char(' '), Qt::SkipEmptyParts); ++ bool ok = false; ++ const long long value = parts.value(1).toLongLong(&ok); ++ return ok ? value : 0; ++ } ++ return 0; ++} ++ ++struct PsEntry { ++ long ppid = -1; ++ long ruid = 0; ++ long rgid = 0; ++ long euid = 0; ++ long egid = 0; ++ long nthreads = 1; ++ QString status; ++ QString name; ++}; ++ ++struct MemEntry { ++ qlonglong privKiB = 0; ++ qlonglong sharedKiB = 0; ++}; ++} ++ ++namespace KSysGuard ++{ ++class ProcessesLocal::Private ++{ ++public: ++ // Refreshed once per cycle from getAllPids(), then reused by the ++ // getParentPid()/updateProcessInfo() calls that follow it. ++ QHash ps; ++ QHash mem; ++ ++ void refresh() ++ { ++ ps.clear(); ++ mem.clear(); ++ ++ const QList psRows = readTableRows(QStringLiteral("/scheme/proc/ps")); ++ for (const QString &row : psRows) { ++ bool ok = false; ++ const long pid = columnAt(row, kPsPid, kPsPgid).toLong(&ok); ++ if (!ok) { ++ continue; ++ } ++ PsEntry e; ++ e.ppid = columnAt(row, kPsPpid, kPsSid).toLong(); ++ e.ruid = columnAt(row, kPsRuid, kPsRgid).toLong(); ++ e.rgid = columnAt(row, kPsRgid, kPsEuid).toLong(); ++ e.euid = columnAt(row, kPsEuid, kPsEgid).toLong(); ++ e.egid = columnAt(row, kPsEgid, kPsNthrd).toLong(); ++ const long n = columnAt(row, kPsNthrd, kPsStatus).toLong(); ++ e.nthreads = n > 0 ? n : 1; ++ e.status = columnAt(row, kPsStatus, kPsName); ++ e.name = columnAt(row, kPsName, -1); ++ ps.insert(pid, e); ++ } ++ ++ const QList ctxRows = readTableRows(QStringLiteral("/scheme/sys/context")); ++ for (const QString &row : ctxRows) { ++ bool ok = false; ++ const long pid = columnAt(row, kCtxPid, kCtxPid + 6).toLong(&ok); ++ if (!ok) { ++ continue; ++ } ++ MemEntry m; ++ m.privKiB = parseFormattedBytesToKiB(columnAt(row, kCtxPrivate, kCtxShared)); ++ m.sharedKiB = parseFormattedBytesToKiB(columnAt(row, kCtxShared, kCtxName)); ++ mem.insert(pid, m); ++ } ++ } ++}; ++ ++ProcessesLocal::ProcessesLocal() ++ : d(new Private()) ++{ ++} ++ ++ProcessesLocal::~ProcessesLocal() ++{ ++ delete d; ++} ++ ++QSet ProcessesLocal::getAllPids() ++{ ++ d->refresh(); ++ QSet pids; ++ for (auto it = d->ps.constBegin(); it != d->ps.constEnd(); ++it) { ++ pids.insert(it.key()); ++ } ++ // A context with no procmgr entry (e.g. a kernel-side context) still counts ++ // as a running process for display purposes. ++ for (auto it = d->mem.constBegin(); it != d->mem.constEnd(); ++it) { ++ pids.insert(it.key()); ++ } ++ return pids; ++} ++ ++long ProcessesLocal::getParentPid(long pid) ++{ ++ const auto it = d->ps.constFind(pid); ++ if (it == d->ps.constEnd()) { ++ return -1; ++ } ++ // procmgr reports init as its own parent; Processes wants -1 for "no parent" ++ // so the tree gets a single root instead of a self-referential cycle. ++ return it->ppid == pid ? -1 : it->ppid; ++} ++ ++bool ProcessesLocal::updateProcessInfo(long pid, Process *process) ++{ ++ bool found = false; ++ ++ const auto psIt = d->ps.constFind(pid); ++ if (psIt != d->ps.constEnd()) { ++ found = true; ++ process->setUid(psIt->ruid); ++ process->setEuid(psIt->euid); ++ process->setSuid(psIt->euid); ++ process->setFsuid(psIt->euid); ++ process->setGid(psIt->rgid); ++ process->setEgid(psIt->egid); ++ process->setSgid(psIt->egid); ++ process->setFsgid(psIt->egid); ++ process->setName(psIt->name); ++ process->setCommand(psIt->name); ++ process->setNumThreads(psIt->nthreads); ++ ++ // procmgr's ProcessStatus, stringified in ps_data(): ++ // R PossiblyRunnable, S Stopped, E Exiting, X Exited ++ const QString st = psIt->status; ++ if (st.startsWith(QLatin1Char('R'))) { ++ process->setStatus(Process::Running); ++ } else if (st.startsWith(QLatin1Char('S'))) { ++ process->setStatus(Process::Stopped); ++ } else if (st.startsWith(QLatin1Char('E'))) { ++ // Exiting: torn down but not yet reaped by its parent. ++ process->setStatus(Process::Zombie); ++ } else if (st.startsWith(QLatin1Char('X'))) { ++ process->setStatus(Process::Ended); ++ } else { ++ process->setStatus(Process::OtherStatus); ++ } ++ } ++ ++ const auto memIt = d->mem.constFind(pid); ++ if (memIt != d->mem.constEnd()) { ++ found = true; ++ // The kernel gives private and shared resident memory directly, which ++ // maps onto the "imprecise" field set. Redox has no equivalent of ++ // Linux's expensive smaps pass, so there is no "precise" counterpart; ++ // MemoryInfo falls back to the imprecise values when precise is unset. ++ MemoryFields fields; ++ fields.rss = memIt->privKiB + memIt->sharedKiB; ++ fields.shared = memIt->sharedKiB; ++ fields.priv = memIt->privKiB; ++ fields.swap = 0; // Redox has no swap ++ fields.lastUpdate = std::chrono::steady_clock::now(); ++ process->memoryInfo()->imprecise = fields; ++ process->memoryInfo()->vmSize = memIt->privKiB + memIt->sharedKiB; ++ process->addChange(Process::Memory); ++ } ++ ++ if (!found) { ++ return false; ++ } ++ ++ // No nice/scheduler concept is exposed yet (see setNiceness below), so ++ // report the defaults rather than pretending a value was read. ++ process->setNiceLevel(0); ++ process->setScheduler(Process::Other); ++ process->setTracerpid(-1); ++ return true; ++} ++ ++Processes::Error ProcessesLocal::sendSignal(long pid, int sig) ++{ ++ if (::kill(static_cast(pid), sig) < 0) { ++ switch (errno) { ++ case ESRCH: ++ return Processes::ProcessDoesNotExistOrZombie; ++ case EPERM: ++ return Processes::InsufficientPermissions; ++ default: ++ return Processes::Unknown; ++ } ++ } ++ return Processes::NoError; ++} ++ ++Processes::Error ProcessesLocal::setNiceness(long pid, int priority) ++{ ++ Q_UNUSED(pid) ++ Q_UNUSED(priority) ++ // Redox is a microkernel: scheduling policy belongs in userspace and that ++ // surface has not been built out yet, so there is no setpriority(2) ++ // equivalent to call. Reporting NotSupported is the accurate answer. ++ return Processes::NotSupported; ++} ++ ++int ProcessesLocal::getNiceness(long pid) ++{ ++ Q_UNUSED(pid) ++ return 0; ++} ++ ++Processes::Error ProcessesLocal::setScheduler(long pid, int priorityClass, int priority) ++{ ++ Q_UNUSED(pid) ++ Q_UNUSED(priorityClass) ++ Q_UNUSED(priority) ++ // See setNiceness(): no userspace scheduling-policy surface yet. ++ return Processes::NotSupported; ++} ++ ++int ProcessesLocal::getSchedulerClass(long pid) ++{ ++ Q_UNUSED(pid) ++ return Process::Other; ++} ++ ++Processes::Error ProcessesLocal::setIoNiceness(long pid, int priorityClass, int priority) ++{ ++ Q_UNUSED(pid) ++ Q_UNUSED(priorityClass) ++ Q_UNUSED(priority) ++ // No ioprio_set(2) equivalent. ++ return Processes::NotSupported; ++} ++ ++int ProcessesLocal::getIoNiceness(long pid) ++{ ++ Q_UNUSED(pid) ++ return -1; ++} ++ ++int ProcessesLocal::getIoPriorityClass(long pid) ++{ ++ Q_UNUSED(pid) ++ return Process::None; ++} ++ ++bool ProcessesLocal::supportsIoNiceness() ++{ ++ return false; ++} ++ ++long long ProcessesLocal::totalPhysicalMemory() ++{ ++ return meminfoValueKiB("MemTotal:"); ++} ++ ++long long ProcessesLocal::totalSwapMemory() ++{ ++ // Redox has no swap; the kernel reports SwapTotal: 0 kB accordingly. ++ return meminfoValueKiB("SwapTotal:"); ++} ++ ++} diff --git a/local/recipes/kde/libksysguard/recipe.toml b/local/recipes/kde/libksysguard/recipe.toml new file mode 100644 index 0000000000..756333cb25 --- /dev/null +++ b/local/recipes/kde/libksysguard/recipe.toml @@ -0,0 +1,98 @@ +[package] +name = "libksysguard" +version = "6.7.2" + +#TODO: libksysguard — process/sensor monitoring library (provides the KSysGuard +# cmake package). Required by plasma-desktop: +# find_package(KSysGuard CONFIG REQUIRED) (CMakeLists.txt:193) +# and used by plasma-workspace's system-monitor applets. +[source] +tar = "https://download.kde.org/stable/plasma/6.7.2/libksysguard-6.7.2.tar.xz" +blake3 = "f7dc18885ba37fec5f72e10253512e86e8b14993726a37b59368c9bf17dfc620" +patches = ["P0-redox-process-backend.patch"] + +[build] +template = "custom" +dependencies = [ + "qtbase", + "qtdeclarative", + "kf6-extra-cmake-modules", + # CMakeLists.txt:36 REQUIRED COMPONENTS + "kf6-kcoreaddons", + "kf6-kconfig", + "kf6-ki18n", + "kf6-kauth", + "kf6-kservice", + "kf6-kpackage", + "kf6-knewstuff", + "kf6-solid", + # CMakeLists.txt:38 / :89 + "zlib", + "libdrm", + "libudev", +] +script = """ +DYNAMIC_INIT + +HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build" + +for qtdir in plugins mkspecs metatypes modules qml; do + if [ -d "${COOKBOOK_SYSROOT}/usr/${qtdir}" ] && [ ! -e "${COOKBOOK_SYSROOT}/${qtdir}" ]; then + ln -s "usr/${qtdir}" "${COOKBOOK_SYSROOT}/${qtdir}" + fi +done + +rm -f CMakeCache.txt +rm -rf CMakeFiles + +# Narrow upstream's Linux-only block (CMakeLists.txt:53) so it does not fire on +# Redox. It gates interfaces that are Linux-KERNEL specific and simply do not +# exist here: +# NL -- libnl, socket info via the sock_diag NETLINK subsystem +# Sensors -- lm_sensors hardware sensor reading +# Both are declared find_package(...) without REQUIRED but then marked +# set_package_properties(TYPE REQUIRED), so feature_summary() aborts the whole +# configure when they are absent. +# +# The guard only misfires because our cmake toolchain sets +# CMAKE_SYSTEM_NAME=Linux on purpose -- CMake then defines UNIX=TRUE, which Qt's +# build system needs to select its Unix sources (see the rationale in +# local/recipes/qt/redox-toolchain.cmake). That is a build-system +# accommodation, NOT a claim that Redox provides Linux kernel APIs. Same root +# cause and same remedy as the `if (LINUX)` NetworkManagerQt guard in +# local/recipes/kde/plasma-workspace/recipe.toml. +# +# Scope: per-application network usage (netlink) and hardware sensor readout in +# the system monitor. The KSysGuard library that plasma-desktop links is fully +# built. Revisit if Redox grows a sensors/netlink-equivalent surface. +# Idempotent: after rewriting, the original pattern no longer matches. +sed -i 's/^if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")$/if (${CMAKE_SYSTEM_NAME} MATCHES "Linux" AND NOT REDOX)/' \ + "${COOKBOOK_SOURCE}/CMakeLists.txt" + +# ENABLE_KAUTH_HELPER (CMakeLists.txt:91) builds a setuid KAuth helper that +# reads /proc to renice/kill other users' processes. Redox has no /proc process +# table of that shape and no setuid helper story yet, and the helper is not +# needed for the library API that plasma-desktop links. Upstream-supported +# option; the KSysGuard library itself is still fully built. +cmake "${COOKBOOK_SOURCE}" \ + -DKF_SKIP_PO_PROCESSING=ON \ + -DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \ + -DQT_HOST_PATH="${HOST_BUILD}" \ + -DKF6_HOST_TOOLING=/usr/lib/cmake \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \ + -DENABLE_KAUTH_HELPER=OFF \ + -DBUILD_TESTING=OFF \ + -DBUILD_QCH=OFF \ + -Wno-dev +cmake --build . -j${COOKBOOK_MAKE_JOBS} +# DESTDIR, not --prefix: KDE/ECM emits some install destinations as ABSOLUTE +# ${KDE_INSTALL_FULL_*} paths (e.g. /usr/lib/libexec/kf6). `--prefix` only +# re-roots RELATIVE destinations, so absolute ones escape the stage and cmake +# writes straight into the BUILD HOST filesystem. DESTDIR re-roots both. +# Same approach as local/recipes/kde/kde-cli-tools and sddm. +DESTDIR="${COOKBOOK_STAGE}" cmake --install . --prefix /usr + +find "${COOKBOOK_STAGE}" -name '*.so*' -exec patchelf --remove-rpath {} \\; 2>/dev/null || true +""" diff --git a/local/recipes/kde/plasma-activities-stats/recipe.toml b/local/recipes/kde/plasma-activities-stats/recipe.toml new file mode 100644 index 0000000000..d8e8e02269 --- /dev/null +++ b/local/recipes/kde/plasma-activities-stats/recipe.toml @@ -0,0 +1,56 @@ +[package] +name = "plasma-activities-stats" +version = "6.7.2" + +#TODO: PlasmaActivitiesStats — usage-statistics query library layered on +# plasma-activities. Required by plasma-workspace and plasma-desktop +# (find_package(PlasmaActivitiesStats REQUIRED)). +[source] +tar = "https://download.kde.org/stable/plasma/6.7.2/plasma-activities-stats-6.7.2.tar.xz" +blake3 = "5a8cdd83553ccd45380e32f388d658379188776af7c19628f1b02d0419b22857" + +[build] +template = "custom" +dependencies = [ + "qtbase", + "qtdeclarative", + "kf6-extra-cmake-modules", + "kf6-kconfig", + "kf6-kcoreaddons", + "kf6-plasma-activities", +] +script = """ +DYNAMIC_INIT + +HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build" + +for qtdir in plugins mkspecs metatypes modules qml; do + if [ -d "${COOKBOOK_SYSROOT}/usr/${qtdir}" ] && [ ! -e "${COOKBOOK_SYSROOT}/${qtdir}" ]; then + ln -s "usr/${qtdir}" "${COOKBOOK_SYSROOT}/${qtdir}" + fi +done + +rm -f CMakeCache.txt +rm -rf CMakeFiles + +cmake "${COOKBOOK_SOURCE}" \ + -DKF_SKIP_PO_PROCESSING=ON \ + -DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \ + -DQT_HOST_PATH="${HOST_BUILD}" \ + -DKF6_HOST_TOOLING=/usr/lib/cmake \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \ + -DBUILD_TESTING=OFF \ + -DBUILD_QCH=OFF \ + -Wno-dev +cmake --build . -j${COOKBOOK_MAKE_JOBS} +# DESTDIR, not --prefix: KDE/ECM emits some install destinations as ABSOLUTE +# ${KDE_INSTALL_FULL_*} paths (e.g. /usr/lib/libexec/kf6). `--prefix` only +# re-roots RELATIVE destinations, so absolute ones escape the stage and cmake +# writes straight into the BUILD HOST filesystem. DESTDIR re-roots both. +# Same approach as local/recipes/kde/kde-cli-tools and sddm. +DESTDIR="${COOKBOOK_STAGE}" cmake --install . --prefix /usr + +find "${COOKBOOK_STAGE}" -name '*.so*' -exec patchelf --remove-rpath {} \\; 2>/dev/null || true +""" diff --git a/local/recipes/kde/plasma-desktop/recipe.toml b/local/recipes/kde/plasma-desktop/recipe.toml index d172add494..71a1e8194e 100644 --- a/local/recipes/kde/plasma-desktop/recipe.toml +++ b/local/recipes/kde/plasma-desktop/recipe.toml @@ -39,6 +39,15 @@ dependencies = [ "kf6-kwindowsystem", "kf6-kxmlgui", "kf6-solid", + # Newly ported REQUIRED cmake packages (CMakeLists.txt:92-96, 184-191). + # LibKWorkspace / LibTaskManager / LibNotificationManager / LibKLookAndFeel + # and the *DBusInterface packages are all produced BY plasma-workspace, so + # it must be staged before plasma-desktop configures. + "plasma-workspace", + "plasma5support", + "libksysguard", + "plasma-activities-stats", + "kf6-plasma-activities", "plasma-framework", "kirigami", "libxkbcommon", diff --git a/local/recipes/kde/plasma-workspace/recipe.toml b/local/recipes/kde/plasma-workspace/recipe.toml index 3948032288..af9a4c0236 100644 --- a/local/recipes/kde/plasma-workspace/recipe.toml +++ b/local/recipes/kde/plasma-workspace/recipe.toml @@ -20,6 +20,7 @@ dependencies = [ "qtdeclarative", "qtwayland", "qtsvg", + "qtpositioning", "kf6-extra-cmake-modules", "kf6-karchive", "kf6-kauth", @@ -52,6 +53,24 @@ dependencies = [ "kirigami", "kf6-prison", "kf6-solid", + # KF6 REQUIRED COMPONENTS that had no in-tree recipe until now + # (CMakeLists.txt:66 -- Holidays / Runner / StatusNotifierItem). + "kf6-kholidays", + "kf6-krunner", + "kf6-kstatusnotifieritem", + "kf6-ktexteditor", + "kf6-kded6", + "kf6-kcmutils", + "kf6-notifyconfig", + "kf6-parts", + "kf6-plasma-activities", + # Newly ported: previously-missing REQUIRED cmake packages + # (LayerShellQt / KF6Screen / PlasmaActivitiesStats / KNightTime). + "layer-shell-qt", + "libkscreen", + "kscreenlocker", + "knighttime", + "plasma-activities-stats", "plasma-framework", "kwin", "libwayland", @@ -77,6 +96,54 @@ done #DISABLED: find "${COOKBOOK_SOURCE}" -name CMakeLists.txt -exec sed -i 's/^ecm_install_po_files_as_qm/#ecm_install_po_files_as_qm/' {} \\; #DISABLED: find "${COOKBOOK_SOURCE}" -name CMakeLists.txt -exec sed -i 's/^ki18n_install(po)/#ki18n_install(po)/' {} \\; +# Drop Qt6::Location from the REQUIRED component list (CMakeLists.txt:54). +# It is a spurious upstream over-declaration: Location appears in that +# find_package COMPONENTS line and NOWHERE else in the tree -- no target links +# Qt6::Location, no source includes a QtLocation header. qtlocation is not +# ported, so the REQUIRED list alone aborts configure: +# Failed to find required Qt component "Location". +# Removing an unused component declaration disables nothing. +# Idempotent: once the word is gone the pattern no longer matches. +sed -i 's/ Location / /' "${COOKBOOK_SOURCE}/CMakeLists.txt" + +# Drop the QCoro6 requirement (CMakeLists.txt:63). Same class as Location: +# find_package(QCoro6 REQUIRED COMPONENTS Core) +# is the ONLY occurrence of the string "QCoro" in the entire plasma-workspace +# source tree -- no target links QCoro6::Core, no source includes a QCoro +# header. Verified with `grep -rn QCoro ` returning exactly that one +# line. So this is an unused REQUIRED declaration that aborts configure on a +# library nothing actually needs; removing it disables no functionality and +# saves porting qcoro outright. +# Idempotent: the line is deleted, so it cannot match again. +sed -i '/^find_package(QCoro6 REQUIRED COMPONENTS Core)$/d' "${COOKBOOK_SOURCE}/CMakeLists.txt" + +# Qt6::Positioning and kcms/nighttime are ENABLED. +# +# They were briefly deferred here because Positioning's only consumer, +# kcms/nighttime (kcms/nighttime/CMakeLists.txt:33), also needs KNightTime, and +# at the time none of qtpositioning / KF6Holidays / knighttime had a recipe -- +# the same chain kwin's recipe cited when it deferred its nightlight plugin. +# All three are now ported and export their cmake packages, so the deferral is +# obsolete and the night-colour module builds for real: +# local/recipes/qt/qtpositioning -> Qt6PositioningConfig.cmake +# local/recipes/kde/kf6-kholidays -> KF6HolidaysConfig.cmake +# local/recipes/kde/knighttime -> KNightTimeConfig.cmake +# kwin's nightlight plugin is re-enabled for the same reason. + +# KF6NetworkManagerQt is required only under `if (LINUX)` (CMakeLists.txt:103), +# whose upstream comment reads "there's no NetworkManager on FreeBSD" -- i.e. +# the guard exists precisely to skip platforms that have no NetworkManager. +# Redox is such a platform (its network control plane is redbear-netctl), but +# our cmake toolchain deliberately sets CMAKE_SYSTEM_NAME=Linux so that CMake +# defines UNIX=TRUE for Qt's Unix source selection -- which makes LINUX true and +# misfires this guard. Narrow it with the REDOX marker the toolchain already +# sets (redox-toolchain.cmake:53, a CACHE INTERNAL var, so it is in scope here). +# +# The sole consumer, geotimezoned, is already written to degrade gracefully: +# CMakeLists.txt:443 wraps it in `if(TARGET KF6::NetworkManagerQt)`. +# Idempotent: after rewriting, `^if (LINUX)$` no longer matches. +sed -i 's/^if (LINUX)$/if (LINUX AND NOT REDOX)/' "${COOKBOOK_SOURCE}/CMakeLists.txt" + cmake "${COOKBOOK_SOURCE}" \ -DKF_SKIP_PO_PROCESSING=ON \ -DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \ @@ -87,6 +154,8 @@ cmake "${COOKBOOK_SOURCE}" \ -DQT_BUILD_EXAMPLES=OFF \ -DQT_BUILD_TESTS=OFF \ -DBUILD_TESTING=OFF \ + -DWITH_X11=OFF \ + -DWITH_X11_SESSION=OFF \ -Wno-dev cmake --build . -j"${COOKBOOK_MAKE_JOBS}" cmake --install . --prefix "${COOKBOOK_STAGE}/usr" diff --git a/local/recipes/kde/plasma5support/recipe.toml b/local/recipes/kde/plasma5support/recipe.toml new file mode 100644 index 0000000000..e5df60f1e7 --- /dev/null +++ b/local/recipes/kde/plasma5support/recipe.toml @@ -0,0 +1,72 @@ +[package] +name = "plasma5support" +version = "6.7.2" + +#TODO: Plasma5Support — the Plasma 5 compatibility/data-engine layer +# (runner, dataengine, and service APIs carried forward into Plasma 6). +# Required by plasma-desktop: find_package(Plasma5Support REQUIRED). +[source] +tar = "https://download.kde.org/stable/plasma/6.7.2/plasma5support-6.7.2.tar.xz" +blake3 = "1d4ee8eaf9dd997c453f5368781e72b7774841d84d5fb190d2b7992d265e98f4" + +[build] +template = "custom" +dependencies = [ + "qtbase", + "qtdeclarative", + "kf6-extra-cmake-modules", + # CMakeLists.txt:53 REQUIRED COMPONENTS + "kf6-kconfig", + "kf6-kcoreaddons", + "kf6-kguiaddons", + "kf6-ki18n", + "kf6-knotifications", + "kf6-solid", + "kf6-kservice", + "kf6-kidletime", + "kf6-kio", + "kf6-kunitconversion", + "kf6-kholidays", + # optional-but-present consumers + "kf6-plasma-activities", +] +script = """ +DYNAMIC_INIT + +HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build" + +for qtdir in plugins mkspecs metatypes modules qml; do + if [ -d "${COOKBOOK_SYSROOT}/usr/${qtdir}" ] && [ ! -e "${COOKBOOK_SYSROOT}/${qtdir}" ]; then + ln -s "usr/${qtdir}" "${COOKBOOK_SYSROOT}/${qtdir}" + fi +done + +rm -f CMakeCache.txt +rm -rf CMakeFiles + +# WITH_X11 (CMakeLists.txt:72) defaults ON and then hard-REQUIREs X11 for the +# mouse data engine only. Red Bear is Wayland-only (KWIN_BUILD_X11=OFF, +# plasma-workspace WITH_X11=OFF), so this is the upstream-supported way to skip +# it. Scope: the X11 mouse-position data engine. +cmake "${COOKBOOK_SOURCE}" \ + -DKF_SKIP_PO_PROCESSING=ON \ + -DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \ + -DQT_HOST_PATH="${HOST_BUILD}" \ + -DKF6_HOST_TOOLING=/usr/lib/cmake \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \ + -DWITH_X11=OFF \ + -DBUILD_TESTING=OFF \ + -DBUILD_QCH=OFF \ + -Wno-dev +cmake --build . -j${COOKBOOK_MAKE_JOBS} +# DESTDIR, not --prefix: KDE/ECM emits some install destinations as ABSOLUTE +# ${KDE_INSTALL_FULL_*} paths (e.g. /usr/lib/libexec/kf6). `--prefix` only +# re-roots RELATIVE destinations, so absolute ones escape the stage and cmake +# writes straight into the BUILD HOST filesystem. DESTDIR re-roots both. +# Same approach as local/recipes/kde/kde-cli-tools and sddm. +DESTDIR="${COOKBOOK_STAGE}" cmake --install . --prefix /usr + +find "${COOKBOOK_STAGE}" -name '*.so*' -exec patchelf --remove-rpath {} \\; 2>/dev/null || true +""" diff --git a/local/recipes/libs/pam-redbear/source/include/security/pam_appl.h b/local/recipes/libs/pam-redbear/source/include/security/pam_appl.h index 6418c7a05a..9fa5f14d7b 100644 --- a/local/recipes/libs/pam-redbear/source/include/security/pam_appl.h +++ b/local/recipes/libs/pam-redbear/source/include/security/pam_appl.h @@ -37,9 +37,17 @@ typedef int pam_item_type; #define PAM_IGNORE 25 #define PAM_ABORT 26 #define PAM_AUTHTOK_EXPIRED 27 +/* 28 was missing from this table: Linux-PAM defines PAM_MODULE_UNKNOWN there, + * and the gap between 27 and 29 shows it was skipped rather than deliberately + * omitted. kscreenlocker's greeter/pamauthenticator.cpp references it, so its + * absence was a real hole in the PAM error surface, not an unused constant. */ +#define PAM_MODULE_UNKNOWN 28 #define PAM_BAD_ITEM 29 -#define PAM_BAD_STATE 30 -#define PAM_NO_MODULE_DATA_CRIT 31 +/* 30/31 realigned to the Linux-PAM assignments. They previously carried the + * Red Bear-only names PAM_BAD_STATE / PAM_NO_MODULE_DATA_CRIT, so a program + * built against stock Linux-PAM headers would have mis-decoded these codes. */ +#define PAM_CONV_AGAIN 30 +#define PAM_INCOMPLETE 31 #define PAM_SERVICE 1 #define PAM_USER 2 diff --git a/local/recipes/libs/pam-redbear/source/src/lib.rs b/local/recipes/libs/pam-redbear/source/src/lib.rs index b51b283e80..16e4324c17 100644 --- a/local/recipes/libs/pam-redbear/source/src/lib.rs +++ b/local/recipes/libs/pam-redbear/source/src/lib.rs @@ -60,9 +60,16 @@ pub const PAM_TRY_AGAIN: c_int = 24; pub const PAM_IGNORE: c_int = 25; pub const PAM_ABORT: c_int = 26; pub const PAM_AUTHTOK_EXPIRED: c_int = 27; +pub const PAM_MODULE_UNKNOWN: c_int = 28; pub const PAM_BAD_ITEM: c_int = 29; -pub const PAM_BAD_STATE: c_int = 30; -pub const PAM_NO_MODULE_DATA_CRIT: c_int = 31; +// 30 and 31 previously held Red Bear-invented names (PAM_BAD_STATE and +// PAM_NO_MODULE_DATA_CRIT) sitting on values that Linux-PAM assigns to +// PAM_CONV_AGAIN and PAM_INCOMPLETE. Any application compiled against real +// Linux-PAM headers and run against this library would have mis-decoded those +// two codes. Neither old name was referenced outside this crate, so realigning +// to the standard names/values is safe and removes the ABI divergence. +pub const PAM_CONV_AGAIN: c_int = 30; +pub const PAM_INCOMPLETE: c_int = 31; // PAM item types. pub const PAM_SERVICE: c_int = 1; @@ -227,7 +234,9 @@ fn error_string(errnum: c_int) -> &'static str { PAM_ABORT => "Critical error; abort immediately", PAM_AUTHTOK_EXPIRED => "Authentication token has expired", PAM_BAD_ITEM => "The application passed an invalid item to the PAM library", - PAM_BAD_STATE => "The PAM library is in a bad state", + PAM_MODULE_UNKNOWN => "Module is unknown", + PAM_CONV_AGAIN => "Conversation is waiting for event", + PAM_INCOMPLETE => "Application needs to call PAM again", _ => "Unknown PAM error", } } diff --git a/local/recipes/qt/qtmultimedia/recipe.toml b/local/recipes/qt/qtmultimedia/recipe.toml index 8c51aa18a6..dbe1f40af3 100644 --- a/local/recipes/qt/qtmultimedia/recipe.toml +++ b/local/recipes/qt/qtmultimedia/recipe.toml @@ -98,6 +98,16 @@ for path in cmake_dir.rglob('*.cmake'): text = text.replace(f'{sysroot}/include', '${PACKAGE_PREFIX_DIR}/include') text = text.replace('"/usr/lib/libQt6', '"${PACKAGE_PREFIX_DIR}/lib/libQt6') text = text.replace('"/usr/include', '"${PACKAGE_PREFIX_DIR}/include') + # Metatype JSON paths. Qt exports the BUILD-tree location of each module's + # meta_types/*.json into its cmake config, e.g. + # /usr/src/multimedia/meta_types/qt6multimedia_metatypes.json + # Nothing rewrote those here, so any downstream module that pulls in + # Qt6::Multimedia via qt_internal_add_qml_module died at generate time with + # Cannot find source file: /usr/src/multimedia/meta_types/...json + # (first hit: qtspeech's src/tts/qml). The files themselves ARE staged, at + # /usr/metatypes/, so point the config at the sysroot copy. + text = text.replace('"/usr/src/multimedia/meta_types/', '"${PACKAGE_PREFIX_DIR}/metatypes/') + text = text.replace('/usr/src/multimedia/meta_types/', '${PACKAGE_PREFIX_DIR}/metatypes/') path.write_text(text) PY fi @@ -119,5 +129,11 @@ if [ -d "${COOKBOOK_STAGE}/usr/qml" ]; then mkdir -p "${SYSROOT}/qml" cp -a "${COOKBOOK_STAGE}/usr/qml/"* "${SYSROOT}/qml/" 2>/dev/null || true fi +# The metatype JSONs the cmake config now points at must actually be in the +# sysroot; staging them alone is not enough for a consuming module's configure. +if [ -d "${COOKBOOK_STAGE}/usr/metatypes" ]; then + mkdir -p "${SYSROOT}/metatypes" + cp -a "${COOKBOOK_STAGE}/usr/metatypes/"* "${SYSROOT}/metatypes/" 2>/dev/null || true +fi """ diff --git a/local/recipes/qt/qtpositioning/recipe.toml b/local/recipes/qt/qtpositioning/recipe.toml new file mode 100644 index 0000000000..247517b2d1 --- /dev/null +++ b/local/recipes/qt/qtpositioning/recipe.toml @@ -0,0 +1,101 @@ +[package] +name = "qtpositioning" +version = "6.11" + +#TODO: Qt6 Positioning module — geo-coordinate/positioning API. +# Needed by knighttime (solar position for the night-colour schedule), which is +# in turn needed by plasma-workspace's kcms/nighttime and kwin's nightlight +# plugin. Provides: libQt6Positioning (+ QML plugin via qtdeclarative). +[build] +template = "custom" +dependencies = [ + "qtbase", + "qtdeclarative", +] +script = """ +DYNAMIC_INIT + +HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build" +source "${COOKBOOK_ROOT}/local/scripts/lib/qt-sysroot.sh" + +redbear_qt_link_sysroot_dirs "${COOKBOOK_SYSROOT}" plugins mkspecs metatypes modules qml + +if [ -f "${COOKBOOK_SYSROOT}/lib/libredbear-qt-strtold-compat.so" ]; then + mkdir -p "${COOKBOOK_SYSROOT}/usr/lib" + cp -f "${COOKBOOK_SYSROOT}/lib/libredbear-qt-strtold-compat.so" "${COOKBOOK_SYSROOT}/usr/lib/" 2>/dev/null || true +fi + +redbear_qt_reset_cmake_cache_dir + +cmake "${COOKBOOK_SOURCE}" \ + -DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \ + -DQT_HOST_PATH="${HOST_BUILD}" \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \ + -DQT_BUILD_EXAMPLES=OFF \ + -DQT_BUILD_TESTS=OFF \ + -DQT_GENERATE_SBOM=OFF \ + -DFEATURE_geoservices_nmea=OFF \ + -Wno-dev + +cmake --build . -j${COOKBOOK_MAKE_JOBS} + +cmake --install . --prefix "${COOKBOOK_STAGE}/usr" + +redbear_qt_dual_stage_plugins "${COOKBOOK_STAGE}" + +for lib in lib/libQt6*.so*; do + [ -f "${lib}" ] && cp -an "${lib}" "${COOKBOOK_STAGE}/usr/lib/" +done +for lib in lib/libQt6*.a; do + [ -f "${lib}" ] && cp -an "${lib}" "${COOKBOOK_STAGE}/usr/lib/" +done + +if [ -d "lib/cmake" ]; then + mkdir -p "${COOKBOOK_STAGE}/usr/lib/cmake" + cp -a lib/cmake/Qt6* "${COOKBOOK_STAGE}/usr/lib/cmake/" 2>/dev/null || true +fi +if [ -d "lib/pkgconfig" ]; then + mkdir -p "${COOKBOOK_STAGE}/usr/lib/pkgconfig" + cp -a lib/pkgconfig/*.pc "${COOKBOOK_STAGE}/usr/lib/pkgconfig/" 2>/dev/null || true +fi +if [ -d "include" ]; then + mkdir -p "${COOKBOOK_STAGE}/usr/include" + cp -a include/* "${COOKBOOK_STAGE}/usr/include/" 2>/dev/null || true +fi +if [ -d "plugins" ]; then + mkdir -p "${COOKBOOK_STAGE}/usr/plugins" + cp -a plugins/* "${COOKBOOK_STAGE}/usr/plugins/" 2>/dev/null || true +fi +if [ -d "qml" ]; then + mkdir -p "${COOKBOOK_STAGE}/usr/qml" + cp -a qml/* "${COOKBOOK_STAGE}/usr/qml/" 2>/dev/null || true +fi +mkdir -p "${COOKBOOK_STAGE}/usr/metatypes" +find . -path '*/meta_types/*.json' -type f -exec cp -an {} "${COOKBOOK_STAGE}/usr/metatypes/" \\; 2>/dev/null || true + +for lib in "${COOKBOOK_STAGE}/usr/lib/libQt6"*.so.*; do + [ -f "${lib}" ] || continue + patchelf --remove-rpath "${lib}" 2>/dev/null || true +done +find "${COOKBOOK_STAGE}/usr/plugins" -name '*.so' -exec patchelf --remove-rpath {} + 2>/dev/null || true + +BUILD_DIR="${COOKBOOK_BUILD}" +STAGE_USR="${COOKBOOK_STAGE}/usr" +SYSROOT="${COOKBOOK_SYSROOT}" +redbear_qt_rewrite_stage_build_paths "${STAGE_USR}" "${BUILD_DIR}" +redbear_qt_rewrite_stage_include_paths "${STAGE_USR}/lib/cmake/Qt6Positioning" "${SYSROOT}" +redbear_qt_rewrite_usr_src_metatype_paths "${STAGE_USR}/lib/cmake/Qt6Positioning" "${SYSROOT}" +redbear_qt_rewrite_stage_lib_paths "${STAGE_USR}/lib/cmake/Qt6Positioning" "${SYSROOT}" +redbear_qt_rewrite_stage_include_paths "${STAGE_USR}/lib/cmake/Qt6PositioningPrivate" "${SYSROOT}" +redbear_qt_rewrite_stage_lib_paths "${STAGE_USR}/lib/cmake/Qt6PositioningPrivate" "${SYSROOT}" +redbear_qt_copy_common_stage_to_sysroot "${STAGE_USR}" "${SYSROOT}" +redbear_qt_copy_stage_cmake_subdir_to_sysroot "${STAGE_USR}" "${SYSROOT}" "Qt6Positioning" +redbear_qt_copy_stage_cmake_subdir_to_sysroot "${STAGE_USR}" "${SYSROOT}" "Qt6PositioningPrivate" +redbear_qt_copy_optional_stage_dir_to_sysroot "${STAGE_USR}" "${SYSROOT}" metatypes +""" + +[source] +tar = "https://download.qt.io/official_releases/qt/6.11/6.11.1/submodules/qtpositioning-everywhere-src-6.11.1.tar.xz" +blake3 = "d6af6b1c13d2b162062bf6475dad4b1950760a78c25b1996adbdeb16d34a99ba" diff --git a/local/recipes/qt/qtspeech/recipe.toml b/local/recipes/qt/qtspeech/recipe.toml new file mode 100644 index 0000000000..7630619467 --- /dev/null +++ b/local/recipes/qt/qtspeech/recipe.toml @@ -0,0 +1,130 @@ +[package] +name = "qtspeech" +version = "6.11" + +#TODO: Qt6 TextToSpeech module — provides libQt6TextToSpeech. +# Required (unconditionally, with no cmake gate) by kf6-ktexteditor: +# find_package(Qt6 ... REQUIRED Core Widgets Qml PrintSupport TextToSpeech) +# and kf6-ktexteditor is in turn genuinely used by plasma-workspace's +# interactiveconsole (KTextEditor::Document / ::View via the katepart plugin). +# +# Only the API library is built. Every actual speech engine plugin +# (flite, speechdispatcher, sapi, winrt, android, darwin) targets a platform +# Redox does not have, so none is selected -- the module still provides the +# complete QtTextToSpeech API surface that ktexteditor links against. Wiring a +# real engine is separate future work; nothing in the desktop path speaks. +[build] +template = "custom" +dependencies = [ + "qtbase", + "qtdeclarative", + # MANDATORY, not optional. qtspeech's top-level CMakeLists.txt does: + # if(NOT TARGET Qt6::Multimedia) + # message(NOTICE "Skipping the build ...") + # return() + # endif() + # A NOTICE + return() means cmake exits 0, make has nothing to do, and + # install stages nothing -- the recipe "succeeds" while producing an empty + # package. Without this dep that is exactly what happened: a 136-byte + # stage.pkgar, no libQt6TextToSpeech, no cmake config, and kf6-ktexteditor + # then failing on a missing Qt6TextToSpeech that looked unrelated. + "qtmultimedia", +] +script = """ +DYNAMIC_INIT + +HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build" +source "${COOKBOOK_ROOT}/local/scripts/lib/qt-sysroot.sh" + +redbear_qt_link_sysroot_dirs "${COOKBOOK_SYSROOT}" plugins mkspecs metatypes modules qml + +if [ -f "${COOKBOOK_SYSROOT}/lib/libredbear-qt-strtold-compat.so" ]; then + mkdir -p "${COOKBOOK_SYSROOT}/usr/lib" + cp -f "${COOKBOOK_SYSROOT}/lib/libredbear-qt-strtold-compat.so" "${COOKBOOK_SYSROOT}/usr/lib/" 2>/dev/null || true +fi + +redbear_qt_reset_cmake_cache_dir + +cmake "${COOKBOOK_SOURCE}" \ + -DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \ + -DQT_HOST_PATH="${HOST_BUILD}" \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \ + -DQT_BUILD_EXAMPLES=OFF \ + -DQT_BUILD_TESTS=OFF \ + -DQT_GENERATE_SBOM=OFF \ + -DFEATURE_flite=OFF \ + -DFEATURE_flite_alsa=OFF \ + -DFEATURE_speechd=OFF \ + -Wno-dev + +cmake --build . -j${COOKBOOK_MAKE_JOBS} + +# Fail loudly if upstream's silent `return()` guard fired. qtspeech skips its +# entire build with only a cmake NOTICE when Qt6::Multimedia is absent, which +# otherwise yields a green "cook qtspeech - successful" over an empty stage. +# A recipe that produces no artifact must not report success. +if [ ! -f lib/libQt6TextToSpeech.so ] && [ -z "$(find . -name 'libQt6TextToSpeech.so*' -print -quit)" ]; then + echo "ERROR: qtspeech produced no libQt6TextToSpeech." >&2 + echo " Upstream skips the whole build when Qt6::Multimedia is missing" >&2 + echo " (CMakeLists.txt: 'Skipping the build as the condition" >&2 + echo " \\"TARGET Qt6::Multimedia\\" is not met'). Check that qtmultimedia" >&2 + echo " is staged into this recipe's sysroot." >&2 + exit 1 +fi + +cmake --install . --prefix "${COOKBOOK_STAGE}/usr" + +redbear_qt_dual_stage_plugins "${COOKBOOK_STAGE}" + +for lib in lib/libQt6*.so*; do + [ -f "${lib}" ] && cp -an "${lib}" "${COOKBOOK_STAGE}/usr/lib/" +done +for lib in lib/libQt6*.a; do + [ -f "${lib}" ] && cp -an "${lib}" "${COOKBOOK_STAGE}/usr/lib/" +done + +if [ -d "lib/cmake" ]; then + mkdir -p "${COOKBOOK_STAGE}/usr/lib/cmake" + cp -a lib/cmake/Qt6* "${COOKBOOK_STAGE}/usr/lib/cmake/" 2>/dev/null || true +fi +if [ -d "lib/pkgconfig" ]; then + mkdir -p "${COOKBOOK_STAGE}/usr/lib/pkgconfig" + cp -a lib/pkgconfig/*.pc "${COOKBOOK_STAGE}/usr/lib/pkgconfig/" 2>/dev/null || true +fi +if [ -d "include" ]; then + mkdir -p "${COOKBOOK_STAGE}/usr/include" + cp -a include/* "${COOKBOOK_STAGE}/usr/include/" 2>/dev/null || true +fi +if [ -d "plugins" ]; then + mkdir -p "${COOKBOOK_STAGE}/usr/plugins" + cp -a plugins/* "${COOKBOOK_STAGE}/usr/plugins/" 2>/dev/null || true +fi +mkdir -p "${COOKBOOK_STAGE}/usr/metatypes" +find . -path '*/meta_types/*.json' -type f -exec cp -an {} "${COOKBOOK_STAGE}/usr/metatypes/" \\; 2>/dev/null || true + +for lib in "${COOKBOOK_STAGE}/usr/lib/libQt6"*.so.*; do + [ -f "${lib}" ] || continue + patchelf --remove-rpath "${lib}" 2>/dev/null || true +done +find "${COOKBOOK_STAGE}/usr/plugins" -name '*.so' -exec patchelf --remove-rpath {} + 2>/dev/null || true + +BUILD_DIR="${COOKBOOK_BUILD}" +STAGE_USR="${COOKBOOK_STAGE}/usr" +SYSROOT="${COOKBOOK_SYSROOT}" +redbear_qt_rewrite_stage_build_paths "${STAGE_USR}" "${BUILD_DIR}" +redbear_qt_rewrite_stage_include_paths "${STAGE_USR}/lib/cmake/Qt6TextToSpeech" "${SYSROOT}" +redbear_qt_rewrite_usr_src_metatype_paths "${STAGE_USR}/lib/cmake/Qt6TextToSpeech" "${SYSROOT}" +redbear_qt_rewrite_stage_lib_paths "${STAGE_USR}/lib/cmake/Qt6TextToSpeech" "${SYSROOT}" +redbear_qt_rewrite_stage_include_paths "${STAGE_USR}/lib/cmake/Qt6TextToSpeechPrivate" "${SYSROOT}" +redbear_qt_rewrite_stage_lib_paths "${STAGE_USR}/lib/cmake/Qt6TextToSpeechPrivate" "${SYSROOT}" +redbear_qt_copy_common_stage_to_sysroot "${STAGE_USR}" "${SYSROOT}" +redbear_qt_copy_stage_cmake_subdir_to_sysroot "${STAGE_USR}" "${SYSROOT}" "Qt6TextToSpeech" +redbear_qt_copy_stage_cmake_subdir_to_sysroot "${STAGE_USR}" "${SYSROOT}" "Qt6TextToSpeechPrivate" +redbear_qt_copy_optional_stage_dir_to_sysroot "${STAGE_USR}" "${SYSROOT}" metatypes +""" + +[source] +tar = "https://download.qt.io/official_releases/qt/6.11/6.11.1/submodules/qtspeech-everywhere-src-6.11.1.tar.xz" +blake3 = "611f308bf5beda306c3b724409a7bb5cf87e23a11e2aaccbc180d788757e5331" diff --git a/local/recipes/qt/qttools/recipe.toml b/local/recipes/qt/qttools/recipe.toml new file mode 100644 index 0000000000..4d551aaa45 --- /dev/null +++ b/local/recipes/qt/qttools/recipe.toml @@ -0,0 +1,108 @@ +[package] +name = "qttools" +version = "6.11" + +#TODO: Qt6 Tools — built here for Qt6::UiTools (libQt6UiTools), the runtime +# .ui form loader (QUiLoader). +# +# Why: kwin's kcm_kwin4_genericscripted (the configuration UI for scripted +# window-manager effects) includes QUiLoader and links Qt::UiTools. Without +# this module the kwin recipe has to strip that KCM out entirely, which is a +# real loss of desktop functionality rather than an X11-only or +# Linux-kernel-only feature -- so it gets ported instead of gated. +# +# Scope: UiTools lives under src/designer/, which is gated by +# QT_FEATURE_designer, so that feature is ON. The heavyweight documentation and +# translator applications are OFF: assistant and qdoc need Clang for doc +# parsing, and none of them is a runtime dependency of the desktop. Linguist's +# lrelease/lupdate are HOST tools supplied via QT_HOST_PATH, not target +# binaries. +[build] +template = "custom" +dependencies = [ + "qtbase", + "qtdeclarative", +] +script = """ +DYNAMIC_INIT + +HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build" +source "${COOKBOOK_ROOT}/local/scripts/lib/qt-sysroot.sh" + +redbear_qt_link_sysroot_dirs "${COOKBOOK_SYSROOT}" plugins mkspecs metatypes modules qml + +if [ -f "${COOKBOOK_SYSROOT}/lib/libredbear-qt-strtold-compat.so" ]; then + mkdir -p "${COOKBOOK_SYSROOT}/usr/lib" + cp -f "${COOKBOOK_SYSROOT}/lib/libredbear-qt-strtold-compat.so" "${COOKBOOK_SYSROOT}/usr/lib/" 2>/dev/null || true +fi + +redbear_qt_reset_cmake_cache_dir + +cmake "${COOKBOOK_SOURCE}" \ + -DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \ + -DQT_HOST_PATH="${HOST_BUILD}" \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \ + -DQT_BUILD_EXAMPLES=OFF \ + -DQT_BUILD_TESTS=OFF \ + -DQT_GENERATE_SBOM=OFF \ + -DFEATURE_designer=ON \ + -DFEATURE_assistant=OFF \ + -DFEATURE_clang=OFF \ + -DFEATURE_qdoc=OFF \ + -Wno-dev + +cmake --build . -j${COOKBOOK_MAKE_JOBS} + +cmake --install . --prefix "${COOKBOOK_STAGE}/usr" + +redbear_qt_dual_stage_plugins "${COOKBOOK_STAGE}" + +for lib in lib/libQt6*.so*; do + [ -f "${lib}" ] && cp -an "${lib}" "${COOKBOOK_STAGE}/usr/lib/" +done +for lib in lib/libQt6*.a; do + [ -f "${lib}" ] && cp -an "${lib}" "${COOKBOOK_STAGE}/usr/lib/" +done + +if [ -d "lib/cmake" ]; then + mkdir -p "${COOKBOOK_STAGE}/usr/lib/cmake" + cp -a lib/cmake/Qt6* "${COOKBOOK_STAGE}/usr/lib/cmake/" 2>/dev/null || true +fi +if [ -d "lib/pkgconfig" ]; then + mkdir -p "${COOKBOOK_STAGE}/usr/lib/pkgconfig" + cp -a lib/pkgconfig/*.pc "${COOKBOOK_STAGE}/usr/lib/pkgconfig/" 2>/dev/null || true +fi +if [ -d "include" ]; then + mkdir -p "${COOKBOOK_STAGE}/usr/include" + cp -a include/* "${COOKBOOK_STAGE}/usr/include/" 2>/dev/null || true +fi +if [ -d "plugins" ]; then + mkdir -p "${COOKBOOK_STAGE}/usr/plugins" + cp -a plugins/* "${COOKBOOK_STAGE}/usr/plugins/" 2>/dev/null || true +fi +mkdir -p "${COOKBOOK_STAGE}/usr/metatypes" +find . -path '*/meta_types/*.json' -type f -exec cp -an {} "${COOKBOOK_STAGE}/usr/metatypes/" \\; 2>/dev/null || true + +for lib in "${COOKBOOK_STAGE}/usr/lib/libQt6"*.so.*; do + [ -f "${lib}" ] || continue + patchelf --remove-rpath "${lib}" 2>/dev/null || true +done +find "${COOKBOOK_STAGE}/usr/plugins" -name '*.so' -exec patchelf --remove-rpath {} + 2>/dev/null || true + +BUILD_DIR="${COOKBOOK_BUILD}" +STAGE_USR="${COOKBOOK_STAGE}/usr" +SYSROOT="${COOKBOOK_SYSROOT}" +redbear_qt_rewrite_stage_build_paths "${STAGE_USR}" "${BUILD_DIR}" +redbear_qt_rewrite_stage_include_paths "${STAGE_USR}/lib/cmake/Qt6UiTools" "${SYSROOT}" +redbear_qt_rewrite_usr_src_metatype_paths "${STAGE_USR}/lib/cmake/Qt6UiTools" "${SYSROOT}" +redbear_qt_rewrite_stage_lib_paths "${STAGE_USR}/lib/cmake/Qt6UiTools" "${SYSROOT}" +redbear_qt_copy_common_stage_to_sysroot "${STAGE_USR}" "${SYSROOT}" +redbear_qt_copy_stage_cmake_subdir_to_sysroot "${STAGE_USR}" "${SYSROOT}" "Qt6UiTools" +redbear_qt_copy_optional_stage_dir_to_sysroot "${STAGE_USR}" "${SYSROOT}" metatypes +""" + +[source] +tar = "https://download.qt.io/official_releases/qt/6.11/6.11.1/submodules/qttools-everywhere-src-6.11.1.tar.xz" +blake3 = "ba0575bb3187c52c2b4924fba5353cc26c2b8751861b59df2463e37ecdeec932" diff --git a/local/recipes/qt/redox-toolchain.cmake b/local/recipes/qt/redox-toolchain.cmake index 50cc296b32..642d4c5a72 100644 --- a/local/recipes/qt/redox-toolchain.cmake +++ b/local/recipes/qt/redox-toolchain.cmake @@ -136,6 +136,31 @@ if(DEFINED ENV{COOKBOOK_SYSROOT} AND EXISTS "$ENV{COOKBOOK_SYSROOT}/lib") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${_redbear_sysroot_link_flags}" CACHE STRING "" FORCE) endif() +# --- libgcc soft-float helpers for shared libraries (GCC 16) --- +# C++23 instantiates std::formatter<_Float16> / std::formatter, +# whose bodies widen via the libgcc builtins __extendhfsf2 / __extendbfsf2 (a libcall +# at -march=x86-64, which has no F16C). Those two symbols are GLOBAL (T) in the static +# libgcc.a but LOCAL (t, hidden) in libgcc_s.so.1, because the Redox libgcc version +# map stops at GCC_7.0.0 and never emits the GCC_12.0.0 / GCC_13.0.0 nodes that export +# them upstream (host glibc libgcc_s.so.1 does: __extendhfsf2@GCC_12.0.0, +# __extendbfsf2@GCC_13.0.0). GCC's driver spec deliberately omits the trailing -lgcc +# when linking -shared, so nothing can resolve them and any C++ shared object built +# with -Wl,--no-undefined fails to link (first observed: kwin libkwin.so). +# +# Appending the static libgcc.a supplies the real libgcc implementations — these are +# pure leaf conversion routines with no unwinder state, so binding them statically into +# a shared object is semantically identical to resolving them from libgcc_s at runtime. +# This is a link-sequence repair, not a stub. +# +# It MUST go in CMAKE_*_STANDARD_LIBRARIES (end of the link line), not in +# CMAKE_*_LINKER_FLAGS: CMake emits linker flags BEFORE the object files, and the +# linker only pulls members from an archive it sees AFTER the references to them. +# +# Proper upstream fix (tracked separately): teach the gcc16 recipe's libgcc build to +# emit the missing i386 version-script nodes so libgcc_s.so.1 exports these directly. +set(CMAKE_C_STANDARD_LIBRARIES_INIT "${CMAKE_C_STANDARD_LIBRARIES_INIT} -lgcc") +set(CMAKE_CXX_STANDARD_LIBRARIES_INIT "${CMAKE_CXX_STANDARD_LIBRARIES_INIT} -lgcc") + if(DEFINED ENV{COOKBOOK_SYSROOT} AND EXISTS "$ENV{COOKBOOK_SYSROOT}/lib/libredbear-qt-strtold-compat.so") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-as-needed -L$ENV{COOKBOOK_SYSROOT}/lib -lredbear-qt-strtold-compat" CACHE STRING "" FORCE) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-as-needed -L$ENV{COOKBOOK_SYSROOT}/lib -lredbear-qt-strtold-compat" CACHE STRING "" FORCE) diff --git a/local/recipes/system/redbear-authd/source/Cargo.lock b/local/recipes/system/redbear-authd/source/Cargo.lock index 15914a85f0..559dba8ed0 100644 --- a/local/recipes/system/redbear-authd/source/Cargo.lock +++ b/local/recipes/system/redbear-authd/source/Cargo.lock @@ -102,6 +102,15 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.12.1" @@ -111,12 +120,49 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "blowfish" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32fa6a061124e37baba002e496d203e23ba3d7b73750be82dbfbc92913048a5b" +dependencies = [ + "byteorder", + "cipher 0.2.5", + "opaque-debug", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cipher" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" +dependencies = [ + "block-buffer 0.12.1", + "crypto-common", + "inout", +] + [[package]] name = "cmov" version = "0.5.4" @@ -135,6 +181,15 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "cpufeatures" version = "0.3.0" @@ -159,6 +214,16 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "crypto-mac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "ctutils" version = "0.4.2" @@ -199,14 +264,24 @@ dependencies = [ "thiserror", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ - "block-buffer", + "block-buffer 0.12.1", "crypto-common", + "ctutils", ] [[package]] @@ -232,6 +307,57 @@ dependencies = [ "log", ] +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi", +] + +[[package]] +name = "hmac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.3", +] + [[package]] name = "hybrid-array" version = "0.4.13" @@ -241,6 +367,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "inout" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" +dependencies = [ + "hybrid-array", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -310,6 +445,17 @@ dependencies = [ "base64ct", ] +[[package]] +name = "md-5" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "memchr" version = "2.8.3" @@ -322,11 +468,42 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "password-hash" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aab41826031698d6ffcd9cff78ef56ef998e39dc7e5067cdfebe373842d4723b" +dependencies = [ + "getrandom 0.4.3", + "phc", +] + +[[package]] +name = "pbkdf2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112d82ceb8c5bf524d9af484d4e4970c9fd5a0cc15ba14ad93dccd28873b0629" +dependencies = [ + "digest 0.11.3", + "hmac 0.13.0", +] + +[[package]] +name = "phc" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44dc769b75f93afdddd8c7fa12d685292ddeff1e66f7f0f3a234cf1818afe892" +dependencies = [ + "base64ct", + "ctutils", + "getrandom 0.4.3", +] [[package]] name = "portable-atomic" @@ -343,6 +520,15 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -352,6 +538,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pwhash" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419a3ad8fa9f9d445e69d9b185a24878ae6e6f55c96e4512f4a0e28cd3bc5c56" +dependencies = [ + "blowfish", + "byteorder", + "hmac 0.10.1", + "md-5", + "rand", + "sha-1", + "sha2 0.9.9", +] + [[package]] name = "quote" version = "1.0.46" @@ -362,21 +563,59 @@ dependencies = [ ] [[package]] -name = "redbear-authd" +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "redbear-authd" +version = "0.3.2" dependencies = [ "env_logger", "log", + "pwhash", "redbear-login-protocol", "rust-argon2", "serde", "serde_json", "sha-crypt", + "yescrypt", ] [[package]] name = "redbear-login-protocol" -version = "0.3.1" +version = "0.3.2" dependencies = [ "serde", "serde_json", @@ -423,6 +662,16 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "salsa20" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f874456e72520ff1375a06c588eaf074b0f01f9e9e1aada45bd9b7954a6e42c" +dependencies = [ + "cfg-if", + "cipher 0.5.2", +] + [[package]] name = "serde" version = "1.0.228" @@ -466,6 +715,19 @@ dependencies = [ "zmij", ] +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha-crypt" version = "0.6.0" @@ -476,7 +738,20 @@ dependencies = [ "ctutils", "mcf", "password-hash", - "sha2", + "sha2 0.11.0", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.9.0", + "opaque-debug", ] [[package]] @@ -486,10 +761,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.3.0", + "digest 0.11.3", ] +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + [[package]] name = "syn" version = "2.0.118" @@ -550,6 +831,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "windows-link" version = "0.2.1" @@ -565,6 +858,41 @@ dependencies = [ "windows-link", ] +[[package]] +name = "yescrypt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02bbacd58fdb2f95e4f18bb76819d9a9bfa9b2df0774ae294428652782862a6" +dependencies = [ + "ctutils", + "hmac 0.13.0", + "mcf", + "password-hash", + "pbkdf2", + "salsa20", + "sha2 0.11.0", +] + +[[package]] +name = "zerocopy" +version = "0.8.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "zmij" version = "1.0.21" diff --git a/local/recipes/system/redbear-authd/source/Cargo.toml b/local/recipes/system/redbear-authd/source/Cargo.toml index 33874359e9..3819a2023d 100644 --- a/local/recipes/system/redbear-authd/source/Cargo.toml +++ b/local/recipes/system/redbear-authd/source/Cargo.toml @@ -20,3 +20,9 @@ serde_json = "1" # Pure-Rust SHA-256/SHA-512 crypt verifier for /etc/shadow entries. # Free/open-source (`MIT OR Apache-2.0` upstream; acceptable under the project's free-software policy). sha-crypt = "0.6.0-rc.4" +# yescrypt ($y$) -- the default /etc/shadow format on current Debian/Ubuntu/Fedora. +# Free/open-source (`MIT OR Apache-2.0`). +yescrypt = { version = "0.1", features = ["password-hash"] } +# Classic crypt(3) formats for migrated shadow files: $1$ md5-crypt and +# $2a$/$2b$/$2y$ bcrypt. Free/open-source (`MIT`). +pwhash = "1" diff --git a/local/recipes/system/redbear-authd/source/src/main.rs b/local/recipes/system/redbear-authd/source/src/main.rs index 5e2c9f5e45..03e6cffd98 100644 --- a/local/recipes/system/redbear-authd/source/src/main.rs +++ b/local/recipes/system/redbear-authd/source/src/main.rs @@ -15,6 +15,7 @@ use argon2::{self, verify_encoded}; use redbear_login_protocol::{AuthRequest, AuthResponse}; use serde::Serialize; use sha_crypt::{PasswordVerifier, ShaCrypt}; +use yescrypt::Yescrypt; #[derive(Debug, PartialEq, Eq)] enum VerifyError { @@ -191,9 +192,42 @@ fn verify_shadow_password(password: &str, shadow_hash: &str) -> Result bool { + Path::new(PLAINTEXT_SENTINEL).exists() +} + fn verify_password(account: &Account, password: &str) -> bool { if account.password.is_empty() || account.password.starts_with('!') || account.password.starts_with('*') { return false; @@ -212,7 +246,39 @@ fn verify_password(account: &Account, password: &str) -> bool { } } - account.password == password + // Anything that is not a `$id$`-prefixed crypt hash is a PLAINTEXT shadow + // entry. Comparing it directly is what this code used to do unconditionally. + // + // It is not reachable on an installed system -- the installer always writes + // an argon2 hash (installer.rs hash_password -> argon2::hash_encoded) and an + // unset password becomes "", rejected above. But Red Bear is still under + // development: hand-written test images, QEMU scratch rootfs and bring-up + // flows do populate plaintext entries, and silently refusing them would lock + // developers out of their own images with no obvious cause. + // + // So it stays available, but never silently and never by default: it + // requires the sentinel file below (same pattern as + // /etc/redbear/enable-legacy-greeter) and logs a warning on every use, so a + // plaintext-authenticating image cannot be mistaken for a hardened one. + if plaintext_passwords_enabled() { + log::warn!( + "redbear-authd: SECURITY: authenticating user {} against a PLAINTEXT \ + shadow entry, permitted only because {} exists. Remove that file \ + before shipping this image.", + account.username, + PLAINTEXT_SENTINEL + ); + return account.password == password; + } + + log::error!( + "redbear-authd: refusing non-hashed password entry for user {} (shadow \ + field is not a $id$ crypt hash). For development images, create {} to \ + allow plaintext entries.", + account.username, + PLAINTEXT_SENTINEL + ); + false } fn remember_success(state: &RuntimeState, username: &str, vt: u32) -> Result<(), String> { @@ -538,14 +604,20 @@ mod tests { } #[test] - fn verify_password_accepts_plain_passwords() { + fn verify_password_refuses_non_hashed_entries_by_default() { + // Without the sentinel file, a bare (unhashed) shadow field must NEVER + // authenticate, even when it matches the supplied password verbatim. + // (The test host is not expected to have the sentinel present.) + if plaintext_passwords_enabled() { + return; // sentinel present on this host; covered by the dev path + } let account = Account { username: String::from("root"), password: String::from("password"), uid: 0, shell: String::from("/usr/bin/ion"), }; - assert!(verify_password(&account, "password")); + assert!(!verify_password(&account, "password")); assert!(!verify_password(&account, "wrong")); } @@ -574,7 +646,46 @@ mod tests { #[test] fn verify_shadow_password_rejects_unknown_hash_prefix() { - assert_eq!(verify_shadow_password("password", "$1$legacy$hash"), Err(VerifyError::UnsupportedHashFormat)); + assert_eq!( + verify_shadow_password("password", "$unknown$legacy$hash"), + Err(VerifyError::UnsupportedHashFormat) + ); + } + + #[test] + fn verify_shadow_password_accepts_yescrypt() { + // yescrypt is the default shadow format on current Debian/Ubuntu/Fedora, + // so this is the format most likely to arrive from a real system. + // Generated with an explicit salt (hash_password_with_salt) so the test + // needs no RNG feature and stays deterministic. + use yescrypt::PasswordHasher; + let hash = Yescrypt::default() + .hash_password_with_salt(b"password", b"redbearsalt") + .expect("yescrypt hash should encode"); + let hash = hash.as_str(); + assert!(hash.starts_with("$y$"), "expected a $y$ hash, got {hash}"); + assert_eq!(verify_shadow_password("password", hash), Ok(true)); + assert_eq!(verify_shadow_password("wrong", hash), Ok(false)); + } + + #[test] + fn verify_shadow_password_accepts_bcrypt() { + // Generated rather than hard-coded: bcrypt embeds cost+salt in the + // hash, so a literal is easy to get subtly wrong and then "passes" for + // the wrong reason. Round-tripping against the same implementation + // real $2b$ shadow entries use is the meaningful check. + let hash = pwhash::bcrypt::hash("password").expect("bcrypt hash should encode"); + assert!(hash.starts_with("$2"), "expected a $2*$ hash, got {hash}"); + assert_eq!(verify_shadow_password("password", &hash), Ok(true)); + assert_eq!(verify_shadow_password("wrong", &hash), Ok(false)); + } + + #[test] + fn verify_shadow_password_accepts_md5_crypt() { + // $1$ md5-crypt of "password" + let hash = "$1$saltsalt$qjXMvbEw8oaL.CzflDtaK/"; + assert_eq!(verify_shadow_password("password", hash), Ok(true)); + assert_eq!(verify_shadow_password("wrong", hash), Ok(false)); } #[test] diff --git a/local/scripts/build-preflight.sh b/local/scripts/build-preflight.sh index 2d5dfb8b6e..e9881f5d68 100755 --- a/local/scripts/build-preflight.sh +++ b/local/scripts/build-preflight.sh @@ -63,6 +63,26 @@ if [ -x "$SCRIPT_DIR/verify-overlay-integrity.sh" ]; then fi fi +# Tracked vendored source integrity. +# +# local/sources/ (the submodule forks) is protected by two existing gates: the +# dirty-source gate in build-redbear.sh and verify-fork-versions.sh. The +# vendored recipe trees under local/recipes/*/source/ carry equally durable +# committed work and had NO equivalent gate, which allowed two real incidents: +# an `rm -rf /source` deleting thousands of tracked files, and a +# pristine tarball re-extract silently overwriting committed Red Bear fixes +# (kwin's std::expected/vulkan-hpp + X11 gating, kio's Q_OS_REDOX resolver +# guards) with no error raised. This closes that asymmetry. +if [ -x "$SCRIPT_DIR/verify-tracked-sources.sh" ] && [ "${REDBEAR_SKIP_TRACKED_SOURCE_CHECK:-0}" != "1" ]; then + if ! "$SCRIPT_DIR/verify-tracked-sources.sh"; then + echo "" >&2 + echo ">>> ERROR: tracked vendored source trees are damaged (see above)." >&2 + echo " Set REDBEAR_SKIP_TRACKED_SOURCE_CHECK=1 to bypass (DANGEROUS: you are" >&2 + echo " choosing to build from, and potentially commit, a corrupted source tree)." >&2 + exit 1 + fi +fi + # Enforce the "no fake version label" rule (local/AGENTS.md): # every Cat 2 fork's `-rb` version must match the source # content from upstream `` + Red Bear patches, and the `-rb` diff --git a/local/scripts/test-sddm-virgl-qemu.sh b/local/scripts/test-sddm-virgl-qemu.sh index b09bd7c355..e4355460a9 100755 --- a/local/scripts/test-sddm-virgl-qemu.sh +++ b/local/scripts/test-sddm-virgl-qemu.sh @@ -141,6 +141,24 @@ report() { # name regex required fi } +# Negative assertion: the marker's ABSENCE is the pass condition. These must NOT +# go through report(), which passes when the regex MATCHES — using it for a fault +# signature inverts the verdict, so an observed fault prints "PASS". That is how +# the Qt6 Wayland null+8 fault (the known #1 desktop blocker) could be present in +# the serial log and still be reported as a clean run. +report_absent() { # name regex severity + local name="$1" re="$2" severity="$3" + if grep -qiE "$re" "$log" 2>/dev/null; then + echo " FAIL $name" + grep -niE "$re" "$log" | head -3 | sed 's/^/ /' + if [[ "$severity" == "required" ]]; then + rc=1 + fi + else + echo " PASS $name" + fi +} + echo ">>> Boot markers:" report "kernel reached userspace" 'RedoxFS|redox_init|init: |bootstrap' required report "driver-manager started" 'driver-manager' required @@ -151,7 +169,7 @@ report "seatd" 'seatd' report "compositor started" 'redbear-compositor|wayland-0|WAYLAND_DISPLAY' required report "SDDM started" 'sddm' required report "SDDM greeter process" 'sddm-greeter|greeter' required -report "no Wayland null-proxy fault" '(wl_proxy_add_listener.*null|fault.*0x8)' optional +report_absent "no Wayland null-proxy fault" 'wl_proxy_add_listener.*null|fault.*0x8' required echo ">>> Failure signatures:" for sig in 'panic' 'PANIC' 'SIGSEGV' 'page fault' 'Segmentation fault' 'FATAL'; do diff --git a/local/scripts/validate-source-trees.py b/local/scripts/validate-source-trees.py index d9ca61e9b2..0a0f176cff 100755 --- a/local/scripts/validate-source-trees.py +++ b/local/scripts/validate-source-trees.py @@ -19,7 +19,13 @@ def build_lookup(): continue package_name = recipe_toml.parent.name if package_name not in lookup: - lookup[package_name] = recipe_toml.parent + # Resolve through the overlay symlink. `recipes//` is + # frequently a symlink (whole-dir) or contains a symlinked + # recipe.toml pointing into `local/recipes//`, which is + # where the vendored `source/` tree actually lives. Recording the + # unresolved parent made the source lookup miss it and report a + # false MISSING (e.g. xwayland). + lookup[package_name] = recipe_toml.resolve().parent return lookup diff --git a/local/scripts/verify-tracked-sources.sh b/local/scripts/verify-tracked-sources.sh new file mode 100755 index 0000000000..073bc34525 --- /dev/null +++ b/local/scripts/verify-tracked-sources.sh @@ -0,0 +1,222 @@ +#!/usr/bin/env bash +# verify-tracked-sources.sh — integrity gate for git-tracked vendored source trees. +# +# WHY THIS EXISTS +# --------------- +# Several recipes under local/recipes/ are "vendored forks": their source/ tree +# is committed to git and carries real Red Bear work baked directly into files +# that also exist upstream (see local/docs/VERSIONING.md, "baked shim"). That +# tree is durable state, but nothing guarded it, and two failure modes have +# actually happened: +# +# 1. DELETION. An ad-hoc `rm -rf /source` (to force a clean +# re-extract) removes thousands of tracked files. AGENTS.md already warns +# that this "has wiped tracked local/recipes/*/source/ trees in the past". +# +# 2. SILENT OVERWRITE. For a `tar` recipe, deleting source/ makes the +# cookbook re-extract PRISTINE upstream over it. Files that exist upstream +# get their committed Red Bear edits replaced with upstream content. No +# error is raised -- the package may even still build -- so the loss ships +# as a silent regression. This is how kwin lost its std::expected / +# vulkan-hpp and X11-gating fixes, and kio lost the Q_OS_REDOX guards in +# hostinfo.cpp. +# +# Neither mode is detectable by the existing gates: verify-fork-versions.sh +# covers local/sources/ (the submodule forks), and the build-redbear.sh +# dirty-source gate likewise only looks at local/sources/. The vendored recipe +# trees carry equally durable work and had no equivalent check. +# +# WHAT IT CHECKS +# -------------- +# Recipe seds legitimately rewrite tracked source files on every build (that is +# the current in-tree build model), so "modified" alone cannot mean "damaged". +# The gate therefore separates three cases: +# +# DELETED tracked file under */source/ -> ERROR, always. A build never +# legitimately deletes tracked +# source. +# MODIFIED, not in the baseline -> ERROR. Something changed a +# tracked source file that no +# recorded recipe sed accounts +# for -- the overwrite signature. +# MODIFIED, listed in the baseline -> known churn. Reported as a +# durability WARNING (this is +# uncommitted work living in a +# durable tree); escalate to an +# error with +# REDBEAR_STRICT_TRACKED_SOURCES=1. +# +# The baseline (local/tracked-source-baseline.txt) is itself tracked, so the set +# of "expected to be dirty" files is reviewable in git rather than invisible. +# +# USAGE +# verify-tracked-sources.sh [--quiet] # check (preflight mode) +# verify-tracked-sources.sh --update # regenerate the baseline +# verify-tracked-sources.sh --list # show current dirty tracked sources +# +# Bypass: REDBEAR_SKIP_TRACKED_SOURCE_CHECK=1 (emergency only). +# Escalate baseline churn to an error: REDBEAR_STRICT_TRACKED_SOURCES=1. + +set -uo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +BASELINE="$ROOT/local/tracked-source-baseline.txt" + +QUIET=0 +MODE=check +for arg in "$@"; do + case "$arg" in + --quiet) QUIET=1 ;; + --update) MODE=update ;; + --list) MODE=list ;; + -h|--help) + sed -n '2,60p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//' + exit 0 ;; + *) echo "verify-tracked-sources.sh: unknown argument '$arg'" >&2; exit 2 ;; + esac +done + +say() { [ "$QUIET" -eq 1 ] || echo "$@"; } + +cd "$ROOT" || exit 2 + +# Collect tracked files under any local/recipes/**/source/ that git reports as +# changed. -z keeps paths with spaces intact (several KDE avatar assets have +# them, which is exactly where a naive `xargs` split goes wrong). +collect() { # $1 = status filter letter (M or D) + local want="$1" + git status --porcelain -z -- local/recipes 2>/dev/null \ + | while IFS= read -r -d '' entry; do + local st="${entry:0:2}" path="${entry:3}" + case "$st" in + *"$want"*) [[ "$path" == */source/* ]] && printf '%s\n' "$path" ;; + esac + done +} + +mapfile -t DELETED < <(collect D | LC_ALL=C sort) +mapfile -t MODIFIED < <(collect M | LC_ALL=C sort) + +if [ "$MODE" = list ]; then + printf '%s\n' "${MODIFIED[@]}" | sed '/^$/d' + exit 0 +fi + +if [ "$MODE" = update ]; then + { + echo "# tracked-source-baseline.txt" + echo "#" + echo "# Tracked files under local/recipes/*/source/ that are EXPECTED to differ" + echo "# from HEAD during a normal build -- almost all of it recipe-sed churn." + echo "# Consumed by local/scripts/verify-tracked-sources.sh." + echo "#" + echo "# A file listed here is uncommitted work sitting in a DURABLE tree. That is" + echo "# tolerated, not endorsed: commit it, or convert the change into a recipe sed" + echo "# or a patch, so a clean re-extract cannot lose it." + echo "#" + echo "# A line prefixed 'D ' records an INTENTIONAL deletion of a tracked source" + echo "# file (e.g. removing a vendored stub). Without an entry, any deletion is" + echo "# treated as damage." + echo "#" + echo "# Regenerate with: ./local/scripts/verify-tracked-sources.sh --update" + printf '%s\n' "${MODIFIED[@]}" | sed '/^$/d' + printf 'D %s\n' "${DELETED[@]}" | sed '/^D $/d' + } > "$BASELINE" + echo ">>> baseline updated: $(printf '%s\n' "${MODIFIED[@]}" | sed '/^$/d' | wc -l) modified," \ + "$(printf '%s\n' "${DELETED[@]}" | sed '/^$/d' | wc -l) deleted -> $BASELINE" + exit 0 +fi + +# ---- check mode ---------------------------------------------------------- +rc=0 + +if [ "${REDBEAR_SKIP_TRACKED_SOURCE_CHECK:-0}" = "1" ]; then + echo ">>> WARNING: tracked-source integrity check bypassed via REDBEAR_SKIP_TRACKED_SOURCE_CHECK=1." >&2 + exit 0 +fi + +# Load the baseline first: it records both expected modifications (plain lines) +# and intentional deletions ("D "). +declare -A known=() +declare -A known_del=() +if [ -f "$BASELINE" ]; then + while IFS= read -r line; do + [[ -z "$line" || "$line" == \#* ]] && continue + if [[ "$line" == "D "* ]]; then + known_del["${line:2}"]=1 + else + known["$line"]=1 + fi + done < "$BASELINE" +fi + +# 1. Deletions are damage unless explicitly recorded as intentional. +unexpected_del=() +for f in "${DELETED[@]}"; do + [ -z "$f" ] && continue + [ -n "${known_del[$f]:-}" ] || unexpected_del+=("$f") +done +DELETED=("${unexpected_del[@]}") + +if [ "${#DELETED[@]}" -gt 0 ] && [ -n "${DELETED[0]:-}" ]; then + echo "" >&2 + echo ">>> ERROR: ${#DELETED[@]} git-tracked file(s) under a vendored source/ tree are DELETED." >&2 + echo " A build never legitimately deletes tracked source. This is the signature of an" >&2 + echo " ad-hoc 'rm -rf /source'." >&2 + printf ' %s\n' "${DELETED[@]:0:15}" >&2 + [ "${#DELETED[@]}" -gt 15 ] && echo " ... and $(( ${#DELETED[@]} - 15 )) more" >&2 + echo "" >&2 + echo " Restore with: git checkout -- local/recipes//source" >&2 + echo " To force a clean re-extract of a tracked tree, restore it from git afterwards;" >&2 + echo " never delete it." >&2 + rc=1 +fi + +# 2. Modifications the baseline does not account for. +unexpected=() +for f in "${MODIFIED[@]}"; do + [ -z "$f" ] && continue + [ -n "${known[$f]:-}" ] || unexpected+=("$f") +done + +if [ "${#unexpected[@]}" -gt 0 ]; then + echo "" >&2 + echo ">>> ERROR: ${#unexpected[@]} tracked source file(s) changed with no baseline entry." >&2 + echo " Either a recipe gained a new sed, or committed Red Bear work was overwritten" >&2 + echo " (e.g. a pristine tarball re-extract replacing baked-in fixes)." >&2 + printf ' %s\n' "${unexpected[@]:0:15}" >&2 + [ "${#unexpected[@]}" -gt 15 ] && echo " ... and $(( ${#unexpected[@]} - 15 )) more" >&2 + echo "" >&2 + echo " Inspect: git diff -- " >&2 + echo " If the change is LOST WORK: git checkout -- " >&2 + echo " If the change is EXPECTED: ./local/scripts/verify-tracked-sources.sh --update" >&2 + rc=1 +fi + +# 3. Baseline churn: uncommitted work in a durable tree. Visible, not fatal by +# default -- this is the local/recipes/ counterpart of the local/sources/ +# dirty gate in build-redbear.sh. +baseline_dirty=0 +for f in "${MODIFIED[@]}"; do + [ -z "$f" ] && continue + [ -n "${known[$f]:-}" ] && baseline_dirty=$(( baseline_dirty + 1 )) +done + +if [ "$baseline_dirty" -gt 0 ]; then + if [ "${REDBEAR_STRICT_TRACKED_SOURCES:-0}" = "1" ]; then + echo ">>> ERROR: $baseline_dirty uncommitted change(s) in tracked source trees" \ + "(REDBEAR_STRICT_TRACKED_SOURCES=1)." >&2 + rc=1 + else + say ">>> Preflight note: $baseline_dirty uncommitted change(s) in tracked vendored" \ + "source trees (baseline-known)." + say " These live in a DURABLE tree but are not committed; a clean re-extract would" \ + "lose them. Commit them, or move them into a recipe sed / patch." + fi +fi + +if [ "$rc" -eq 0 ]; then + say ">>> Preflight: tracked vendored source trees intact (no deletions, no unexplained edits)." +fi +exit "$rc" diff --git a/local/tracked-source-baseline.txt b/local/tracked-source-baseline.txt new file mode 100644 index 0000000000..19d337dcff --- /dev/null +++ b/local/tracked-source-baseline.txt @@ -0,0 +1,45 @@ +# tracked-source-baseline.txt +# +# Tracked files under local/recipes/*/source/ that are EXPECTED to differ +# from HEAD during a normal build -- almost all of it recipe-sed churn. +# Consumed by local/scripts/verify-tracked-sources.sh. +# +# A file listed here is uncommitted work sitting in a DURABLE tree. That is +# tolerated, not endorsed: commit it, or convert the change into a recipe sed +# or a patch, so a clean re-extract cannot lose it. +# +# A line prefixed 'D ' records an INTENTIONAL deletion of a tracked source +# file (e.g. removing a vendored stub). Without an entry, any deletion is +# treated as damage. +# +# Regenerate with: ./local/scripts/verify-tracked-sources.sh --update +local/recipes/kde/kf6-attica/source/CMakeLists.txt +local/recipes/kde/kf6-kcolorscheme/source/CMakeLists.txt +local/recipes/kde/kf6-kcompletion/source/CMakeLists.txt +local/recipes/kde/kf6-kconfigwidgets/source/CMakeLists.txt +local/recipes/kde/kf6-kiconthemes/source/CMakeLists.txt +local/recipes/kde/kf6-kiconthemes/source/src/CMakeLists.txt +local/recipes/kde/kf6-kio/source/CMakeLists.txt +local/recipes/kde/kf6-kio/source/src/CMakeLists.txt +local/recipes/kde/kf6-kitemmodels/source/CMakeLists.txt +local/recipes/kde/kf6-kitemmodels/source/src/CMakeLists.txt +local/recipes/kde/kf6-kitemviews/source/CMakeLists.txt +local/recipes/kde/kf6-ktextwidgets/source/CMakeLists.txt +local/recipes/kde/kf6-kxmlgui/source/src/CMakeLists.txt +local/recipes/kde/kf6-kxmlgui/source/src/kswitchlanguagedialog_p.cpp +local/recipes/kde/kf6-pty/source/CMakeLists.txt +local/recipes/kde/kf6-solid/source/CMakeLists.txt +local/recipes/kde/kf6-syntaxhighlighting/source/CMakeLists.txt +local/recipes/kde/kirigami/source/src/layouts/toolbarlayout.cpp +local/recipes/kde/kwin/source/CMakeLists.txt +local/recipes/kde/plasma-framework/source/CMakeLists.txt +local/recipes/kde/plasma-workspace/source/CMakeLists.txt +local/recipes/kde/sddm/source/CMakeLists.txt +local/recipes/kde/sddm/source/data/themes/CMakeLists.txt +local/recipes/kde/sddm/source/data/translations/CMakeLists.txt +local/recipes/kde/sddm/source/test/CMakeLists.txt +local/recipes/libs/pam-redbear/source/include/security/pam_appl.h +local/recipes/qt/qtbase/source/src/corelib/global/qtypes.h +local/recipes/qt/qtbase/source/src/network/socket/qnativesocketengine_unix.cpp +local/recipes/system/redbear-authd/source/Cargo.lock +D local/recipes/tui/mc/source/lib/stdckdint.h