Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
at master 151 lines 4.7 kB view raw
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 6import sys 7from typing import Any 8 9import tomli 10import tomli_w 11 12 13def load_file(path: str) -> dict[str, Any]: 14 with open(path, "rb") as f: 15 return tomli.load(f) 16 17 18# This replicates the dependency merging logic from Cargo. 19# See `inner_dependency_inherit_with`: 20# https://github.com/rust-lang/cargo/blob/4de0094ac78743d2c8ff682489e35c8a7cafe8e4/src/cargo/util/toml/mod.rs#L982 21def replace_key( 22 workspace_manifest: dict[str, Any], table: dict[str, Any], section: str, key: str 23) -> bool: 24 if ( 25 isinstance(table[key], dict) 26 and "workspace" in table[key] 27 and table[key]["workspace"] is True 28 ): 29 print("replacing " + key) 30 31 local_dep = table[key] 32 del local_dep["workspace"] 33 34 try: 35 workspace_dep = workspace_manifest[section][key] 36 except KeyError: 37 # Key is not present in workspace manifest, we can't inherit the value, so we mark it for deletion 38 table[key] = {} 39 return True 40 41 if section == "dependencies": 42 if isinstance(workspace_dep, str): 43 workspace_dep = {"version": workspace_dep} 44 45 final: dict[str, Any] = workspace_dep.copy() 46 47 merged_features = local_dep.pop("features", []) + workspace_dep.get( 48 "features", [] 49 ) 50 if merged_features: 51 final["features"] = merged_features 52 53 local_default_features = local_dep.pop( 54 "default-features", local_dep.pop("default_features", None) 55 ) 56 workspace_default_features = workspace_dep.get( 57 "default-features", workspace_dep.get("default_features") 58 ) 59 60 if not workspace_default_features and local_default_features: 61 final["default-features"] = True 62 63 optional = local_dep.pop("optional", False) 64 if optional: 65 final["optional"] = True 66 67 if "package" in local_dep: 68 final["package"] = local_dep.pop("package") 69 70 if local_dep: 71 raise Exception( 72 f"Unhandled keys in inherited dependency {key}: {local_dep}" 73 ) 74 75 table[key] = final 76 elif section == "package": 77 table[key] = workspace_dep 78 79 return True 80 81 return False 82 83 84def replace_dependencies( 85 workspace_manifest: dict[str, Any], root: dict[str, Any] 86) -> bool: 87 changed = False 88 89 for key in ["dependencies", "dev-dependencies", "build-dependencies"]: 90 if key in root: 91 for k in root[key].keys(): 92 changed |= replace_key(workspace_manifest, root[key], "dependencies", k) 93 94 return changed 95 96 97def main() -> None: 98 top_cargo_toml = load_file(sys.argv[2]) 99 100 if "workspace" not in top_cargo_toml: 101 # If top_cargo_toml is not a workspace manifest, then this script was probably 102 # ran on something that does not actually use workspace dependencies 103 print(f"{sys.argv[2]} is not a workspace manifest, doing nothing.") 104 return 105 106 crate_manifest = load_file(sys.argv[1]) 107 workspace_manifest = top_cargo_toml["workspace"] 108 109 if "workspace" in crate_manifest: 110 return 111 112 changed = False 113 114 to_remove = [] 115 for key in crate_manifest["package"].keys(): 116 changed_key = replace_key( 117 workspace_manifest, crate_manifest["package"], "package", key 118 ) 119 if changed_key and crate_manifest["package"][key] == {}: 120 # Key is missing from workspace manifest, mark for deletion 121 to_remove.append(key) 122 changed |= changed_key 123 # Remove keys which have no value 124 for key in to_remove: 125 del crate_manifest["package"][key] 126 127 changed |= replace_dependencies(workspace_manifest, crate_manifest) 128 129 if "target" in crate_manifest: 130 for key in crate_manifest["target"].keys(): 131 changed |= replace_dependencies( 132 workspace_manifest, crate_manifest["target"][key] 133 ) 134 135 if ( 136 "lints" in crate_manifest 137 and "workspace" in crate_manifest["lints"] 138 and crate_manifest["lints"]["workspace"] is True 139 ): 140 crate_manifest["lints"] = workspace_manifest["lints"] 141 changed = True 142 143 if not changed: 144 return 145 146 with open(sys.argv[1], "wb") as f: 147 tomli_w.dump(crate_manifest, f) 148 149 150if __name__ == "__main__": 151 main()