From 5686f0064d5f36e7f39984ea9232b42c1104bf1e Mon Sep 17 00:00:00 2001 From: Winter Date: Sun, 19 Feb 2023 13:01:18 -0500 Subject: [PATCH] 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]: https://github.com/rust-lang/cargo/pull/11414 --- pkgs/build-support/rust/import-cargo-lock.nix | 12 ++- .../rust/replace-workspace-values.py | 95 +++++++++++++++++++ .../rust/test/import-cargo-lock/default.nix | 7 +- .../crate.toml | 6 ++ .../default.nix | 7 ++ .../want.toml | 12 +++ .../workspace.toml | 5 + 7 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 pkgs/build-support/rust/replace-workspace-values.py create mode 100644 pkgs/build-support/rust/test/import-cargo-lock/git-dependency-workspace-inheritance/crate.toml create mode 100644 pkgs/build-support/rust/test/import-cargo-lock/git-dependency-workspace-inheritance/default.nix create mode 100644 pkgs/build-support/rust/test/import-cargo-lock/git-dependency-workspace-inheritance/want.toml create mode 100644 pkgs/build-support/rust/test/import-cargo-lock/git-dependency-workspace-inheritance/workspace.toml diff --git a/pkgs/build-support/rust/import-cargo-lock.nix b/pkgs/build-support/rust/import-cargo-lock.nix index 9ff9c2f6289c..1c739933af16 100644 --- a/pkgs/build-support/rust/import-cargo-lock.nix +++ b/pkgs/build-support/rust/import-cargo-lock.nix @@ -1,4 +1,4 @@ -{ fetchgit, fetchurl, lib, runCommand, cargo, jq }: +{ fetchgit, fetchurl, lib, writers, python3Packages, runCommand, cargo, jq }: { # Cargo lock file @@ -93,6 +93,11 @@ let sha256 = checksum; }; + # Replaces values inherited by workspace members. + replaceWorkspaceValues = writers.writePython3 "replace-workspace-values" + { libraries = with python3Packages; [ tomli tomli-w ]; flakeIgnore = [ "E501" ]; } + (builtins.readFile ./replace-workspace-values.py); + # Fetch and unpack a crate. mkCrate = pkg: let @@ -171,6 +176,11 @@ let cp -prvd "$tree/" $out chmod u+w $out + if grep -q workspace "$out/Cargo.toml"; then + chmod u+w "$out/Cargo.toml" + ${replaceWorkspaceValues} "$out/Cargo.toml" "${tree}/Cargo.toml" + fi + # Cargo is happy with empty metadata. printf '{"files":{},"package":null}' > "$out/.cargo-checksum.json" diff --git a/pkgs/build-support/rust/replace-workspace-values.py b/pkgs/build-support/rust/replace-workspace-values.py new file mode 100644 index 000000000000..fe5462883302 --- /dev/null +++ b/pkgs/build-support/rust/replace-workspace-values.py @@ -0,0 +1,95 @@ +# This script implements the workspace inheritance mechanism described +# here: https://doc.rust-lang.org/cargo/reference/workspaces.html#the-package-table +# +# Please run `mypy --strict`, `black`, and `isort --profile black` on this after editing, thanks! + +import sys +from typing import Any + +import tomli +import tomli_w + + +def load_file(path: str) -> dict[str, Any]: + with open(path, "rb") as f: + return tomli.load(f) + + +def replace_key( + workspace_manifest: dict[str, Any], table: dict[str, Any], section: str, key: str +) -> bool: + if "workspace" in table[key] and table[key]["workspace"] is True: + print("replacing " + key) + + replaced = table[key] + del replaced["workspace"] + + workspace_copy = workspace_manifest[section][key] + + if section == "dependencies": + crate_features = replaced.get("features") + + if type(workspace_copy) is str: + replaced["version"] = workspace_copy + else: + replaced.update(workspace_copy) + + merged_features = (crate_features or []) + ( + workspace_copy.get("features") or [] + ) + + if len(merged_features) > 0: + # Dictionaries are guaranteed to be ordered (https://stackoverflow.com/a/7961425) + replaced["features"] = list(dict.fromkeys(merged_features)) + elif section == "package": + table[key] = replaced = workspace_copy + + return True + + return False + + +def replace_dependencies( + workspace_manifest: dict[str, Any], root: dict[str, Any] +) -> bool: + changed = False + + for key in ["dependencies", "dev-dependencies", "build-dependencies"]: + if key in root: + for k in root[key].keys(): + changed |= replace_key(workspace_manifest, root[key], "dependencies", k) + + return changed + + +def main() -> None: + crate_manifest = load_file(sys.argv[1]) + workspace_manifest = load_file(sys.argv[2])["workspace"] + + if "workspace" in crate_manifest: + return + + changed = False + + for key in crate_manifest["package"].keys(): + changed |= replace_key( + workspace_manifest, crate_manifest["package"], "package", key + ) + + changed |= replace_dependencies(workspace_manifest, crate_manifest) + + if "target" in crate_manifest: + for key in crate_manifest["target"].keys(): + changed |= replace_dependencies( + workspace_manifest, crate_manifest["target"][key] + ) + + if not changed: + return + + with open(sys.argv[1], "wb") as f: + tomli_w.dump(crate_manifest, f) + + +if __name__ == "__main__": + main() diff --git a/pkgs/build-support/rust/test/import-cargo-lock/default.nix b/pkgs/build-support/rust/test/import-cargo-lock/default.nix index 74be68b2c356..f399107eb982 100644 --- a/pkgs/build-support/rust/test/import-cargo-lock/default.nix +++ b/pkgs/build-support/rust/test/import-cargo-lock/default.nix @@ -1,4 +1,4 @@ -{ callPackage }: +{ callPackage, writers, python3Packages }: # Build like this from nixpkgs root: # $ nix-build -A tests.importCargoLock @@ -12,4 +12,9 @@ gitDependencyBranch = callPackage ./git-dependency-branch { }; maturin = callPackage ./maturin { }; v1 = callPackage ./v1 { }; + gitDependencyWorkspaceInheritance = callPackage ./git-dependency-workspace-inheritance { + replaceWorkspaceValues = writers.writePython3 "replace-workspace-values" + { libraries = with python3Packages; [ tomli tomli-w ]; flakeIgnore = [ "E501" ]; } + (builtins.readFile ../../replace-workspace-values.py); + }; } diff --git a/pkgs/build-support/rust/test/import-cargo-lock/git-dependency-workspace-inheritance/crate.toml b/pkgs/build-support/rust/test/import-cargo-lock/git-dependency-workspace-inheritance/crate.toml new file mode 100644 index 000000000000..a50855706dff --- /dev/null +++ b/pkgs/build-support/rust/test/import-cargo-lock/git-dependency-workspace-inheritance/crate.toml @@ -0,0 +1,6 @@ +[package] +version = { workspace = true } + +[dependencies] +foo = { workspace = true, features = ["cat"] } +bar = "1.0.0" diff --git a/pkgs/build-support/rust/test/import-cargo-lock/git-dependency-workspace-inheritance/default.nix b/pkgs/build-support/rust/test/import-cargo-lock/git-dependency-workspace-inheritance/default.nix new file mode 100644 index 000000000000..138b7179b95f --- /dev/null +++ b/pkgs/build-support/rust/test/import-cargo-lock/git-dependency-workspace-inheritance/default.nix @@ -0,0 +1,7 @@ +{ replaceWorkspaceValues, runCommand }: + +runCommand "git-dependency-workspace-inheritance-test" { } '' + cp --no-preserve=mode ${./crate.toml} "$out" + ${replaceWorkspaceValues} "$out" ${./workspace.toml} + diff -u "$out" ${./want.toml} +'' diff --git a/pkgs/build-support/rust/test/import-cargo-lock/git-dependency-workspace-inheritance/want.toml b/pkgs/build-support/rust/test/import-cargo-lock/git-dependency-workspace-inheritance/want.toml new file mode 100644 index 000000000000..3c11228e97c2 --- /dev/null +++ b/pkgs/build-support/rust/test/import-cargo-lock/git-dependency-workspace-inheritance/want.toml @@ -0,0 +1,12 @@ +[package] +version = "1.0.0" + +[dependencies] +bar = "1.0.0" + +[dependencies.foo] +features = [ + "cat", + "meow", +] +version = "1.0.0" diff --git a/pkgs/build-support/rust/test/import-cargo-lock/git-dependency-workspace-inheritance/workspace.toml b/pkgs/build-support/rust/test/import-cargo-lock/git-dependency-workspace-inheritance/workspace.toml new file mode 100644 index 000000000000..c58112a782d0 --- /dev/null +++ b/pkgs/build-support/rust/test/import-cargo-lock/git-dependency-workspace-inheritance/workspace.toml @@ -0,0 +1,5 @@ +[workspace.package] +version = "1.0.0" + +[workspace.dependencies] +foo = { version = "1.0.0", features = ["meow"] }