diff --git a/pkgs/build-support/rust/build-rust-crate/build-crate.nix b/pkgs/build-support/rust/build-rust-crate/build-crate.nix index d6d1cebb2e91..f82effdbca7c 100644 --- a/pkgs/build-support/rust/build-rust-crate/build-crate.nix +++ b/pkgs/build-support/rust/build-rust-crate/build-crate.nix @@ -1,4 +1,4 @@ -{ lib, stdenv, mkRustcDepArgs, rust }: +{ lib, stdenv, mkRustcDepArgs, mkRustcFeatureArgs, rust }: { crateName, dependencies, crateFeatures, crateRenames, libName, release, libPath, @@ -13,7 +13,7 @@ ++ ["-C codegen-units=$NIX_BUILD_CORES"] ++ ["--remap-path-prefix=$NIX_BUILD_TOP=/" ] ++ [(mkRustcDepArgs dependencies crateRenames)] - ++ [crateFeatures] + ++ [(mkRustcFeatureArgs crateFeatures)] ++ extraRustcOpts ++ lib.optional (stdenv.hostPlatform != stdenv.buildPlatform) "--target ${rust.toRustTarget stdenv.hostPlatform} -C linker=${stdenv.hostPlatform.config}-gcc" # since rustc 1.42 the "proc_macro" crate is part of the default crate prelude diff --git a/pkgs/build-support/rust/build-rust-crate/configure-crate.nix b/pkgs/build-support/rust/build-rust-crate/configure-crate.nix index c47bc00938c1..8e2f5f7f35e0 100644 --- a/pkgs/build-support/rust/build-rust-crate/configure-crate.nix +++ b/pkgs/build-support/rust/build-rust-crate/configure-crate.nix @@ -1,5 +1,6 @@ -{ lib, stdenv, echo_colored, noisily, mkRustcDepArgs }: -{ build +{ lib, stdenv, echo_colored, noisily, mkRustcDepArgs, mkRustcFeatureArgs }: +{ + build , buildDependencies , colors , completeBuildDeps @@ -30,6 +31,9 @@ let version_ = lib.splitString "-" crateVersion; optLevel = if release then 3 else 0; completeDepsDir = lib.concatStringsSep " " completeDeps; completeBuildDepsDir = lib.concatStringsSep " " completeBuildDeps; + envFeatures = lib.concatStringsSep " " ( + map (f: lib.replaceChars ["-"] ["_"] (lib.toUpper f)) crateFeatures + ); in '' ${echo_colored colors} ${noisily colors verbose} @@ -161,14 +165,24 @@ in '' EXTRA_BUILD_FLAGS="$EXTRA_BUILD_FLAGS $(tr '\n' ' ' < target/link.build)" fi noisily rustc --crate-name build_script_build $BUILD --crate-type bin ${rustcOpts} \ - ${crateFeatures} --out-dir target/build/${crateName} --emit=dep-info,link \ + ${mkRustcFeatureArgs crateFeatures} --out-dir target/build/${crateName} --emit=dep-info,link \ -L dependency=target/buildDeps ${buildDeps} --cap-lints allow $EXTRA_BUILD_FLAGS --color ${colors} mkdir -p target/build/${crateName}.out export RUST_BACKTRACE=1 BUILD_OUT_DIR="-L $OUT_DIR" mkdir -p $OUT_DIR - target/build/${crateName}/build_script_build > target/build/${crateName}.opt + + ( + # Features should be set as environment variable for build scripts: + # https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts + for feature in ${envFeatures}; do + export CARGO_FEATURE_$feature=1 + done + + target/build/${crateName}/build_script_build > target/build/${crateName}.opt + ) + set +e EXTRA_BUILD=$(sed -n "s/^cargo:rustc-flags=\(.*\)/\1/p" target/build/${crateName}.opt | tr '\n' ' ' | sort -u) EXTRA_FEATURES=$(sed -n "s/^cargo:rustc-cfg=\(.*\)/--cfg \1/p" target/build/${crateName}.opt | tr '\n' ' ') diff --git a/pkgs/build-support/rust/build-rust-crate/default.nix b/pkgs/build-support/rust/build-rust-crate/default.nix index d9ed26f1d94a..d559aba16165 100644 --- a/pkgs/build-support/rust/build-rust-crate/default.nix +++ b/pkgs/build-support/rust/build-rust-crate/default.nix @@ -45,14 +45,17 @@ let " --extern ${name}=${dep.lib}/lib/lib${extern}-${dep.metadata}${stdenv.hostPlatform.extensions.sharedLibrary}") ) dependencies; + # Create feature arguments for rustc. + mkRustcFeatureArgs = lib.concatMapStringsSep " " (f: ''--cfg feature=\"${f}\"''); + inherit (import ./log.nix { inherit lib; }) noisily echo_colored; configureCrate = import ./configure-crate.nix { - inherit lib stdenv echo_colored noisily mkRustcDepArgs; + inherit lib stdenv echo_colored noisily mkRustcDepArgs mkRustcFeatureArgs; }; buildCrate = import ./build-crate.nix { - inherit lib stdenv mkRustcDepArgs rust; + inherit lib stdenv mkRustcDepArgs mkRustcFeatureArgs rust; }; installCrate = import ./install-crate.nix { inherit stdenv; }; @@ -233,8 +236,11 @@ stdenv.mkDerivation (rec { ++ lib.concatMap (dep: dep.completeBuildDeps ++ dep.completeDeps) buildDependencies ); - crateFeatures = lib.optionalString (crate ? features) - (lib.concatMapStringsSep " " (f: ''--cfg feature=\"${f}\"'') (crate.features ++ features)); + # 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. + crateFeatures = lib.optionals (crate ? features) + (builtins.filter (f: !lib.hasInfix "/" f) (crate.features ++ features)); libName = if crate ? libName then crate.libName else crate.crateName; libPath = if crate ? libPath then crate.libPath else ""; @@ -244,7 +250,8 @@ stdenv.mkDerivation (rec { metadata = let depsMetadata = lib.foldl' (str: dep: str + dep.metadata) "" (dependencies ++ buildDependencies); hashedMetadata = builtins.hashString "sha256" - (crateName + "-" + crateVersion + "___" + toString crateFeatures + "___" + depsMetadata); + (crateName + "-" + crateVersion + "___" + toString (mkRustcFeatureArgs crateFeatures) + + "___" + depsMetadata); in lib.substring 0 10 hashedMetadata; build = crate.build or ""; 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 bdd6c86d5f07..24ddc11459ec 100644 --- a/pkgs/build-support/rust/build-rust-crate/test/default.nix +++ b/pkgs/build-support/rust/build-rust-crate/test/default.nix @@ -344,6 +344,32 @@ let buildTests = true; expectedTestOutputs = [ "test baz_false ... ok" ]; }; + buildScriptFeatureEnv = { + crateName = "build-script-feature-env"; + features = [ "some-feature" "crate/another_feature" ]; + src = symlinkJoin { + name = "build-script-feature-env"; + paths = [ + (mkFile "src/main.rs" '' + #[cfg(test)] + #[test] + fn feature_not_visible() { + assert!(std::env::var("CARGO_FEATURE_SOME_FEATURE").is_err()); + assert!(option_env!("CARGO_FEATURE_SOME_FEATURE").is_none()); + } + fn main() {} + '') + (mkFile "build.rs" '' + fn main() { + assert!(std::env::var("CARGO_FEATURE_SOME_FEATURE").is_ok()); + assert!(option_env!("CARGO_FEATURE_SOME_FEATURE").is_none()); + } + '') + ]; + }; + buildTests = true; + expectedTestOutputs = [ "test feature_not_visible ... ok" ]; + }; # Regression test for https://github.com/NixOS/nixpkgs/pull/88054 # Build script output should be rewritten as valid env vars. buildScriptIncludeDirDeps = let