···9import tomllib
10from pathlib import Path
11from typing import Any, TypedDict, cast
01213import requests
14from requests.adapters import HTTPAdapter, Retry
···19def load_toml(path: Path) -> dict[str, Any]:
20 with open(path, "rb") as f:
21 return tomllib.load(f)
000000000222324def download_file_with_checksum(url: str, destination_path: Path) -> str:
···93 git_sha_rev: str
949596-def parse_git_source(source: str) -> GitSourceInfo:
97 match = GIT_SOURCE_REGEX.match(source)
98 if match is None:
99 raise Exception(f"Unable to process git source: {source}.")
100- return cast(GitSourceInfo, match.groupdict(default=None))
00000000101102103def create_vendor_staging(lockfile_path: Path, out_dir: Path) -> None:
104- cargo_toml = load_toml(lockfile_path)
0105106 git_packages: list[dict[str, Any]] = []
107 registry_packages: list[dict[str, Any]] = []
108109- for pkg in cargo_toml["package"]:
110 # ignore local dependenices
111 if "source" not in pkg.keys():
112 eprint(f"Skipping local dependency: {pkg["name"]}")
···122123 git_sha_rev_to_url: dict[str, str] = {}
124 for pkg in git_packages:
125- source_info = parse_git_source(pkg["source"])
126 git_sha_rev_to_url[source_info["git_sha_rev"]] = source_info["url"]
127128 out_dir.mkdir(exist_ok=True)
···207 out_dir.mkdir(exist_ok=True)
208 shutil.copy(lockfile_path, out_dir / "Cargo.lock")
209210- cargo_toml = load_toml(lockfile_path)
0211212 config_lines = [
213 '[source.vendored-sources]',
···217 ]
218219 seen_source_keys = set()
220- for pkg in cargo_toml["package"]:
221222 # ignore local dependenices
223 if "source" not in pkg.keys():
···230231 if source.startswith("git+"):
232233- source_info = parse_git_source(pkg["source"])
0234 git_sha_rev = source_info["git_sha_rev"]
235 git_tree = vendor_staging_dir / "git" / git_sha_rev
236
···9import tomllib
10from pathlib import Path
11from typing import Any, TypedDict, cast
12+from urllib.parse import unquote
1314import requests
15from requests.adapters import HTTPAdapter, Retry
···20def load_toml(path: Path) -> dict[str, Any]:
21 with open(path, "rb") as f:
22 return tomllib.load(f)
23+24+25+def get_lockfile_version(cargo_lock_toml: dict[str, Any]) -> int:
26+ # lockfile v1 and v2 don't have the `version` key, so assume v2
27+ version = cargo_lock_toml.get("version", 2)
28+29+ # TODO: add logic for differentiating between v1 and v2
30+31+ return version
323334def download_file_with_checksum(url: str, destination_path: Path) -> str:
···103 git_sha_rev: str
104105106+def parse_git_source(source: str, lockfile_version: int) -> GitSourceInfo:
107 match = GIT_SOURCE_REGEX.match(source)
108 if match is None:
109 raise Exception(f"Unable to process git source: {source}.")
110+111+ source_info = cast(GitSourceInfo, match.groupdict(default=None))
112+113+ # the source URL is URL-encoded in lockfile_version >=4
114+ # since we just used regex to parse it we have to manually decode the escaped branch/tag name
115+ if lockfile_version >= 4 and source_info["value"] is not None:
116+ source_info["value"] = unquote(source_info["value"])
117+118+ return source_info
119120121def create_vendor_staging(lockfile_path: Path, out_dir: Path) -> None:
122+ cargo_lock_toml = load_toml(lockfile_path)
123+ lockfile_version = get_lockfile_version(cargo_lock_toml)
124125 git_packages: list[dict[str, Any]] = []
126 registry_packages: list[dict[str, Any]] = []
127128+ for pkg in cargo_lock_toml["package"]:
129 # ignore local dependenices
130 if "source" not in pkg.keys():
131 eprint(f"Skipping local dependency: {pkg["name"]}")
···141142 git_sha_rev_to_url: dict[str, str] = {}
143 for pkg in git_packages:
144+ source_info = parse_git_source(pkg["source"], lockfile_version)
145 git_sha_rev_to_url[source_info["git_sha_rev"]] = source_info["url"]
146147 out_dir.mkdir(exist_ok=True)
···226 out_dir.mkdir(exist_ok=True)
227 shutil.copy(lockfile_path, out_dir / "Cargo.lock")
228229+ cargo_lock_toml = load_toml(lockfile_path)
230+ lockfile_version = get_lockfile_version(cargo_lock_toml)
231232 config_lines = [
233 '[source.vendored-sources]',
···237 ]
238239 seen_source_keys = set()
240+ for pkg in cargo_lock_toml["package"]:
241242 # ignore local dependenices
243 if "source" not in pkg.keys():
···250251 if source.startswith("git+"):
252253+ source_info = parse_git_source(pkg["source"], lockfile_version)
254+255 git_sha_rev = source_info["git_sha_rev"]
256 git_tree = vendor_staging_dir / "git" / git_sha_rev
257
+15-2
pkgs/build-support/rust/import-cargo-lock.nix
···4950 parsedLockFile = builtins.fromTOML lockFileContents;
51000052 packages = parsedLockFile.package;
5354 # There is no source attribute for the source package itself. But
···202 # Cargo is happy with empty metadata.
203 printf '{"files":{},"package":null}' > "$out/.cargo-checksum.json"
204000000000205 # Set up configuration for the vendor directory.
206 cat > $out/.cargo-config <<EOF
207- [source."${gitParts.url}${lib.optionalString (gitParts ? type) "?${gitParts.type}=${gitParts.value}"}"]
208 git = "${gitParts.url}"
209- ${lib.optionalString (gitParts ? type) "${gitParts.type} = \"${gitParts.value}\""}
210 replace-with = "vendored-sources"
211 EOF
212 ''
···4950 parsedLockFile = builtins.fromTOML lockFileContents;
5152+ # lockfile v1 and v2 don't have the `version` key, so assume v2
53+ # we can implement more fine-grained detection later, if needed
54+ lockFileVersion = parsedLockFile.version or 2;
55+56 packages = parsedLockFile.package;
5758 # There is no source attribute for the source package itself. But
···206 # Cargo is happy with empty metadata.
207 printf '{"files":{},"package":null}' > "$out/.cargo-checksum.json"
208209+ ${lib.optionalString (gitParts ? type) ''
210+ gitPartsValue=${lib.escapeShellArg gitParts.value}
211+ # starting with lockfile version v4 the git source url contains encoded query parameters
212+ # our regex parser does not know how to unescape them to get the actual value, so we do it here
213+ ${lib.optionalString (lockFileVersion >= 4) ''
214+ gitPartsValue=$(${lib.getExe python3Packages.python} -c "import sys, urllib.parse; print(urllib.parse.unquote(sys.argv[1]))" "$gitPartsValue")
215+ ''}
216+ ''}
217+218 # Set up configuration for the vendor directory.
219 cat > $out/.cargo-config <<EOF
220+ [source."${pkg.source}"]
221 git = "${gitParts.url}"
222+ ${lib.optionalString (gitParts ? type) "${gitParts.type} = \"$gitPartsValue\""}
223 replace-with = "vendored-sources"
224 EOF
225 ''