fix(rust): don't vendor path deps, handle git dep configs more correctly, handle cyclic deps correctly

This commit is contained in:
Yusuf Bera Ertan 2022-02-20 12:16:09 +03:00
parent 68faa2378c
commit 129134fcd3
No known key found for this signature in database
GPG Key ID: 1D8F8FAF2294D6EA
2 changed files with 131 additions and 91 deletions

View File

@ -22,100 +22,15 @@
let
l = lib // builtins;
getAllDependencies = pname: version:
(args.getDependencies pname version)
++ (args.getCyclicDependencies pname version);
getAllTransitiveDependencies = pname: version:
let direct = getAllDependencies pname version; in
l.unique (l.flatten (
direct ++ (l.map (dep: getAllTransitiveDependencies dep.name dep.version) direct)
));
vendorPackageDependencies = pname: version:
let
deps = getAllTransitiveDependencies pname version;
makeSource = dep:
let
path = getSource dep.name dep.version;
spec = getSourceSpec dep.name dep.version;
in {
inherit path spec 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:
let
isGit = source.spec.type == "git";
isPath = source.spec.type == "path";
in
''
tree="${source.path}"
${l.optionalString isGit (findCrateSource source)}
cp -prvd "$tree" $out/${source.name}
chmod u+w $out/${source.name}
${l.optionalString (isGit || isPath) "printf '{\"files\":{},\"package\":null}' > \"$out/${source.name}/.cargo-checksum.json\""}
'';
in
pkgs.runCommand "vendor-${pname}-${version}" {} ''
mkdir -p $out
${
l.concatMapStringsSep "\n"
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 <<EOF
${l.concatStringsSep "\n" entries}
EOF
'';
vendoring = import ../vendor.nix {
inherit lib pkgs getSource getSourceSpec
getDependencies getCyclicDependencies subsystemAttrs;
};
buildPackage = pname: version:
let
src = getSource pname version;
vendorDir = vendorPackageDependencies pname version;
vendorDir = vendoring.vendorPackageDependencies pname version;
in
produceDerivation pname (pkgs.rustPlatform.buildRustPackage {
inherit pname version src;
@ -127,7 +42,7 @@ let
cargoVendorDir = "../nix-vendor";
preBuild = ''
${writeGitVendorEntries}
${vendoring.writeGitVendorEntries "vendored-sources"}
'';
});
in

View File

@ -0,0 +1,125 @@
{
lib,
pkgs,
getSource,
getSourceSpec,
getDependencies,
getCyclicDependencies,
subsystemAttrs,
}:
let
l = lib // builtins;
isCyclic = cyclic: dep:
l.any (odep: dep.name == odep.name && dep.version == odep.version) cyclic;
getUncyclicDependencies = cyclic: deps:
l.map
(dep: getAllTransitiveDependencies dep.name dep.version)
(l.filter (dep: !(isCyclic cyclic dep)) deps);
getAllTransitiveUncyclicDependencies = pname: version: let
cyclic = getCyclicDependencies pname version;
direct = getDependencies pname version;
in
l.unique (l.flatten (direct ++ (getUncyclicDependencies cyclic direct)));
getAllTransitiveDependencies = pname: version: let
direct = getDependencies pname version;
cyclic = getCyclicDependencies pname version;
in
l.unique (l.flatten (
direct
++ (getUncyclicDependencies cyclic direct)
++ (
l.map
(dep: getAllTransitiveUncyclicDependencies dep.name dep.version)
(l.filter (isCyclic cyclic) direct)
)
));
in {
# Generates a shell script that writes git vendor entries to .cargo/config.
# `replaceWith` is the name of the vendored source(s) to use.
writeGitVendorEntries = replaceWith:
let
makeEntry = source:
''
[source."${source.url}${l.optionalString (source ? type) "?${source.type}=${source.value}"}"]
replace-with = "${replaceWith}"
git = "${source.url}"
${l.optionalString (source ? type) "${source.type} = \"${source.value}\""}
'';
entries = l.map makeEntry subsystemAttrs.gitSources;
in ''
mkdir -p ../.cargo/ && touch ../.cargo/config
cat >> ../.cargo/config <<EOF
${l.concatStringsSep "\n" entries}
EOF
'';
# Vendor a package's dependencies like how `cargo vendor` would do,
# so we can use it with `cargo`.
vendorPackageDependencies = pname: version:
let
deps = getAllTransitiveDependencies pname version;
makeSource = dep:
let
path = getSource dep.name dep.version;
spec = getSourceSpec dep.name dep.version;
in {
inherit path spec 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:
let
isGit = source.spec.type == "git";
isPath = source.spec.type == "path";
in
l.optionalString (!isPath) ''
tree="${source.path}"
${l.optionalString isGit (findCrateSource source)}
cp -prvd "$tree" $out/${source.name}
chmod u+w $out/${source.name}
${l.optionalString isGit "printf '{\"files\":{},\"package\":null}' > \"$out/${source.name}/.cargo-checksum.json\""}
'';
in
pkgs.runCommand "vendor-${pname}-${version}" {} ''
mkdir -p $out
${
l.concatMapStringsSep "\n"
makeScript
sources
}
'';
}