From bf5cb913fcfbe18a64ef0308728adeff47b30317 Mon Sep 17 00:00:00 2001 From: Damocles Date: Sun, 3 May 2026 20:01:47 +0200 Subject: [PATCH] nix: build nova-plugin via crane to cache deps separately from crate src --- flake.lock | 16 +++++++++ flake.nix | 11 ++++-- nix/plugin.nix | 95 ++++++++++++++++++++++++++++--------------------- plugin/build.rs | 10 +++++- 4 files changed, 88 insertions(+), 44 deletions(-) diff --git a/flake.lock b/flake.lock index 7350926..744b7a5 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,20 @@ { "nodes": { + "crane": { + "locked": { + "lastModified": 1777830388, + "narHash": "sha256-2uoQAqUk2H0ijQtGiWAyNeQYGYc6yfAcRRLlJAz4Gp8=", + "owner": "ipetkov", + "repo": "crane", + "rev": "d459c1350e96ce1a7e3859c513ef5e9869d67d6f", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1776169885, @@ -38,6 +53,7 @@ }, "root": { "inputs": { + "crane": "crane", "nixpkgs": "nixpkgs", "quickshell": "quickshell", "treefmt-nix": "treefmt-nix" diff --git a/flake.nix b/flake.nix index 33cbc64..f9a60e1 100644 --- a/flake.nix +++ b/flake.nix @@ -13,6 +13,8 @@ url = "github:numtide/treefmt-nix"; inputs.nixpkgs.follows = "nixpkgs"; }; + + crane.url = "github:ipetkov/crane"; }; outputs = @@ -21,6 +23,7 @@ nixpkgs, quickshell, treefmt-nix, + crane, ... }: let @@ -57,6 +60,7 @@ fn rec { rawPkgs = nixpkgs.legacyPackages.${system}; pkgs = rawPkgs.extend qtbaseOverlay; + craneLib = crane.mkLib pkgs; treefmt-eval = treefmt-nix.lib.evalModule rawPkgs treefmt-config; } ); @@ -65,7 +69,7 @@ formatter = forAllSystems ({ treefmt-eval, ... }: treefmt-eval.config.build.wrapper); packages = forAllSystems ( - { pkgs, rawPkgs, ... }: + { pkgs, rawPkgs, craneLib, ... }: let # Rebuild quickshell against patched Qt via its overlay qs = (pkgs.extend quickshell.overlays.default).quickshell.override { @@ -74,7 +78,7 @@ }; nova-stats = pkgs.callPackage ./nix/stats-daemon.nix { }; nova-shaders = pkgs.callPackage ./nix/shaders.nix { }; - nova-plugin = pkgs.callPackage ./nix/plugin.nix { }; + nova-plugin = pkgs.callPackage ./nix/plugin.nix { inherit craneLib; }; in rec { inherit nova-stats nova-shaders nova-plugin; @@ -122,12 +126,13 @@ withX11 = false; withI3 = false; }; + craneLibRaw = crane.mkLib rawPkgs; in rawPkgs.callPackage ./nix/package.nix { quickshell = qsUnpatched; nova-stats = rawPkgs.callPackage ./nix/stats-daemon.nix { }; nova-shaders = rawPkgs.callPackage ./nix/shaders.nix { }; - nova-plugin = rawPkgs.callPackage ./nix/plugin.nix { }; + nova-plugin = rawPkgs.callPackage ./nix/plugin.nix { craneLib = craneLibRaw; }; }; } ); diff --git a/nix/plugin.nix b/nix/plugin.nix index 1d5bebc..0dd39e6 100644 --- a/nix/plugin.nix +++ b/nix/plugin.nix @@ -1,6 +1,6 @@ { lib, - rustPlatform, + craneLib, pkg-config, qt6, writeShellScript, @@ -33,47 +33,62 @@ let fi exec ${qt6.qtbase}/bin/qmake6 "$@" ''; -in -rustPlatform.buildRustPackage { - pname = "nova-plugin"; - version = "0.1.0"; - src = lib.cleanSource ../plugin; - cargoLock.lockFile = ../plugin/Cargo.lock; - nativeBuildInputs = [ - pkg-config - qt6.qtbase - qt6.qtdeclarative - ]; + # 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. + commonArgs = { + pname = "nova-plugin"; + version = "0.1.0"; + src = craneLib.cleanCargoSource ../plugin; + strictDeps = true; - buildInputs = [ - qt6.qtbase - qt6.qtdeclarative - ]; + nativeBuildInputs = [ + pkg-config + qt6.qtbase + qt6.qtdeclarative + ]; + buildInputs = [ + qt6.qtbase + qt6.qtdeclarative + ]; - dontWrapQtApps = true; + 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. - preBuild = '' - export QMAKE=${qmakeWrapper} - ''; - - installPhase = '' - runHook preInstall - - qml_dir="$out/lib/qt-6/qml/NovaStats" - mkdir -p "$qml_dir" - - install -m755 target/*/release/libnova_plugin.so "$qml_dir/libNovaStats.so" - install -m644 target/*/cxxqt/qml_modules/NovaStats/qmldir "$qml_dir/" - install -m644 target/*/cxxqt/qml_modules/NovaStats/plugin.qmltypes "$qml_dir/" 2>/dev/null || true - - runHook postInstall - ''; - - meta = { - description = "In-process system stats QML plugin for nova-shell"; - platforms = lib.platforms.linux; + # qt6.qtbase's setup hook overrides QMAKE after derivation attrs are set, + # so re-assert it in preBuild which runs after all setup hooks. + preBuild = '' + export QMAKE=${qmakeWrapper} + ''; }; -} + + 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; + }; + } +) diff --git a/plugin/build.rs b/plugin/build.rs index 8b3b99e..25f5ca8 100644 --- a/plugin/build.rs +++ b/plugin/build.rs @@ -8,7 +8,15 @@ fn main() { // plugin loader finds nothing exported. --export-dynamic re-exposes them. println!("cargo:rustc-link-arg=-Wl,--export-dynamic"); + // Crane's deps-only build stubs out lib.rs and removes our bridge sources to + // build a dummy crate that only compiles dependencies. Skip cxx-qt codegen in + // that case - we just want cxx-qt-lib (the heavy dep) cached, not our own glue. + let bridge_files = ["src/system_stats.rs", "src/cpu_service.rs"]; + if !bridge_files.iter().all(|p| std::path::Path::new(p).exists()) { + return; + } + CxxQtBuilder::new_qml_module(QmlModule::new("NovaStats").plugin_type(PluginType::Dynamic)) - .files(["src/system_stats.rs", "src/cpu_service.rs"]) + .files(bridge_files) .build(); }