about summary refs log tree commit diff
path: root/pkgs/build-support/rust
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/build-support/rust')
-rw-r--r--pkgs/build-support/rust/build-rust-crate/build-crate.nix4
-rw-r--r--pkgs/build-support/rust/build-rust-crate/configure-crate.nix22
-rw-r--r--pkgs/build-support/rust/build-rust-crate/default.nix17
-rw-r--r--pkgs/build-support/rust/build-rust-crate/test/default.nix26
-rw-r--r--pkgs/build-support/rust/default.nix50
5 files changed, 92 insertions, 27 deletions
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
diff --git a/pkgs/build-support/rust/default.nix b/pkgs/build-support/rust/default.nix
index 770a6d1042e7..8d3a7ba6929c 100644
--- a/pkgs/build-support/rust/default.nix
+++ b/pkgs/build-support/rust/default.nix
@@ -28,6 +28,13 @@
 , meta ? {}
 , target ? null
 , cargoVendorDir ? null
+, checkType ? buildType
+
+# Needed to `pushd`/`popd` into a subdir of a tarball if this subdir
+# contains a Cargo.toml, but isn't part of a workspace (which is e.g. the
+# case for `rustfmt`/etc from the `rust-sources).
+# Otherwise, everything from the tarball would've been built/tested.
+, buildAndTestSubdir ? null
 , ... } @ args:
 
 assert cargoVendorDir == null -> cargoSha256 != "unset";
@@ -163,6 +170,7 @@ stdenv.mkDerivation (args // {
   '';
 
   buildPhase = with builtins; args.buildPhase or ''
+    ${stdenv.lib.optionalString (buildAndTestSubdir != null) "pushd ${buildAndTestSubdir}"}
     runHook preBuild
 
     (
@@ -178,22 +186,29 @@ stdenv.mkDerivation (args // {
         --frozen ${concatStringsSep " " cargoBuildFlags}
     )
 
-    # rename the output dir to a architecture independent one
-    mapfile -t targets < <(find "$NIX_BUILD_TOP" -type d | grep '${releaseDir}$')
-    for target in "''${targets[@]}"; do
-      rm -rf "$target/../../${buildType}"
-      ln -srf "$target" "$target/../../"
-    done
-
     runHook postBuild
+
+    ${stdenv.lib.optionalString (buildAndTestSubdir != null) "popd"}
+
+    # This needs to be done after postBuild: packages like `cargo` do a pushd/popd in
+    # the pre/postBuild-hooks that need to be taken into account before gathering
+    # all binaries to install.
+    bins=$(find $releaseDir \
+      -maxdepth 1 \
+      -type f \
+      -executable ! \( -regex ".*\.\(so.[0-9.]+\|so\|a\|dylib\)" \))
   '';
 
-  checkPhase = args.checkPhase or ''
+  checkPhase = args.checkPhase or (let
+    argstr = "${stdenv.lib.optionalString (checkType == "release") "--release"} --target ${rustTarget} --frozen";
+  in ''
+    ${stdenv.lib.optionalString (buildAndTestSubdir != null) "pushd ${buildAndTestSubdir}"}
     runHook preCheck
-    echo "Running cargo cargo test -- ''${checkFlags} ''${checkFlagsArray+''${checkFlagsArray[@]}}"
-    cargo test -- ''${checkFlags} ''${checkFlagsArray+"''${checkFlagsArray[@]}"}
+    echo "Running cargo test ${argstr} -- ''${checkFlags} ''${checkFlagsArray+''${checkFlagsArray[@]}}"
+    cargo test ${argstr} -- ''${checkFlags} ''${checkFlagsArray+"''${checkFlagsArray[@]}"}
     runHook postCheck
-  '';
+    ${stdenv.lib.optionalString (buildAndTestSubdir != null) "popd"}
+  '');
 
   doCheck = args.doCheck or true;
 
@@ -203,13 +218,16 @@ stdenv.mkDerivation (args // {
 
   installPhase = args.installPhase or ''
     runHook preInstall
+
+    # rename the output dir to a architecture independent one
+    mapfile -t targets < <(find "$NIX_BUILD_TOP" -type d | grep '${releaseDir}$')
+    for target in "''${targets[@]}"; do
+      rm -rf "$target/../../${buildType}"
+      ln -srf "$target" "$target/../../"
+    done
     mkdir -p $out/bin $out/lib
 
-    find $releaseDir \
-      -maxdepth 1 \
-      -type f \
-      -executable ! \( -regex ".*\.\(so.[0-9.]+\|so\|a\|dylib\)" \) \
-      -print0 | xargs -r -0 cp -t $out/bin
+    xargs -r cp -t $out/bin <<< $bins
     find $releaseDir \
       -maxdepth 1 \
       -regex ".*\.\(so.[0-9.]+\|so\|a\|dylib\)" \