summary refs log tree commit diff
path: root/pkgs/build-support/rust/build-rust-crate
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/build-support/rust/build-rust-crate')
-rw-r--r--pkgs/build-support/rust/build-rust-crate/build-crate.nix35
-rw-r--r--pkgs/build-support/rust/build-rust-crate/default.nix44
-rw-r--r--pkgs/build-support/rust/build-rust-crate/install-crate.nix23
-rw-r--r--pkgs/build-support/rust/build-rust-crate/lib.sh27
-rw-r--r--pkgs/build-support/rust/build-rust-crate/test/default.nix140
5 files changed, 226 insertions, 43 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 9f1930fa62b..dec49d24f52 100644
--- a/pkgs/build-support/rust/build-rust-crate/build-crate.nix
+++ b/pkgs/build-support/rust/build-rust-crate/build-crate.nix
@@ -4,6 +4,7 @@
   crateFeatures, crateRenames, libName, release, libPath,
   crateType, metadata, crateBin, hasCrateBin,
   extraRustcOpts, verbose, colors,
+  buildTests
 }:
 
   let
@@ -30,6 +31,7 @@
       baseRustcOpts
     );
 
+    build_bin = if buildTests then "build_bin_test" else "build_bin";
   in ''
     runHook preBuild
     ${echo_build_heading colors}
@@ -48,14 +50,15 @@
     setup_link_paths
 
     if [[ -e "$LIB_PATH" ]]; then
-       build_lib $LIB_PATH
+       build_lib "$LIB_PATH"
+       ${lib.optionalString buildTests ''build_lib_test "$LIB_PATH"''}
     elif [[ -e src/lib.rs ]]; then
        build_lib src/lib.rs
-    elif [[ -e "src/$LIB_NAME.rs" ]]; then
-       build_lib src/$LIB_NAME.rs
+       ${lib.optionalString buildTests "build_lib_test src/lib.rs"}
     fi
 
 
+
     ${lib.optionalString (lib.length crateBin > 0) (lib.concatMapStringsSep "\n" (bin: ''
       mkdir -p target/bin
       BIN_NAME='${bin.name or crateName}'
@@ -65,19 +68,39 @@
       '' else ''
         BIN_PATH='${bin.path}'
       ''}
-      build_bin "$BIN_NAME" "$BIN_PATH"
+        ${build_bin} "$BIN_NAME" "$BIN_PATH"
     '') crateBin)}
 
+    ${lib.optionalString buildTests ''
+    # When tests are enabled build all the files in the `tests` directory as
+    # test binaries.
+    if [ -d tests ]; then
+      # find all the .rs files (or symlinks to those) in the tests directory, no subdirectories
+      find tests -maxdepth 1 \( -type f -o -type l \) -a -name '*.rs' -print0 | while IFS= read -r -d ''' file; do
+        mkdir -p target/bin
+        build_bin_test_file "$file"
+      done
+
+      # find all the subdirectories of tests/ that contain a main.rs file as
+      # that is also a test according to cargo
+      find tests/ -mindepth 1 -maxdepth 2 \( -type f -o -type l \) -a -name 'main.rs' -print0 | while IFS= read -r -d ''' file; do
+        mkdir -p target/bin
+        build_bin_test_file "$file"
+      done
+
+    fi
+    ''}
+
     # If crateBin is empty and hasCrateBin is not set then we must try to
     # detect some kind of bin target based on some files that might exist.
     ${lib.optionalString (lib.length crateBin == 0 && !hasCrateBin) ''
       if [[ -e src/main.rs ]]; then
         mkdir -p target/bin
-        build_bin ${crateName} src/main.rs
+        ${build_bin} ${crateName} src/main.rs
       fi
       for i in src/bin/*.rs; do #*/
         mkdir -p target/bin
-        build_bin "$(basename $i .rs)" "$i"
+        ${build_bin} "$(basename $i .rs)" "$i"
       done
     ''}
     # Remove object files to avoid "wrong ELF type"
diff --git a/pkgs/build-support/rust/build-rust-crate/default.nix b/pkgs/build-support/rust/build-rust-crate/default.nix
index d5d6bf30b7c..2885b2aef51 100644
--- a/pkgs/build-support/rust/build-rust-crate/default.nix
+++ b/pkgs/build-support/rust/build-rust-crate/default.nix
@@ -39,12 +39,12 @@ let
      inherit lib stdenv echo_build_heading noisily mkRustcDepArgs rust;
    };
 
-   installCrate = import ./install-crate.nix;
+   installCrate = import ./install-crate.nix { inherit stdenv; };
 in
 
 crate_: lib.makeOverridable ({ rust, release, verbose, features, buildInputs, crateOverrides,
   dependencies, buildDependencies, crateRenames,
-  extraRustcOpts,
+  extraRustcOpts, buildTests,
   preUnpack, postUnpack, prePatch, patches, postPatch,
   preConfigure, postConfigure, preBuild, postBuild, preInstall, postInstall }:
 
@@ -55,10 +55,12 @@ let crate = crate_ // (lib.attrByPath [ crate_.crateName ] (attr: {}) crateOverr
       "src" "buildInputs" "crateBin" "crateLib" "libName" "libPath"
       "buildDependencies" "dependencies" "features" "crateRenames"
       "crateName" "version" "build" "authors" "colors" "edition"
+      "buildTests"
     ];
-    extraDerivationAttrs = lib.filterAttrs (n: v: ! lib.elem n processedAttrs) crate;
+    extraDerivationAttrs = builtins.removeAttrs crate processedAttrs;
     buildInputs_ = buildInputs;
     extraRustcOpts_ = extraRustcOpts;
+    buildTests_ = buildTests;
 
     # take a list of crates that we depend on and override them to fit our overrides, rustc, release, …
     makeDependencies = map (dep: lib.getLib (dep.override { inherit release verbose crateOverrides; }));
@@ -72,13 +74,23 @@ in
 stdenv.mkDerivation (rec {
 
     inherit (crate) crateName;
-    inherit preUnpack postUnpack prePatch patches postPatch preConfigure postConfigure preBuild postBuild preInstall postInstall;
-
-    src = if lib.hasAttr "src" crate then
-        crate.src
-      else
-        fetchCrate { inherit (crate) crateName version sha256; };
-    name = "rust_${crate.crateName}-${crate.version}";
+    inherit
+      preUnpack
+      postUnpack
+      prePatch
+      patches
+      postPatch
+      preConfigure
+      postConfigure
+      preBuild
+      postBuild
+      preInstall
+      postInstall
+      buildTests
+    ;
+
+    src = crate.src or (fetchCrate { inherit (crate) crateName version sha256; });
+    name = "rust_${crate.crateName}-${crate.version}${lib.optionalString buildTests_ "-test"}";
     depsBuildBuild = [ rust stdenv.cc ];
     buildInputs = (crate.buildInputs or []) ++ buildInputs_;
     dependencies = makeDependencies dependencies_;
@@ -122,6 +134,7 @@ stdenv.mkDerivation (rec {
       ++ extraRustcOpts_
       ++ (lib.optional (edition != null) "--edition ${edition}");
 
+
     configurePhase = configureCrate {
       inherit crateName buildDependencies completeDeps completeBuildDeps crateDescription
               crateFeatures crateRenames libName build workspace_member release libPath crateVersion
@@ -132,12 +145,14 @@ stdenv.mkDerivation (rec {
       inherit crateName dependencies
               crateFeatures crateRenames libName release libPath crateType
               metadata hasCrateBin crateBin verbose colors
-              extraRustcOpts;
+              extraRustcOpts buildTests;
     };
-    installPhase = installCrate crateName metadata;
+    installPhase = installCrate crateName metadata buildTests;
 
-    outputs = [ "out" "lib" ];
-    outputDev = [ "lib" ];
+    # depending on the test setting we are either producing something with bins
+    # and libs or just test binaries
+    outputs = if buildTests then [ "out" ] else [ "out" "lib" ];
+    outputDev = if buildTests then [ "out" ] else  [ "lib" ];
 
 } // extraDerivationAttrs
 )) {
@@ -162,4 +177,5 @@ stdenv.mkDerivation (rec {
   dependencies = crate_.dependencies or [];
   buildDependencies = crate_.buildDependencies or [];
   crateRenames = crate_.crateRenames or {};
+  buildTests = crate_.buildTests or false;
 }
diff --git a/pkgs/build-support/rust/build-rust-crate/install-crate.nix b/pkgs/build-support/rust/build-rust-crate/install-crate.nix
index 934c3a03176..5ba7b69bedc 100644
--- a/pkgs/build-support/rust/build-rust-crate/install-crate.nix
+++ b/pkgs/build-support/rust/build-rust-crate/install-crate.nix
@@ -1,5 +1,6 @@
-crateName: metadata:
-''
+{ stdenv }:
+crateName: metadata: buildTests:
+if !buildTests then ''
   runHook preInstall
   # always create $out even if we do not have binaries. We are detecting binary targets during compilation, if those are missing there is no way to only have $lib
   mkdir $out
@@ -29,4 +30,22 @@ crateName: metadata:
     fi
   fi
   runHook postInstall
+'' else
+# for tests we just put them all in the output. No execution.
+''
+  runHook preInstall
+
+  mkdir -p $out/tests
+  if [ -e target/bin ]; then
+    find target/bin/ -type f -executable -exec cp {} $out/tests \;
+  fi
+  if [ -e target/lib ]; then
+    find target/lib/ -type f \! -name '*.rlib' \
+      -a \! -name '*${stdenv.hostPlatform.extensions.sharedLibrary}' \
+      -a \! -name '*.d' \
+      -executable \
+      -print0 | xargs --no-run-if-empty --null install --target $out/tests;
+  fi
+
+  runHook postInstall
 ''
diff --git a/pkgs/build-support/rust/build-rust-crate/lib.sh b/pkgs/build-support/rust/build-rust-crate/lib.sh
index 24c712468ea..d4d9317496f 100644
--- a/pkgs/build-support/rust/build-rust-crate/lib.sh
+++ b/pkgs/build-support/rust/build-rust-crate/lib.sh
@@ -13,6 +13,7 @@ build_lib() {
     $BUILD_OUT_DIR \
     $EXTRA_BUILD \
     $EXTRA_FEATURES \
+    $EXTRA_RUSTC_FLAGS \
     --color $colors
 
   EXTRA_LIB=" --extern $CRATE_NAME=target/lib/lib$CRATE_NAME-$metadata.rlib"
@@ -22,9 +23,10 @@ build_lib() {
 }
 
 build_bin() {
-  crate_name=$1
-  crate_name_=$(echo $crate_name | tr '-' '_')
-  main_file=""
+  local crate_name=$1
+  local crate_name_=$(echo $crate_name | tr '-' '_')
+  local main_file=""
+
   if [[ ! -z $2 ]]; then
     main_file=$2
   fi
@@ -43,6 +45,7 @@ build_bin() {
     $BUILD_OUT_DIR \
     $EXTRA_BUILD \
     $EXTRA_FEATURES \
+    $EXTRA_RUSTC_FLAGS \
     --color ${colors} \
 
   if [ "$crate_name_" != "$crate_name" ]; then
@@ -50,6 +53,24 @@ build_bin() {
   fi
 }
 
+build_lib_test() {
+    local file="$1"
+    EXTRA_RUSTC_FLAGS="--test $EXTRA_RUSTC_FLAGS" build_lib "$1" "$2"
+}
+
+build_bin_test() {
+    local crate="$1"
+    local file="$2"
+    EXTRA_RUSTC_FLAGS="--test $EXTRA_RUSTC_FLAGS" build_bin "$1" "$2"
+}
+
+build_bin_test_file() {
+    local file="$1"
+    local derived_crate_name="${file//\//_}"
+    derived_crate_name="${derived_crate_name%.rs}"
+    build_bin_test "$derived_crate_name" "$file"
+}
+
 setup_link_paths() {
   EXTRA_LIB=""
   if [[ -e target/link_ ]]; then
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 4a90cf442a4..aefa279fc5e 100644
--- a/pkgs/build-support/rust/build-rust-crate/test/default.nix
+++ b/pkgs/build-support/rust/build-rust-crate/test/default.nix
@@ -1,4 +1,4 @@
-{ lib, buildRustCrate, runCommand, writeTextFile, symlinkJoin, callPackage }:
+{ lib, buildRustCrate, runCommand, writeTextFile, symlinkJoin, callPackage, releaseTools }:
 let
   mkCrate = args: let
       p = {
@@ -29,10 +29,30 @@ let
     }
   '';
 
+  mkTestFile = name: functionName: mkFile name ''
+    #[cfg(test)]
+    #[test]
+    fn ${functionName}() {
+      assert!(true);
+    }
+  '';
+  mkTestFileWithMain = name: functionName: mkFile name ''
+    #[cfg(test)]
+    #[test]
+    fn ${functionName}() {
+      assert!(true);
+    }
+
+    fn main() {}
+  '';
+
+
   mkLib = name: mkFile name "pub fn test() -> i32 { return 23; }";
 
   mkTest = crateArgs: let
-    crate = mkCrate crateArgs;
+    crate = mkCrate (builtins.removeAttrs crateArgs ["expectedTestOutput"]);
+    hasTests = crateArgs.buildTests or false;
+    expectedTestOutputs = crateArgs.expectedTestOutputs or null;
     binaries = map (v: ''"${v.name}"'') (crateArgs.crateBin or []);
     isLib = crateArgs ? libName || crateArgs ? libPath;
     crateName = crateArgs.crateName or "nixtestcrate";
@@ -44,23 +64,45 @@ let
       src = mkBinExtern "src/main.rs" libName;
     };
 
-    in runCommand "run-buildRustCrate-${crateName}-test" {
-      nativeBuildInputs = [ crate ];
-    } ''
-      ${lib.concatStringsSep "\n" binaries}
-      ${lib.optionalString isLib ''
-          test -e ${crate}/lib/*.rlib || exit 1
-          ${libTestBinary}/bin/run-test-${crateName}
-      ''}
-      touch $out
-  '';
+    in
+      assert expectedTestOutputs != null -> hasTests;
+      assert hasTests -> expectedTestOutputs != null;
+
+      runCommand "run-buildRustCrate-${crateName}-test" {
+        nativeBuildInputs = [ crate ];
+      } (if !hasTests then ''
+          ${lib.concatStringsSep "\n" binaries}
+          ${lib.optionalString isLib ''
+              test -e ${crate}/lib/*.rlib || exit 1
+              ${libTestBinary}/bin/run-test-${crateName}
+          ''}
+          touch $out
+        '' else ''
+          for file in ${crate}/tests/*; do
+            $file 2>&1 >> $out
+          done
+          set -e
+          ${lib.concatMapStringsSep "\n" (o: "grep '${o}' $out || {  echo 'output \"${o}\" not found in:'; cat $out; exit 23; }") expectedTestOutputs}
+        ''
+      );
+
   in rec {
 
   tests = let
     cases = {
       libPath =  { libPath = "src/my_lib.rs"; src = mkLib "src/my_lib.rs"; };
       srcLib =  { src = mkLib "src/lib.rs"; };
-      customLibName =  { libName = "test_lib"; src = mkLib "src/test_lib.rs"; };
+
+      # This used to be supported by cargo but as of 1.40.0 I can't make it work like that with just cargo anymore.
+      # This might be a regression or deprecated thing they finally removed…
+      # customLibName =  { libName = "test_lib"; src = mkLib "src/test_lib.rs"; };
+      # rustLibTestsCustomLibName = {
+      #   libName = "test_lib";
+      #   src = mkTestFile "src/test_lib.rs" "foo";
+      #   buildTests = true;
+      #   expectedTestOutputs = [ "test foo ... ok" ];
+      # };
+
       customLibNameAndLibPath =  { libName = "test_lib"; libPath = "src/best-lib.rs"; src = mkLib "src/best-lib.rs"; };
       crateBinWithPath =  { crateBin = [{ name = "test_binary1"; path = "src/foobar.rs"; }]; src = mkBin "src/foobar.rs"; };
       crateBinNoPath1 =  { crateBin = [{ name = "my-binary2"; }]; src = mkBin "src/my_binary2.rs"; };
@@ -85,6 +127,65 @@ let
         dependencies = [ (mkCrate { crateName = "foo"; libName = "foolib"; src = mkLib "src/lib.rs"; }) ];
         crateRenames = { "foo" = "foo_renamed"; };
       };
+      rustLibTestsDefault = {
+        src = mkTestFile "src/lib.rs" "baz";
+        buildTests = true;
+        expectedTestOutputs = [ "test baz ... ok" ];
+      };
+      rustLibTestsCustomLibPath = {
+        libPath = "src/test_path.rs";
+        src = mkTestFile "src/test_path.rs" "bar";
+        buildTests = true;
+        expectedTestOutputs = [ "test bar ... ok" ];
+      };
+      rustLibTestsCustomLibPathWithTests = {
+        libPath = "src/test_path.rs";
+        src = symlinkJoin {
+          name = "rust-lib-tests-custom-lib-path-with-tests-dir";
+          paths = [
+            (mkTestFile "src/test_path.rs" "bar")
+            (mkTestFile "tests/something.rs" "something")
+          ];
+        };
+        buildTests = true;
+        expectedTestOutputs = [
+          "test bar ... ok"
+          "test something ... ok"
+        ];
+      };
+      rustBinTestsCombined = {
+        src = symlinkJoin {
+          name = "rust-bin-tests-combined";
+          paths = [
+            (mkTestFileWithMain "src/main.rs" "src_main")
+            (mkTestFile "tests/foo.rs" "tests_foo")
+            (mkTestFile "tests/bar.rs" "tests_bar")
+          ];
+        };
+        buildTests = true;
+        expectedTestOutputs = [
+          "test src_main ... ok"
+          "test tests_foo ... ok"
+          "test tests_bar ... ok"
+        ];
+      };
+      rustBinTestsSubdirCombined = {
+        src = symlinkJoin {
+          name = "rust-bin-tests-subdir-combined";
+          paths = [
+            (mkTestFileWithMain "src/main.rs" "src_main")
+            (mkTestFile "tests/foo/main.rs" "tests_foo")
+            (mkTestFile "tests/bar/main.rs" "tests_bar")
+          ];
+        };
+        buildTests = true;
+        expectedTestOutputs = [
+          "test src_main ... ok"
+          "test tests_foo ... ok"
+          "test tests_bar ... ok"
+        ];
+      };
+
     };
     brotliCrates = (callPackage ./brotli-crates.nix {});
   in lib.mapAttrs (key: value: mkTest (value // lib.optionalAttrs (!value?crateName) { crateName = key; })) cases // {
@@ -110,9 +211,12 @@ let
       test -e ${pkg}/bin/brotli-decompressor && touch $out
     '';
   };
-  test = runCommand "run-buildRustCrate-tests" {
-    nativeBuildInputs = builtins.attrValues tests;
-  } "
-    touch $out
-  ";
+  test = releaseTools.aggregate {
+    name = "buildRustCrate-tests";
+    meta = {
+      description = "Test cases for buildRustCrate";
+      maintainers = [ lib.maintainers.andir ];
+    };
+    constituents = builtins.attrValues tests;
+  };
 }