From 6f12c48b933470a8614a85462df8efd3bd70c90d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luka=20Bla=C5=A1kovi=C4=87?= Date: Fri, 27 Mar 2026 05:34:53 +0000 Subject: [PATCH] build-rust-crate: support propagatedBuildInputs in crate overrides Add completePropagatedBuildInputs that collects propagatedBuildInputs from a crate and all its transitive Rust dependencies, analogous to how completeDeps chains .rlib paths. The collected inputs are appended to buildInputs so native library deps (e.g. boost) declared on a library crate automatically propagate to binary crates that depend on it, without requiring repetition in every downstream crate override. --- .../rust/build-rust-crate/default.nix | 14 ++++- .../rust/build-rust-crate/test/default.nix | 63 +++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/pkgs/build-support/rust/build-rust-crate/default.nix b/pkgs/build-support/rust/build-rust-crate/default.nix index ca0d7331daaf..af1ec421da9b 100644 --- a/pkgs/build-support/rust/build-rust-crate/default.nix +++ b/pkgs/build-support/rust/build-rust-crate/default.nix @@ -325,6 +325,7 @@ lib.makeOverridable buildDependencies_ = buildDependencies; processedAttrs = [ "src" + "propagatedBuildInputs" "nativeBuildInputs" "buildInputs" "crateBin" @@ -418,7 +419,8 @@ lib.makeOverridable buildInputs = lib.optionals stdenv.hostPlatform.isDarwin [ libiconv ] ++ (crate.buildInputs or [ ]) - ++ buildInputs_; + ++ buildInputs_ + ++ completePropagatedBuildInputs; dependencies = map lib.getLib dependencies_; buildDependencies = map lib.getLib buildDependencies_; @@ -428,6 +430,16 @@ lib.makeOverridable ++ lib.concatMap (dep: dep.completeBuildDeps ++ dep.completeDeps) buildDependencies ); + # Propagated native build inputs from this crate and all transitive Rust + # dependencies. Analogous to completeDeps but for native library deps: + # a crate can declare `propagatedBuildInputs` in its override and they + # will automatically be added to the buildInputs of every crate that + # depends on it, without having to repeat them up the dependency tree. + completePropagatedBuildInputs = lib.unique ( + (crate.propagatedBuildInputs or [ ]) + ++ lib.concatMap (dep: dep.completePropagatedBuildInputs or [ ]) dependencies + ); + # Create a list of features that are enabled by the crate itself and # through the features argument of buildRustCrate. Exclude features # with a forward slash, since they are passed through to dependencies, diff --git a/pkgs/build-support/rust/build-rust-crate/test/default.nix b/pkgs/build-support/rust/build-rust-crate/test/default.nix index 2c9ada4e3dfd..75a483a0096f 100644 --- a/pkgs/build-support/rust/build-rust-crate/test/default.nix +++ b/pkgs/build-support/rust/build-rust-crate/test/default.nix @@ -1060,6 +1060,69 @@ rec { test -x '${pkg}/bin/rcgen' && touch $out '' ); + + # Test that propagatedBuildInputs declared in a crate override are + # collected by completePropagatedBuildInputs and propagate transitively + # to all crates that depend on it. + propagatedBuildInputsTest = + let + fakeNativeLib = runCommand "fake-native-lib" { } "mkdir -p $out/lib && touch $out/lib/libfoo.a"; + + # Library crate that declares a native dep via propagatedBuildInputs + libCrate = mkHostCrate { + crateName = "mylib"; + src = mkLib "src/lib.rs"; + propagatedBuildInputs = [ fakeNativeLib ]; + }; + + # Binary crate with a direct dependency on libCrate + binCrate = mkHostCrate { + crateName = "mybin"; + src = mkFile "src/main.rs" "fn main() {}"; + dependencies = [ libCrate ]; + }; + + # Intermediate library that depends on libCrate + transitiveLib = mkHostCrate { + crateName = "transitivelib"; + src = mkLib "src/lib.rs"; + dependencies = [ libCrate ]; + }; + + # Binary crate that only depends on transitiveLib (not libCrate directly) + transitiveBin = mkHostCrate { + crateName = "transitivebin"; + src = mkFile "src/main.rs" "fn main() {}"; + dependencies = [ transitiveLib ]; + }; + in + runCommand "propagated-build-inputs-test" + { + libCrateInputs = libCrate.completePropagatedBuildInputs; + binCrateInputs = binCrate.completePropagatedBuildInputs; + transitiveBinInputs = transitiveBin.completePropagatedBuildInputs; + } + '' + # libCrate itself should have fakeNativeLib in completePropagatedBuildInputs + echo "$libCrateInputs" | grep -q "${fakeNativeLib}" || { + echo "ERROR: fakeNativeLib not in libCrate.completePropagatedBuildInputs" + exit 1 + } + + # binCrate depends on libCrate, so fakeNativeLib should propagate + echo "$binCrateInputs" | grep -q "${fakeNativeLib}" || { + echo "ERROR: fakeNativeLib not propagated to binCrate.completePropagatedBuildInputs" + exit 1 + } + + # transitiveBin → transitiveLib → libCrate: fakeNativeLib should propagate transitively + echo "$transitiveBinInputs" | grep -q "${fakeNativeLib}" || { + echo "ERROR: fakeNativeLib not transitively propagated to transitiveBin.completePropagatedBuildInputs" + exit 1 + } + + touch $out + ''; } ); test = releaseTools.aggregate {