about summary refs log tree commit diff
path: root/pkgs/build-support/rust/build-rust-crate
diff options
context:
space:
mode:
authorPeter Kolloch <info@eigenvalue.net>2020-03-01 13:34:36 +0100
committerPeter Kolloch <info@eigenvalue.net>2020-03-09 15:11:50 +0100
commit8a6638daa96318f0cf6d37a1d1d67f1f1c06bd05 (patch)
tree5166bf3c2283a0cefeda1517d1445ad52f002c34 /pkgs/build-support/rust/build-rust-crate
parent04e7462ee61c3f0477b40204f4d90e6b6d5d0a32 (diff)
build-support/rust/buildRustCrate: Search for matching Cargo.toml in sub directories
This is what cargo does for git repositories.

See related issues:

* https://github.com/kolloch/crate2nix/issues/53
* https://github.com/kolloch/crate2nix/issues/33
Diffstat (limited to 'pkgs/build-support/rust/build-rust-crate')
-rw-r--r--pkgs/build-support/rust/build-rust-crate/configure-crate.nix15
-rw-r--r--pkgs/build-support/rust/build-rust-crate/default.nix4
-rw-r--r--pkgs/build-support/rust/build-rust-crate/lib.sh34
-rw-r--r--pkgs/build-support/rust/build-rust-crate/test/default.nix44
4 files changed, 94 insertions, 3 deletions
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 bbe7090aed3d7..013b99a77b4b2 100644
--- a/pkgs/build-support/rust/build-rust-crate/configure-crate.nix
+++ b/pkgs/build-support/rust/build-rust-crate/configure-crate.nix
@@ -31,12 +31,25 @@ let version_ = lib.splitString "-" crateVersion;
     completeDepsDir = lib.concatStringsSep " " completeDeps;
     completeBuildDepsDir = lib.concatStringsSep " " completeBuildDeps;
 in ''
-  cd ${workspace_member}
   ${echo_colored colors}
   ${noisily colors verbose}
   source ${./lib.sh}
 
+  ${lib.optionalString (workspace_member != null) ''
+  noisily cd "${workspace_member}"
+''}
+  ${lib.optionalString (workspace_member == null) ''
+  echo_colored "Searching for matching Cargo.toml (${crateName})" 
+  local cargo_toml_dir=$(matching_cargo_toml_dir "${crateName}")
+  if [ -z "$cargo_toml_dir" ]; then
+    echo_error "ERROR configuring ${crateName}: No matching Cargo.toml in $(pwd) found." >&2
+    exit 23
+  fi
+  noisily cd "$cargo_toml_dir"
+''}
+
   runHook preConfigure
+ 
   symlink_dependency() {
     # $1 is the nix-store path of a dependency
     # $2 is the target path
diff --git a/pkgs/build-support/rust/build-rust-crate/default.nix b/pkgs/build-support/rust/build-rust-crate/default.nix
index 35cb49293e84a..70d48bef8c9b0 100644
--- a/pkgs/build-support/rust/build-rust-crate/default.nix
+++ b/pkgs/build-support/rust/build-rust-crate/default.nix
@@ -88,7 +88,7 @@ stdenv.mkDerivation (rec {
 
     src = crate.src or (fetchCrate { inherit (crate) crateName version sha256; });
     name = "rust_${crate.crateName}-${crate.version}${lib.optionalString buildTests_ "-test"}";
-    depsBuildBuild = [ rust stdenv.cc ];
+    depsBuildBuild = [ rust stdenv.cc cargo jq ];
     buildInputs = (crate.buildInputs or []) ++ buildInputs_;
     dependencies = map lib.getLib dependencies_;
     buildDependencies = map lib.getLib buildDependencies_;
@@ -114,6 +114,8 @@ stdenv.mkDerivation (rec {
       in lib.substring 0 10 hashedMetadata;
 
     build = crate.build or "";
+    # Either set to a concrete sub path to the crate root
+    # or use `null` for auto-detect.
     workspace_member = crate.workspace_member or ".";
     crateVersion = crate.version;
     crateDescription = crate.description or "";
diff --git a/pkgs/build-support/rust/build-rust-crate/lib.sh b/pkgs/build-support/rust/build-rust-crate/lib.sh
index 35e804aa10445..0f08c133e5572 100644
--- a/pkgs/build-support/rust/build-rust-crate/lib.sh
+++ b/pkgs/build-support/rust/build-rust-crate/lib.sh
@@ -144,3 +144,37 @@ search_for_bin_path() {
     exit 1
   fi
 }
+
+# Extracts cargo_toml_path of the matching crate.
+matching_cargo_toml_path() {
+  local manifest_path="$1"
+  local expected_crate_name="$2"
+
+  # If the Cargo.toml is not a workspace root,
+  # it will only contain one package in ".packages"
+  # because "--no-deps" suppressed dependency resolution.
+  #
+  # But to make it more general, we search for a matching
+  # crate in all packages and use the manifest path that
+  # is referenced there.
+  cargo metadata --no-deps --format-version 1 \
+    --manifest-path "$manifest_path" \
+    | jq -r '.packages[] 
+            | select( .name == "'$expected_crate_name'") 
+            | .manifest_path'
+}
+
+# Find a Cargo.toml in the current or any sub directory
+# with a matching crate name.
+matching_cargo_toml_dir() {
+  local expected_crate_name="$1"
+
+  find -L -name Cargo.toml | sort | while read manifest_path; do
+    echo "...checking manifest_path $manifest_path" >&2
+    local matching_path="$(matching_cargo_toml_path "$manifest_path" "$expected_crate_name")"
+    if [ -n "${matching_path}" ]; then
+      echo "$(dirname $matching_path)"
+      break
+    fi
+  done
+}
\ No newline at end of file
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 6aad02992c1b9..710045664686b 100644
--- a/pkgs/build-support/rust/build-rust-crate/test/default.nix
+++ b/pkgs/build-support/rust/build-rust-crate/test/default.nix
@@ -8,6 +8,14 @@ let
     } // args;
   in buildRustCrate p;
 
+  mkCargoToml =
+    { name, crateVersion ? "0.1.0", path ? "Cargo.toml" }:
+      mkFile path ''
+        [package]
+        name = ${builtins.toJSON name}
+        version = ${builtins.toJSON crateVersion}
+      '';
+
   mkFile = destination: text: writeTextFile {
     name = "src";
     destination = "/${destination}";
@@ -89,7 +97,7 @@ let
   in rec {
 
   tests = let
-    cases = {
+    cases = rec {
       libPath =  { libPath = "src/my_lib.rs"; src = mkLib "src/my_lib.rs"; };
       srcLib =  { src = mkLib "src/lib.rs"; };
 
@@ -220,6 +228,40 @@ let
           ];
         };
       };
+      rustCargoTomlInSubDir = {
+        # The "workspace_member" can be set to the sub directory with the crate to build.
+        # By default ".", meaning the top level directory is assumed.
+        # Using null will trigger a search.
+        workspace_member = null;
+        src = symlinkJoin rec {
+          name = "find-cargo-toml";
+          paths = [
+            (mkCargoToml { name = "ignoreMe"; })
+            (mkTestFileWithMain "src/main.rs" "ignore_main")
+
+            (mkCargoToml { name = "rustCargoTomlInSubDir"; path = "subdir/Cargo.toml"; })
+            (mkTestFileWithMain "subdir/src/main.rs" "src_main")
+            (mkTestFile "subdir/tests/foo/main.rs" "tests_foo")
+            (mkTestFile "subdir/tests/bar/main.rs" "tests_bar")
+          ];
+        };
+        buildTests = true;
+        expectedTestOutputs = [
+          "test src_main ... ok"
+          "test tests_foo ... ok"
+          "test tests_bar ... ok"
+        ];
+      };
+
+      rustCargoTomlInTopDir =
+        let
+          withoutCargoTomlSearch = builtins.removeAttrs rustCargoTomlInSubDir [ "workspace_member" ];
+        in
+          withoutCargoTomlSearch // {
+            expectedTestOutputs = [
+              "test ignore_main ... ok"
+            ];
+          };
     };
     brotliCrates = (callPackage ./brotli-crates.nix {});
   in lib.mapAttrs (key: value: mkTest (value // lib.optionalAttrs (!value?crateName) { crateName = key; })) cases // {