{ lib, craneLib, mold, pkg-config, qt6, writeShellScript, runCommand, }: let # nixpkgs splits Qt: tools (moc/rcc/qtpaths in qtbase, qmltyperegistrar/qmlcachegen # in qtdeclarative) and headers (qtbase has Qt6Core etc., qtdeclarative has Qt6Qml # incl. qqmlregistration.h). qt-build-utils derives both from `qmake -query`, which # only knows about qtbase. Symptom of missing headers: moc warns "Potential QML # registration macro was found, but no header containing it was included", silently # strips QML_NAMED_ELEMENT/QML_SINGLETON from the JSON, qmltyperegistrar emits an # empty qml_register_types_*, and the QML import resolves to no types. Fix: combine # tools and headers into symlink trees and point a qmake wrapper at them. qtBuildTools = runCommand "qt6-build-tools" { } '' mkdir -p $out/libexec $out/bin for f in ${qt6.qtbase}/libexec/* ${qt6.qtbase}/bin/*; do ln -sf "$f" "$out/$(echo "$f" | grep -o 'libexec\|bin')/$(basename "$f")" done for tool in qmltyperegistrar qmlcachegen; do src="${qt6.qtdeclarative}/libexec/$tool" [ -f "$src" ] && ln -sf "$src" "$out/libexec/$tool" done ''; # Note: nixpkgs qt6 puts headers in the regular output, not .dev (which only # has mkspecs). Use qt6.qtbase / qt6.qtdeclarative directly. qtIncludeRoots = runCommand "qt6-include-roots" { } '' mkdir -p $out/include for src in ${qt6.qtbase}/include/* ${qt6.qtdeclarative}/include/*; do ln -sfn "$src" "$out/include/$(basename "$src")" done ''; qmakeWrapper = writeShellScript "qmake6" '' if [ "$1" = "-query" ]; then case "$2" in QT_HOST_LIBEXECS|QT_HOST_LIBEXECS/get|QT_INSTALL_LIBEXECS|QT_INSTALL_LIBEXECS/get) echo "${qtBuildTools}/libexec"; exit 0;; QT_HOST_BINS|QT_HOST_BINS/get|QT_INSTALL_BINS|QT_INSTALL_BINS/get) echo "${qtBuildTools}/bin"; exit 0;; QT_HOST_INCLUDES|QT_HOST_INCLUDES/get|QT_INSTALL_HEADERS|QT_INSTALL_HEADERS/get) echo "${qtIncludeRoots}/include"; exit 0;; esac fi exec ${qt6.qtbase}/bin/qmake6 "$@" ''; # Args shared between the deps-only build (cached on Cargo.lock changes only) # and the final crate build. cxx-qt-build runs during the deps build too, so it # needs the Qt env to find qmltyperegistrar/qmlcachegen. # Default cleanCargoSource only keeps .rs/.toml/.lock; also keep the linker # --dynamic-list file referenced by build.rs. pluginSrc = lib.cleanSourceWith { src = ../plugin; name = "nova-plugin-source"; filter = path: type: (baseNameOf path == "qt_plugin_exports.txt") || (craneLib.filterCargoSources path type); }; commonArgs = { pname = "nova-plugin"; version = "0.1.0"; src = pluginSrc; strictDeps = true; nativeBuildInputs = [ pkg-config mold qt6.qtbase qt6.qtdeclarative ]; buildInputs = [ qt6.qtbase qt6.qtdeclarative ]; dontWrapQtApps = true; # qt6.qtbase's setup hook overrides QMAKE after derivation attrs are set, # so re-assert it in preBuild which runs after all setup hooks. # Force mold as the linker (cxx-qt book deprecates ld.gold). Setting via # CARGO_TARGET_*_LINKER + RUSTFLAGS is the only path that survives nixpkgs' # cc-wrapper - `-fuse-ld=` from build.rs gets eaten by the wrapper. preBuild = '' export QMAKE=${qmakeWrapper} export RUSTFLAGS="-C link-arg=-fuse-ld=mold ''${RUSTFLAGS:-}" ''; }; cargoArtifacts = craneLib.buildDepsOnly commonArgs; in craneLib.buildPackage ( commonArgs // { inherit cargoArtifacts; # Crane's default cargo build doesn't set CARGO_BUILD_TARGET, so artifacts land # in `target/release/` and cxx-qt files in `target/cxxqt/...` (no triple subdir, # unlike nixpkgs rustPlatform). Use `find` to stay agnostic to either layout. installPhaseCommand = '' qml_dir="$out/lib/qt-6/qml/NovaStats" mkdir -p "$qml_dir" so=$(find target -name 'libnova_plugin.so' -path '*/release/*' | head -1) install -m755 "$so" "$qml_dir/libNovaStats.so" qmldir=$(find target -path '*/cxxqt/qml_modules/NovaStats/qmldir' | head -1) install -m644 "$qmldir" "$qml_dir/" qmltypes=$(find target -path '*/cxxqt/qml_modules/NovaStats/plugin.qmltypes' | head -1) [ -n "$qmltypes" ] && install -m644 "$qmltypes" "$qml_dir/" || true ''; meta = { description = "In-process system stats QML plugin for nova-shell"; platforms = lib.platforms.linux; }; } )