From d519e6863f93d6ef8c5bd38a86c059d7d9135b5d 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 e571c01f95c5d00..669e006fd964468 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 @@ -89,6 +89,11 @@ let sha256 = pkg.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 @@ -158,6 +163,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 000000000000000..fe5462883302774 --- /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 c5c7b2f1c931bef..749a1ef93edb635 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 @@ -11,4 +11,9 @@ gitDependencyTag = callPackage ./git-dependency-tag { }; gitDependencyBranch = callPackage ./git-dependency-branch { }; maturin = callPackage ./maturin { }; + 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 000000000000000..a50855706dff77f --- /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 000000000000000..138b7179b95fd06 --- /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 000000000000000..3c11228e97c2f09 --- /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 000000000000000..c58112a782d036d --- /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"] }