Skip to content

Commit 5686f00

Browse files
winterqtyuyuyureka
authored andcommitted
rustPlatform.importCargoLock: add support for git dependencies that use workspace inheritance
Rust 1.64.0 added support for workspace inheritance, which allows for crates to inherit values such as dependency version constraints or package metadata information from their workspaces [0]. This works by having workspace members specify a value as a table, with `workspace` set to true. Thus, supporting this in importCargoLock is as simple as walking the crate's Cargo.toml, replacing inherited values with their workspace counterpart. This is also what a forthcoming Cargo release will do for `cargo vendor` [1], but we can get ahead of it ;) [0]: https://blog.rust-lang.org/2022/09/22/Rust-1.64.0.html#cargo-improvements-workspace-inheritance-and-multi-target-builds [1]: rust-lang/cargo#11414
1 parent 7c7d4f6 commit 5686f00

File tree

7 files changed

+142
-2
lines changed

7 files changed

+142
-2
lines changed

pkgs/build-support/rust/import-cargo-lock.nix

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{ fetchgit, fetchurl, lib, runCommand, cargo, jq }:
1+
{ fetchgit, fetchurl, lib, writers, python3Packages, runCommand, cargo, jq }:
22

33
{
44
# Cargo lock file
@@ -93,6 +93,11 @@ let
9393
sha256 = checksum;
9494
};
9595

96+
# Replaces values inherited by workspace members.
97+
replaceWorkspaceValues = writers.writePython3 "replace-workspace-values"
98+
{ libraries = with python3Packages; [ tomli tomli-w ]; flakeIgnore = [ "E501" ]; }
99+
(builtins.readFile ./replace-workspace-values.py);
100+
96101
# Fetch and unpack a crate.
97102
mkCrate = pkg:
98103
let
@@ -171,6 +176,11 @@ let
171176
cp -prvd "$tree/" $out
172177
chmod u+w $out
173178
179+
if grep -q workspace "$out/Cargo.toml"; then
180+
chmod u+w "$out/Cargo.toml"
181+
${replaceWorkspaceValues} "$out/Cargo.toml" "${tree}/Cargo.toml"
182+
fi
183+
174184
# Cargo is happy with empty metadata.
175185
printf '{"files":{},"package":null}' > "$out/.cargo-checksum.json"
176186
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# This script implements the workspace inheritance mechanism described
2+
# here: https://doc.rust-lang.org/cargo/reference/workspaces.html#the-package-table
3+
#
4+
# Please run `mypy --strict`, `black`, and `isort --profile black` on this after editing, thanks!
5+
6+
import sys
7+
from typing import Any
8+
9+
import tomli
10+
import tomli_w
11+
12+
13+
def load_file(path: str) -> dict[str, Any]:
14+
with open(path, "rb") as f:
15+
return tomli.load(f)
16+
17+
18+
def replace_key(
19+
workspace_manifest: dict[str, Any], table: dict[str, Any], section: str, key: str
20+
) -> bool:
21+
if "workspace" in table[key] and table[key]["workspace"] is True:
22+
print("replacing " + key)
23+
24+
replaced = table[key]
25+
del replaced["workspace"]
26+
27+
workspace_copy = workspace_manifest[section][key]
28+
29+
if section == "dependencies":
30+
crate_features = replaced.get("features")
31+
32+
if type(workspace_copy) is str:
33+
replaced["version"] = workspace_copy
34+
else:
35+
replaced.update(workspace_copy)
36+
37+
merged_features = (crate_features or []) + (
38+
workspace_copy.get("features") or []
39+
)
40+
41+
if len(merged_features) > 0:
42+
# Dictionaries are guaranteed to be ordered (https://stackoverflow.com/a/7961425)
43+
replaced["features"] = list(dict.fromkeys(merged_features))
44+
elif section == "package":
45+
table[key] = replaced = workspace_copy
46+
47+
return True
48+
49+
return False
50+
51+
52+
def replace_dependencies(
53+
workspace_manifest: dict[str, Any], root: dict[str, Any]
54+
) -> bool:
55+
changed = False
56+
57+
for key in ["dependencies", "dev-dependencies", "build-dependencies"]:
58+
if key in root:
59+
for k in root[key].keys():
60+
changed |= replace_key(workspace_manifest, root[key], "dependencies", k)
61+
62+
return changed
63+
64+
65+
def main() -> None:
66+
crate_manifest = load_file(sys.argv[1])
67+
workspace_manifest = load_file(sys.argv[2])["workspace"]
68+
69+
if "workspace" in crate_manifest:
70+
return
71+
72+
changed = False
73+
74+
for key in crate_manifest["package"].keys():
75+
changed |= replace_key(
76+
workspace_manifest, crate_manifest["package"], "package", key
77+
)
78+
79+
changed |= replace_dependencies(workspace_manifest, crate_manifest)
80+
81+
if "target" in crate_manifest:
82+
for key in crate_manifest["target"].keys():
83+
changed |= replace_dependencies(
84+
workspace_manifest, crate_manifest["target"][key]
85+
)
86+
87+
if not changed:
88+
return
89+
90+
with open(sys.argv[1], "wb") as f:
91+
tomli_w.dump(crate_manifest, f)
92+
93+
94+
if __name__ == "__main__":
95+
main()

pkgs/build-support/rust/test/import-cargo-lock/default.nix

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{ callPackage }:
1+
{ callPackage, writers, python3Packages }:
22

33
# Build like this from nixpkgs root:
44
# $ nix-build -A tests.importCargoLock
@@ -12,4 +12,9 @@
1212
gitDependencyBranch = callPackage ./git-dependency-branch { };
1313
maturin = callPackage ./maturin { };
1414
v1 = callPackage ./v1 { };
15+
gitDependencyWorkspaceInheritance = callPackage ./git-dependency-workspace-inheritance {
16+
replaceWorkspaceValues = writers.writePython3 "replace-workspace-values"
17+
{ libraries = with python3Packages; [ tomli tomli-w ]; flakeIgnore = [ "E501" ]; }
18+
(builtins.readFile ../../replace-workspace-values.py);
19+
};
1520
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
version = { workspace = true }
3+
4+
[dependencies]
5+
foo = { workspace = true, features = ["cat"] }
6+
bar = "1.0.0"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{ replaceWorkspaceValues, runCommand }:
2+
3+
runCommand "git-dependency-workspace-inheritance-test" { } ''
4+
cp --no-preserve=mode ${./crate.toml} "$out"
5+
${replaceWorkspaceValues} "$out" ${./workspace.toml}
6+
diff -u "$out" ${./want.toml}
7+
''
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
version = "1.0.0"
3+
4+
[dependencies]
5+
bar = "1.0.0"
6+
7+
[dependencies.foo]
8+
features = [
9+
"cat",
10+
"meow",
11+
]
12+
version = "1.0.0"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[workspace.package]
2+
version = "1.0.0"
3+
4+
[workspace.dependencies]
5+
foo = { version = "1.0.0", features = ["meow"] }

0 commit comments

Comments
 (0)