1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
#!/usr/bin/env vpython3
# Copyright 2025 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# This module contains utilities for working with information from
# `//third_party/rust/chromium_crates_io/Cargo.lock`.
#
# Throughout the module, the following naming conventions are used (illustrated
# with a crate named `syn` and published as version `2.0.50`):
#
# * `crate_name` : "syn" string
# * `crate_version`: "2.0.50" string
# * `crate_id` : "syn@2.0.50" string (syntax used by `cargo`)
# * `crate_epoch` : "v2" string (syntax used in dir names under
# //third_party/rust/<crate name>/<crate epoch>)
#
# Note that `crate_name` may not be unique (e.g. if there is both `syn@1.0.109`
# and `syn@2.0.50`). Also note that f`{crate_name}@{crate_epoch}` doesn't
# change during a minor version update (such as the one that this script
import os
import sys
import toml
from typing import Set
THIS_DIR = os.path.dirname(__file__)
CHROMIUM_DIR = os.path.normpath(os.path.join(THIS_DIR, '..', '..', '..'))
THIRD_PARTY_RUST_DIR = os.path.join(CHROMIUM_DIR, "third_party", "rust")
CRATES_DIR = os.path.join(THIRD_PARTY_RUST_DIR, "chromium_crates_io")
CARGO_LOCK_FILEPATH = os.path.join(CRATES_DIR, 'Cargo.lock')
VENDOR_DIR = os.path.join(CRATES_DIR, "vendor")
def GetCurrentCrateIds() -> Set[str]:
"""Parses Cargo.lock and returns a set of crate ids.
Example return value: `set(["serde@1.0.197", "syn@2.0.50", ...])`.
"""
with open(CARGO_LOCK_FILEPATH) as f:
t = toml.load(f)
result = set()
for p in t["package"]:
name = p["name"]
version = p["version"]
crate_id = f"{name}@{version}"
assert crate_id not in result
result.add(crate_id)
return result
def ConvertCrateIdToCrateEpoch(crate_id: str) -> str:
_AssertIsCrateId(crate_id)
crate_version = ConvertCrateIdToCrateVersion(crate_id)
v = crate_version.split('.')
if v[0] == '0':
return f'v0_{v[1]}'
return f'v{v[0]}'
def ConvertCrateIdToCrateName(crate_id: str) -> str:
""" Converts a `crate_id` into a `crate_name`."""
_AssertIsCrateId(crate_id)
return crate_id[:crate_id.find("@")]
def ConvertCrateIdToCrateVersion(crate_id: str) -> str:
""" Converts a `crate_id` into a `crate_version`."""
_AssertIsCrateId(crate_id)
crate_version = crate_id[crate_id.find("@") + 1:]
return crate_version
def ConvertCrateIdToBuildDir(crate_id: str) -> str:
""" Converts a `crate_id` (e.g. "foo@1.2.3") into a path to an epoch dir.
Example return value:
`"<path to chromium root>\\third_party\\rust\\foo\\v1"`
"""
_AssertIsCrateId(crate_id)
return os.path.join(
CHROMIUM_DIR, _ConvertCrateIdToBuildDirRelativeToChromiumRoot(crate_id))
def ConvertCrateIdToVendorDir(crate_id: str) -> str:
""" Converts a `crate_id` (e.g. "foo@1.2.3") into a path to a target dir.
Example return value:
`"<path to chromium root>\\third_party\\rust\\chromium_crates_io\\vendor\\foo-v1"`
"""
_AssertIsCrateId(crate_id)
crate_name = ConvertCrateIdToCrateName(crate_id)
crate_epoch = ConvertCrateIdToCrateEpoch(crate_id)
crate_vendor_dir = os.path.join(VENDOR_DIR, f"{crate_name}-{crate_epoch}")
return crate_vendor_dir
def ConvertCrateIdToGnLabel(crate_id: str) -> str:
""" Converts a `crate_id` (e.g. "foo@1.2.3") into a GN label.
See also
https://gn.googlesource.com/gn/+/main/docs/reference.md#labels
Example return value: `"//third_party/rust/foo/v1:lib"`
"""
_AssertIsCrateId(crate_id)
dir_name = _ConvertCrateIdToBuildDirRelativeToChromiumRoot(crate_id)
dir_name = dir_name.replace(os.sep, "/") # GN uses `/` as a path separator.
return f"//{dir_name}:lib"
def IsPlaceholderCrate(crate_id: str) -> bool:
""" Determines if `crate_id` corresponds to a placeholder package.
See also `//tools/crates/gnrt/removed_crate.md`.
"""
_AssertIsCrateId(crate_id)
vendor_dir = ConvertCrateIdToVendorDir(crate_id)
crate_cargo_toml_path = os.path.join(vendor_dir, "Cargo.toml")
KNOWN_PATTERN = "@generated from `tools/crates/gnrt/removed_Cargo.toml.hbs`"
try:
with open(crate_cargo_toml_path) as f:
return KNOWN_PATTERN in f.read()
except:
return False
def GetPlaceholderCrateIdForTesting() -> str:
""" Test helper for getting a placeholder `crate_id` like `"cc-1.2.22"`.
"""
# This test helper assumes that `cc` is listed in
# `chromium_crates_io/Cargo.lock` and that
# `chromium_crates_io/vendor/cc...` contains a placeholder crate (see
# `tools/crates/gnrt/removed_crate.md`).
#
# Unit tests that depend on external state are a bit icky... But it
# seems that this assumption should hold "forever" + this helps write
# more tests for other stuff, so let ignore the ickiness...
return list(filter(lambda crate_id: "cc@" in crate_id,
GetCurrentCrateIds()))[0]
def _ConvertCrateIdToBuildDirRelativeToChromiumRoot(crate_id: str) -> str:
""" Converts a `crate_id` (e.g. "foo@1.2.3") into an epoch dir.
The returned dir is relative to Chromium root.
Example return value: `"//third_party/rust/foo/v1"`
"""
crate_name = ConvertCrateIdToCrateName(crate_id)
crate_name = crate_name.replace("-", "_")
epoch = ConvertCrateIdToCrateEpoch(crate_id)
target = os.path.join("third_party", "rust", crate_name, epoch)
return target
def _AssertIsCrateId(crate_id: str) -> bool:
if not isinstance(crate_id, str) or "@" not in crate_id:
raise RuntimeError(f"This is not a valid crate id: {crate_id}")
assert __name__ != '__main__'
|