nix: build nova-plugin via crane to cache deps separately from crate src

This commit is contained in:
Damocles 2026-05-03 20:01:47 +02:00
parent 2a691aa66f
commit bf5cb913fc
4 changed files with 88 additions and 44 deletions

16
flake.lock generated
View file

@ -1,5 +1,20 @@
{ {
"nodes": { "nodes": {
"crane": {
"locked": {
"lastModified": 1777830388,
"narHash": "sha256-2uoQAqUk2H0ijQtGiWAyNeQYGYc6yfAcRRLlJAz4Gp8=",
"owner": "ipetkov",
"repo": "crane",
"rev": "d459c1350e96ce1a7e3859c513ef5e9869d67d6f",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1776169885, "lastModified": 1776169885,
@ -38,6 +53,7 @@
}, },
"root": { "root": {
"inputs": { "inputs": {
"crane": "crane",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"quickshell": "quickshell", "quickshell": "quickshell",
"treefmt-nix": "treefmt-nix" "treefmt-nix": "treefmt-nix"

View file

@ -13,6 +13,8 @@
url = "github:numtide/treefmt-nix"; url = "github:numtide/treefmt-nix";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
crane.url = "github:ipetkov/crane";
}; };
outputs = outputs =
@ -21,6 +23,7 @@
nixpkgs, nixpkgs,
quickshell, quickshell,
treefmt-nix, treefmt-nix,
crane,
... ...
}: }:
let let
@ -57,6 +60,7 @@
fn rec { fn rec {
rawPkgs = nixpkgs.legacyPackages.${system}; rawPkgs = nixpkgs.legacyPackages.${system};
pkgs = rawPkgs.extend qtbaseOverlay; pkgs = rawPkgs.extend qtbaseOverlay;
craneLib = crane.mkLib pkgs;
treefmt-eval = treefmt-nix.lib.evalModule rawPkgs treefmt-config; treefmt-eval = treefmt-nix.lib.evalModule rawPkgs treefmt-config;
} }
); );
@ -65,7 +69,7 @@
formatter = forAllSystems ({ treefmt-eval, ... }: treefmt-eval.config.build.wrapper); formatter = forAllSystems ({ treefmt-eval, ... }: treefmt-eval.config.build.wrapper);
packages = forAllSystems ( packages = forAllSystems (
{ pkgs, rawPkgs, ... }: { pkgs, rawPkgs, craneLib, ... }:
let let
# Rebuild quickshell against patched Qt via its overlay # Rebuild quickshell against patched Qt via its overlay
qs = (pkgs.extend quickshell.overlays.default).quickshell.override { qs = (pkgs.extend quickshell.overlays.default).quickshell.override {
@ -74,7 +78,7 @@
}; };
nova-stats = pkgs.callPackage ./nix/stats-daemon.nix { }; nova-stats = pkgs.callPackage ./nix/stats-daemon.nix { };
nova-shaders = pkgs.callPackage ./nix/shaders.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 in
rec { rec {
inherit nova-stats nova-shaders nova-plugin; inherit nova-stats nova-shaders nova-plugin;
@ -122,12 +126,13 @@
withX11 = false; withX11 = false;
withI3 = false; withI3 = false;
}; };
craneLibRaw = crane.mkLib rawPkgs;
in in
rawPkgs.callPackage ./nix/package.nix { rawPkgs.callPackage ./nix/package.nix {
quickshell = qsUnpatched; quickshell = qsUnpatched;
nova-stats = rawPkgs.callPackage ./nix/stats-daemon.nix { }; nova-stats = rawPkgs.callPackage ./nix/stats-daemon.nix { };
nova-shaders = rawPkgs.callPackage ./nix/shaders.nix { }; nova-shaders = rawPkgs.callPackage ./nix/shaders.nix { };
nova-plugin = rawPkgs.callPackage ./nix/plugin.nix { }; nova-plugin = rawPkgs.callPackage ./nix/plugin.nix { craneLib = craneLibRaw; };
}; };
} }
); );

View file

@ -1,6 +1,6 @@
{ {
lib, lib,
rustPlatform, craneLib,
pkg-config, pkg-config,
qt6, qt6,
writeShellScript, writeShellScript,
@ -33,47 +33,62 @@ let
fi fi
exec ${qt6.qtbase}/bin/qmake6 "$@" 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 = [ # Args shared between the deps-only build (cached on Cargo.lock changes only)
pkg-config # and the final crate build. cxx-qt-build runs during the deps build too, so it
qt6.qtbase # needs the Qt env to find qmltyperegistrar/qmlcachegen.
qt6.qtdeclarative commonArgs = {
]; pname = "nova-plugin";
version = "0.1.0";
src = craneLib.cleanCargoSource ../plugin;
strictDeps = true;
buildInputs = [ nativeBuildInputs = [
qt6.qtbase pkg-config
qt6.qtdeclarative 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, # qt6.qtbase's setup hook overrides QMAKE after derivation attrs are set,
# so re-assert it in preBuild which runs after all setup hooks. # so re-assert it in preBuild which runs after all setup hooks.
preBuild = '' preBuild = ''
export QMAKE=${qmakeWrapper} 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;
}; };
}
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;
};
}
)

View file

@ -8,7 +8,15 @@ fn main() {
// plugin loader finds nothing exported. --export-dynamic re-exposes them. // plugin loader finds nothing exported. --export-dynamic re-exposes them.
println!("cargo:rustc-link-arg=-Wl,--export-dynamic"); 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)) CxxQtBuilder::new_qml_module(QmlModule::new("NovaStats").plugin_type(PluginType::Dynamic))
.files(["src/system_stats.rs", "src/cpu_service.rs"]) .files(bridge_files)
.build(); .build();
} }