File: crate_utils.py

package info (click to toggle)
chromium 138.0.7204.157-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,864 kB
  • sloc: cpp: 34,936,859; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,967; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (162 lines) | stat: -rwxr-xr-x 5,797 bytes parent folder | download | duplicates (3)
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__'