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"] }