diff --git a/src/builders/rust/build-rust-package/default.nix b/src/builders/rust/build-rust-package/default.nix index 26acef16..f811149e 100644 --- a/src/builders/rust/build-rust-package/default.nix +++ b/src/builders/rust/build-rust-package/default.nix @@ -12,6 +12,7 @@ getCyclicDependencies, getDependencies, getSource, + getSourceSpec, produceDerivation, ... @@ -30,40 +31,99 @@ let direct ++ (l.map (dep: getAllTransitiveDependencies dep.name dep.version) direct) )); - # TODO: implement a user option that will make the vendoring - # copy sources instead of symlinking them. This can be useful - # for some Rust packages that modify their own dependencies - # via their build hooks. vendorPackageDependencies = pname: version: let deps = getAllTransitiveDependencies pname version; - makeSource = dep: { - name = "${dep.name}-${dep.version}"; - path = getSource dep.name dep.version; - }; + makeSource = dep: + let + path = getSource dep.name dep.version; + isGit = (getSourceSpec dep.name dep.version).type == "git"; + in { + inherit path isGit dep; + name = "${dep.name}-${dep.version}"; + }; sources = l.map makeSource deps; + + findCrateSource = source: + let + inherit (pkgs) cargo jq; + pkg = source.dep; + in '' + # If the target package is in a workspace, or if it's the top-level + # crate, we should find the crate path using `cargo metadata`. + crateCargoTOML=$(${cargo}/bin/cargo metadata --format-version 1 --no-deps --manifest-path $tree/Cargo.toml | \ + ${jq}/bin/jq -r '.packages[] | select(.name == "${pkg.name}") | .manifest_path') + # If the repository is not a workspace the package might be in a subdirectory. + if [[ -z $crateCargoTOML ]]; then + for manifest in $(find $tree -name "Cargo.toml"); do + echo Looking at $manifest + crateCargoTOML=$(${cargo}/bin/cargo metadata --format-version 1 --no-deps --manifest-path "$manifest" | ${jq}/bin/jq -r '.packages[] | select(.name == "${pkg.name}") | .manifest_path' || :) + if [[ ! -z $crateCargoTOML ]]; then + break + fi + done + if [[ -z $crateCargoTOML ]]; then + >&2 echo "Cannot find path for crate '${pkg.name}-${pkg.version}' in the tree in: $tree" + exit 1 + fi + fi + echo Found crate ${pkg.name} at $crateCargoTOML + tree="$(dirname $crateCargoTOML)" + ''; + makeScript = source: + '' + tree="${source.path}" + ${l.optionalString source.isGit (findCrateSource source)} + cp -prvd "$tree" $out/${source.name} + chmod u+w $out/${source.name} + ${l.optionalString source.isGit "printf '{\"files\":{},\"package\":null}' > \"$out/${source.name}/.cargo-checksum.json\""} + ''; in pkgs.runCommand "vendor-${pname}-${version}" {} '' mkdir -p $out ${ l.concatMapStringsSep "\n" - (source: "ln -s ${source.path} $out/${source.name}") + makeScript sources } ''; + + # Generates a shell script that writes git vendor entries to .cargo/config. + writeGitVendorEntries = + let + makeEntry = source: + '' + [source."${source.url}"] + replace-with = "vendored-sources" + git = "${source.url}" + ${l.optionalString (source ? type) "${source.type} = \"${source.value}\""} + ''; + entries = l.map makeEntry subsystemAttrs.gitSources; + in '' + cat >> ../.cargo/config <