tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit b05a254d771304b0a594a64f9a10a1f0771ff577
parent 63ecf8273b0288faae1c62e70ee7d41265c5293e
Author: Mike Hommey <mh+mozilla@glandium.org>
Date:   Mon,  3 Nov 2025 23:23:44 +0000

Bug 1995920 - Attempt to preserve the current Cargo.toml contents when vendoring with cargo >= 1.90. r=firefox-build-system-reviewers,ahochheiden

Differential Revision: https://phabricator.services.mozilla.com/D269706

Diffstat:
Mpython/mozbuild/mozbuild/vendor/vendor_rust.py | 123++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
1 file changed, 84 insertions(+), 39 deletions(-)

diff --git a/python/mozbuild/mozbuild/vendor/vendor_rust.py b/python/mozbuild/mozbuild/vendor/vendor_rust.py @@ -2,6 +2,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, # You can obtain one at http://mozilla.org/MPL/2.0/. +import copy import errno import hashlib import json @@ -16,6 +17,7 @@ from pathlib import Path import mozpack.path as mozpath import toml +import tomlkit from looseversion import LooseVersion from mozboot.util import MINIMUM_RUST_VERSION @@ -879,20 +881,61 @@ license file's hash. ) ) + def recursive_sort(obj): + if isinstance(obj, tomlkit.items.Table): + new_obj = obj.copy() + body = [(k, recursive_sort(v)) for k, v in new_obj.value.body] + # Only order the direct elements in the Table. Anything after + # the first whitespace (key is None) or AoT is not expected to + # be a direct element. This is a more or less assumption based + # on the original order cargo will have written out. + for n, (k, v) in enumerate(body): + if k is None or v.is_aot(): + break + else: + n = len(body) + body[:n] = sorted(body[:n], key=lambda x: str(x[0])) + new_obj.value.body[:] = body + return new_obj + if isinstance(obj, tomlkit.items.AoT): + # Somehow obj.copy() yields a list instead of a AoT + new_obj = copy.copy(obj) + body = [recursive_sort(v) for v in new_obj.body] + new_obj.body[:] = body + return new_obj + return obj + # cargo 1.89 started adding things that older versions didn't add, but # it's a tough sell to bump the vendoring requirement to 1.89 when we're # still using 1.86 on CI. - if self.cargo_version(cargo) >= "1.89": + cargo_version = self.cargo_version(cargo) + if cargo_version >= "1.89": for package in cargo_lock["package"]: - # Crates vendored from crates.io are affected by changes, but not - # those vendored from git. - if not package.get("source", "").startswith("registry+"): + source = package.get("source") + if not source: continue + unlinked = [] + modified = {} package_dir = Path(vendor_dir) / package["name"] - # Cargo.toml.orig was not included before but now is. - unlinked = ["Cargo.toml.orig"] with (package_dir / "Cargo.toml").open(encoding="utf-8") as fh: - toml_data = toml.load(fh) + toml_data = tomlkit.parse(fh.read()) + # Crates vendored from crates.io are affected by changes from cargo 1.89, + # but not those vendored from git. However, crates vendored from git are + # affected by other changes happening starting with cargo 1.90: the + # metadata ends up not ordered in the same manner as it used to be. + if not source.startswith("registry+"): + metadata = toml_data.get("package", {}).get("metadata", {}) + if metadata and cargo_version >= "1.90": + with (package_dir / "Cargo.toml").open("wb") as fh: + toml_data["package"]["metadata"] = recursive_sort(metadata) + raw_data = tomlkit.dumps(toml_data).encode("utf-8") + modified["Cargo.toml"] = hashlib.sha256( + raw_data + ).hexdigest() + fh.write(raw_data) + else: + # Cargo.toml.orig was not included before but now is. + unlinked += ["Cargo.toml.orig"] cargo_package = toml_data.get("package", {}) # A readme explicitly listed in package.readme is now included # even when it's not in package.include. @@ -908,39 +951,39 @@ license file's hash. except FileNotFoundError: pass - # dotfiles weren't included before, but now are. - for path in package_dir.glob("**/.*"): - # The checksum file is handled separately because it needs to - # be updated. - if path.name == ".cargo-checksum.json": - continue - if path.is_dir(): - for root_path, dirs, files in os.walk(path, topdown=False): - root = Path(root_path) - for name in files: - to_unlink = root / name - try: - to_unlink.unlink() - unlinked.append( - mozpath.normsep( - str(to_unlink.relative_to(package_dir)) + # dotfiles weren't included before, but now are. + for path in package_dir.glob("**/.*"): + # The checksum file is handled separately because it needs to + # be updated. + if path.name == ".cargo-checksum.json": + continue + if path.is_dir(): + for root_path, dirs, files in os.walk(path, topdown=False): + root = Path(root_path) + for name in files: + to_unlink = root / name + try: + to_unlink.unlink() + unlinked.append( + mozpath.normsep( + str(to_unlink.relative_to(package_dir)) + ) ) - ) - except FileNotFoundError: - pass - for name in dirs: - try: - (root / name).rmdir() - except OSError: - pass - else: - try: - path.unlink() - unlinked.append( - mozpath.normsep(str(path.relative_to(package_dir))) - ) - except FileNotFoundError: - pass + except FileNotFoundError: + pass + for name in dirs: + try: + (root / name).rmdir() + except OSError: + pass + else: + try: + path.unlink() + unlinked.append( + mozpath.normsep(str(path.relative_to(package_dir))) + ) + except FileNotFoundError: + pass # Update the checksums with the changes we made. checksum_json = package_dir / ".cargo-checksum.json" @@ -951,6 +994,8 @@ license file's hash. del checksum_data["files"][path] except KeyError: pass + for path, sha256 in modified.items(): + checksum_data["files"][path] = sha256 with checksum_json.open(mode="w", encoding="utf-8") as fh: json.dump(checksum_data, fh, separators=(",", ":"))