aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Burgess IV <gbiv@google.com>2024-04-03 13:03:23 -0600
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2024-04-10 23:00:04 +0000
commit91004c1369b35a2d7127bb5b6c1ae33e582fe993 (patch)
treec8bf9d716dfbec26b45aed38b6b0bef548191583
parent26d580d4ecdccce9b92188d13d7e69a58038e2f0 (diff)
downloadtoolchain-utils-91004c1369b35a2d7127bb5b6c1ae33e582fe993.tar.gz
llvm_tools: add revision autodetection to the patch cleanup script
This is the last bit of b/332589934#comment3. Once this lands & is tested, we can add a tiny script to work in a cros worktree, add that to cron, and consider this fixed. BUG=b:332589934 TEST=Ran on Chrotomation Change-Id: I812f9e75e2349b88308be89687687131159bafc3 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/5435855 Commit-Queue: George Burgess <gbiv@chromium.org> Reviewed-by: Jordan Abrahams-Whitehead <ajordanr@google.com> Tested-by: George Burgess <gbiv@chromium.org>
-rwxr-xr-xllvm_tools/clean_up_old_llvm_patches.py118
-rwxr-xr-xllvm_tools/clean_up_old_llvm_patches_test.py68
2 files changed, 175 insertions, 11 deletions
diff --git a/llvm_tools/clean_up_old_llvm_patches.py b/llvm_tools/clean_up_old_llvm_patches.py
index fef4cf00..5418c707 100755
--- a/llvm_tools/clean_up_old_llvm_patches.py
+++ b/llvm_tools/clean_up_old_llvm_patches.py
@@ -6,8 +6,11 @@
"""Removes all LLVM patches before a certain point."""
import argparse
+import importlib.abc
+import importlib.util
import logging
from pathlib import Path
+import re
import subprocess
import sys
import textwrap
@@ -93,6 +96,63 @@ def upload_changes(cros_overlay: Path) -> None:
git_utils.try_set_autosubmit_labels(cros_overlay, cl_id)
+def find_chromeos_llvm_version(chromiumos_overlay: Path) -> int:
+ sys_devel_llvm = chromiumos_overlay / "sys-devel" / "llvm"
+
+ # Pick this from the name of the stable ebuild; 9999 is a bit harder to
+ # parse, and stable is just as good.
+ stable_llvm_re = re.compile(r"^llvm.*_pre(\d+)-r\d+\.ebuild$")
+ match_gen = (
+ stable_llvm_re.fullmatch(x.name) for x in sys_devel_llvm.iterdir()
+ )
+ matches = [int(x.group(1)) for x in match_gen if x]
+
+ if len(matches) != 1:
+ raise ValueError(
+ f"Expected exactly one ebuild name match in {sys_devel_llvm}; "
+ f"found {len(matches)}"
+ )
+ return matches[0]
+
+
+def find_android_llvm_version(android_toolchain_tree: Path) -> int:
+ android_version_py = (
+ android_toolchain_tree
+ / "toolchain"
+ / "llvm_android"
+ / "android_version.py"
+ )
+
+ # Per
+ # https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly.
+ # Parsing this file is undesirable, since `_svn_revision`, as a variable,
+ # isn't meant to be relied on. Let Python handle the logic instead.
+ module_name = "android_version"
+ android_version = sys.modules.get(module_name)
+ if android_version is None:
+ spec = importlib.util.spec_from_file_location(
+ module_name, android_version_py
+ )
+ if not spec:
+ raise ImportError(
+ f"Failed loading module spec from {android_version_py}"
+ )
+ android_version = importlib.util.module_from_spec(spec)
+ sys.modules[module_name] = android_version
+ loader = spec.loader
+ if not isinstance(loader, importlib.abc.Loader):
+ raise ValueError(
+ f"Loader for {android_version_py} was of type "
+ f"{type(loader)}; wanted an importlib.util.Loader"
+ )
+ loader.exec_module(android_version)
+
+ rev = android_version.get_svn_revision()
+ match = re.match(r"r(\d+)", rev)
+ assert match, f"Invalid SVN revision: {rev!r}"
+ return int(match.group(1))
+
+
def get_opts(my_dir: Path, argv: List[str]) -> argparse.Namespace:
"""Returns options for the script."""
@@ -101,20 +161,19 @@ def get_opts(my_dir: Path, argv: List[str]) -> argparse.Namespace:
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument(
- "--chromiumos-overlay",
+ "--android-toolchain",
type=Path,
help="""
- Path to chromiumos-overlay. Will autodetect if none is specified. If
- autodetection fails and none is specified, this script will fail.
+ Path to an android-toolchain repo root. Only meaningful if
+ `--autodetect-revision` is passed.
""",
)
parser.add_argument(
- "--revision",
- type=int,
+ "--chromiumos-overlay",
+ type=Path,
help="""
- Revision to delete before (exclusive). All patches that stopped
- applying before this will be removed. Phrased as an int, e.g.,
- `--revision=1234`.
+ Path to chromiumos-overlay. Will autodetect if none is specified. If
+ autodetection fails and none is specified, this script will fail.
""",
)
parser.add_argument(
@@ -130,6 +189,26 @@ def get_opts(my_dir: Path, argv: List[str]) -> argparse.Namespace:
default reviewers, and starts CQ+1 (among other convenience features).
""",
)
+
+ revision_opt = parser.add_mutually_exclusive_group(required=True)
+ revision_opt.add_argument(
+ "--revision",
+ type=int,
+ help="""
+ Revision to delete before (exclusive). All patches that stopped
+ applying before this will be removed. Phrased as an int, e.g.,
+ `--revision=1234`.
+ """,
+ )
+ revision_opt.add_argument(
+ "--autodetect-revision",
+ action="store_true",
+ help="""
+ Autodetect the value for `--revision`. If this is passed, you must also
+ pass `--android-toolchain`. This sets `--revision` to the _lesser_ of
+ Android's current LLVM version, and ChromeOS'.
+ """,
+ )
opts = parser.parse_args(argv)
if not opts.chromiumos_overlay:
@@ -139,6 +218,23 @@ def get_opts(my_dir: Path, argv: List[str]) -> argparse.Namespace:
"Failed to autodetect --chromiumos-overlay; please pass a value"
)
opts.chromiumos_overlay = maybe_overlay
+
+ if opts.autodetect_revision:
+ if not opts.android_toolchain:
+ parser.error(
+ "--android-toolchain must be passed with --autodetect-revision"
+ )
+
+ cros_llvm_version = find_chromeos_llvm_version(opts.chromiumos_overlay)
+ logging.info("Detected CrOS LLVM revision: r%d", cros_llvm_version)
+ android_llvm_version = find_android_llvm_version(opts.android_toolchain)
+ logging.info(
+ "Detected Android LLVM revision: r%d", android_llvm_version
+ )
+ r = min(cros_llvm_version, android_llvm_version)
+ logging.info("Selected minimum LLVM revision: r%d", r)
+ opts.revision = r
+
return opts
@@ -155,9 +251,9 @@ def main(argv: List[str]) -> None:
cros_overlay = opts.chromiumos_overlay
upload = opts.upload_with_autoreview
commit = opts.commit or upload
- revision = opts.revision
+ min_revision = opts.revision
- made_changes = remove_old_patches(cros_overlay, revision)
+ made_changes = remove_old_patches(cros_overlay, min_revision)
if not made_changes:
logging.info("No changes made; exiting.")
return
@@ -169,7 +265,7 @@ def main(argv: List[str]) -> None:
return
logging.info("Committing changes...")
- commit_changes(cros_overlay, revision)
+ commit_changes(cros_overlay, min_revision)
if not upload:
logging.info("Change with removed patches has been committed locally.")
return
diff --git a/llvm_tools/clean_up_old_llvm_patches_test.py b/llvm_tools/clean_up_old_llvm_patches_test.py
new file mode 100755
index 00000000..02100c8f
--- /dev/null
+++ b/llvm_tools/clean_up_old_llvm_patches_test.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+# Copyright 2024 The ChromiumOS Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Tests for clean_up_old_llvm_patches"""
+
+from pathlib import Path
+import shutil
+import tempfile
+import unittest
+
+import clean_up_old_llvm_patches
+
+
+ANDROID_VERSION_PY_EXAMPLE = """
+def get_svn_revision():
+ return "r654321"
+"""
+
+
+class Test(unittest.TestCase):
+ """Tests for clean_up_old_llvm_patches"""
+
+ def make_tempdir(self) -> Path:
+ tmpdir = Path(tempfile.mkdtemp(prefix="patch_utils_unittest"))
+ self.addCleanup(shutil.rmtree, tmpdir)
+ return tmpdir
+
+ def test_android_version_autodetection(self):
+ android_root = self.make_tempdir()
+ android_version_py = (
+ android_root / "toolchain" / "llvm_android" / "android_version.py"
+ )
+ android_version_py.parent.mkdir(parents=True)
+ android_version_py.write_text(
+ ANDROID_VERSION_PY_EXAMPLE, encoding="utf-8"
+ )
+
+ self.assertEqual(
+ clean_up_old_llvm_patches.find_android_llvm_version(android_root),
+ 654321,
+ )
+
+ def test_chromeos_version_autodetection(self):
+ chromiumos_overlay = self.make_tempdir()
+ llvm = chromiumos_overlay / "sys-devel" / "llvm"
+ llvm.mkdir(parents=True)
+
+ file_names = (
+ "Manifest",
+ "llvm-12.0-r1.ebuild",
+ "llvm-18.0_pre123456-r90.ebuild",
+ "llvm-9999.ebuild",
+ )
+ for f in file_names:
+ (llvm / f).touch()
+
+ self.assertEqual(
+ clean_up_old_llvm_patches.find_chromeos_llvm_version(
+ chromiumos_overlay
+ ),
+ 123456,
+ )
+
+
+if __name__ == "__main__":
+ unittest.main()