| #!/usr/bin/env python3 |
| # Copyright 2021 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| # |
| # Run inside `cros_sdk` to uprev dependency versions between Cargo.lock and |
| # chromiumos-overlay ebuild files. |
| |
| import re |
| import glob |
| import os |
| import sys |
| import subprocess |
| from distutils.version import StrictVersion |
| from datetime import datetime |
| |
| # Ideally we would install a toml parser, but this will do for a quick little |
| # tool. |
| CARGO_LOCK_REGEX = re.compile( |
| r"""name = "([^"]+)" |
| version = "([^"]+)" |
| source =""" |
| ) |
| |
| IGNORED_PACKAGES = ["pin-utils", "pkg-config"] |
| |
| DEV_RUST_PATH = "../../third_party/chromiumos-overlay/dev-rust" |
| EBUILD_FILE_GLOB = f"{DEV_RUST_PATH}/*/*.ebuild" |
| EBUILD_FILE_REGEX = re.compile(r"([\w\-_]+)-([0-9\.]+)\.ebuild") |
| YEAR = datetime.today().year |
| |
| |
| def ebuild_template(package: str): |
| return f"""\ |
| # Copyright {YEAR} The Chromium OS Authors. All rights reserved. |
| # Distributed under the terms of the GNU General Public License v2 |
| |
| EAPI="7" |
| |
| CROS_RUST_REMOVE_DEV_DEPS=1 |
| |
| inherit cros-rust |
| |
| DESCRIPTION="Build file for the {package} crate." |
| HOMEPAGE="https://crates.io/crates/{package}" |
| SRC_URI="https://crates.io/api/v1/crates/${{PN}}/${{PV}}/download -> ${{P}}.crate" |
| |
| LICENSE="|| ( MIT Apache-2.0 )" |
| SLOT="${{PV}}/${{PR}}" |
| KEYWORDS="*" |
| |
| # TODO: Add crate dependencies |
| DEPEND="" |
| """ |
| |
| |
| def ebuild_file_path(package: str, version: str): |
| return f"{DEV_RUST_PATH}/{package}/{package}-{version}.ebuild" |
| |
| |
| def parse_cargo_lock(): |
| """Parses Cargo.lock file and returns (package, version) pairs.""" |
| with open("Cargo.lock", "r") as lock_file: |
| lock_str = lock_file.read() |
| for match in CARGO_LOCK_REGEX.finditer(lock_str): |
| yield (match.group(1), match.group(2)) |
| |
| |
| def all_ebuild_versions(): |
| """Returns (package, version) pairs of ebuild files in dev-rust.""" |
| for ebuild_path in glob.glob(EBUILD_FILE_GLOB): |
| ebuild_name = os.path.basename(ebuild_path) |
| match = EBUILD_FILE_REGEX.match(ebuild_name) |
| if match: |
| yield (match.group(1), match.group(2)) |
| |
| |
| def ebuild_versions_dict(): |
| """Returns a dict of package versions of all ebuild files in dev-rust.""" |
| versions: dict[str, list[str]] = {} |
| for (package, version) in all_ebuild_versions(): |
| if package in versions: |
| versions[package].append(version) |
| versions[package].sort(key=StrictVersion) |
| else: |
| versions[package] = [version] |
| return versions |
| |
| |
| def update_manifest(package: str, version: str): |
| """Regenerate ebuild manifest for the provided package/version.""" |
| cmd = ["ebuild", ebuild_file_path(package, version), "manifest"] |
| print(" ", " ".join(cmd)) |
| subprocess.run(cmd) |
| |
| |
| def uprev_ebuild(package: str, new_version: str, old_version: str): |
| """Updates the ebuild file from `old_version` to `new_version`.""" |
| old_path = ebuild_file_path(package, old_version) |
| new_path = ebuild_file_path(package, new_version) |
| print(f" {old_path} -> {new_path}") |
| os.rename(old_path, new_path) |
| update_manifest(package, new_version) |
| |
| |
| def add_ebuild(package: str, version: str): |
| """Creates a new ebuild file for the provided package.""" |
| ebuild_path = ebuild_file_path(package, version) |
| print(f" Writing {ebuild_path}") |
| open(ebuild_path, "w").write(ebuild_template(package)) |
| update_manifest(package, version) |
| |
| |
| def update_cargo(package: str, latest_version: str): |
| """Runs `cargo update` to update the version in Cargo.lock.""" |
| cmd = ["cargo", "update", "-p", package, "--precise", latest_version] |
| print(" ", " ".join(cmd)) |
| subprocess.run(cmd) |
| |
| |
| def confirm(question: str): |
| print(f"{question} [y/N]") |
| return sys.stdin.readline().strip().lower() == "y" |
| |
| |
| def main(): |
| ebuild_packages = ebuild_versions_dict() |
| for (package, cargo_version) in parse_cargo_lock(): |
| ebuild_versions = ebuild_packages.get(package, []) |
| if package in IGNORED_PACKAGES: |
| continue |
| if cargo_version in ebuild_versions: |
| continue |
| if not ebuild_versions: |
| print(f"{package}: No ebuild file.") |
| if confirm("Create ebuild?"): |
| add_ebuild(package, cargo_version) |
| elif StrictVersion(ebuild_versions[-1]) > StrictVersion(cargo_version): |
| print( |
| f"{package}: Cargo version {cargo_version} is older than " |
| f"latest ebuild version ({', '.join(ebuild_versions)})." |
| ) |
| if confirm("Update Cargo.lock?"): |
| update_cargo(package, ebuild_versions[-1]) |
| else: |
| print( |
| f"{package}: Ebuild versions ({', '.join(ebuild_versions)}) " |
| f"are older than cargo version {cargo_version}." |
| ) |
| if confirm("Uprev ebuild?"): |
| uprev_ebuild(package, cargo_version, ebuild_versions[-1]) |
| |
| |
| if __name__ == "__main__": |
| main() |