aboutsummaryrefslogtreecommitdiff
path: root/llvm_tools/generate_llvm_revert_report.py
diff options
context:
space:
mode:
Diffstat (limited to 'llvm_tools/generate_llvm_revert_report.py')
-rwxr-xr-xllvm_tools/generate_llvm_revert_report.py159
1 files changed, 159 insertions, 0 deletions
diff --git a/llvm_tools/generate_llvm_revert_report.py b/llvm_tools/generate_llvm_revert_report.py
new file mode 100755
index 00000000..f6a11128
--- /dev/null
+++ b/llvm_tools/generate_llvm_revert_report.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python3
+# Copyright 2023 The ChromiumOS Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Reports on all reverts applied and not applied to sys-devel/llvm.
+
+Note that this is primarily intended to produce output that can be easily
+pasted into a spreadsheet (read: the ChromeOS Mage's test matrix), so output is
+in CSV format.
+"""
+
+import argparse
+import csv
+import dataclasses
+import json
+import logging
+from pathlib import Path
+import re
+import subprocess
+import sys
+from typing import List, Set, TextIO
+
+import get_upstream_patch
+import revert_checker
+
+
+@dataclasses.dataclass(frozen=True)
+class RevertInfo:
+ """Information to write about a revert."""
+
+ revert: revert_checker.Revert
+ has_in_patches: bool
+ subject: str
+
+
+def list_upstream_cherrypicks(patches_json: Path) -> Set[str]:
+ with patches_json.open(encoding="utf-8") as f:
+ applicable_patches = [
+ x
+ for x in json.load(f)
+ if not x.get("platforms") or "chromiumos" in x["platforms"]
+ ]
+
+ # Allow for arbitrary suffixes for patches; some have `-v2`, `_fixed`, etc.
+ sha_re = re.compile(r"cherry/([a-fA-F0-9]{40})\b.*\.patch$")
+ sha_like_patches = set()
+ for p in applicable_patches:
+ m = sha_re.match(p["rel_patch_path"])
+ if m:
+ sha_like_patches.add(m.group(1))
+
+ return sha_like_patches
+
+
+def fetch_commit_subject(llvm_git_dir: Path, sha: str) -> str:
+ result = subprocess.run(
+ ["git", "log", "--format=%s", "-n1", sha],
+ check=True,
+ cwd=llvm_git_dir,
+ encoding="utf-8",
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.PIPE,
+ # Don't set stderr, since that should only be written to on error (and
+ # `check=True`).
+ )
+ return result.stdout.strip()
+
+
+def write_reverts_as_csv(write_to: TextIO, reverts: List[RevertInfo]):
+ writer = csv.writer(write_to, quoting=csv.QUOTE_ALL)
+ # Write the header.
+ writer.writerow(("SHA", "Reverted SHA", "Has Revert", "Subject"))
+ writer.writerows(
+ (x.revert.sha, x.revert.reverted_sha, x.has_in_patches, x.subject)
+ for x in reverts
+ )
+
+
+def main(argv: List[str]):
+ logging.basicConfig(
+ format=">> %(asctime)s: %(levelname)s: %(filename)s:%(lineno)d: "
+ "%(message)s",
+ level=logging.INFO,
+ )
+
+ my_dir = Path(__name__).resolve().parent
+
+ parser = argparse.ArgumentParser(
+ description=__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ )
+ parser.add_argument(
+ "-C",
+ "--git-dir",
+ default=my_dir.parent.parent / "llvm-project",
+ help="LLVM git directory to use.",
+ # Note that this is left as `type=str` because that's what
+ # `revert_checker` expects.
+ type=str,
+ )
+ parser.add_argument(
+ "--llvm-next", action="store_true", help="Use the llvm-next hash"
+ )
+ parser.add_argument(
+ "--llvm-dir",
+ help="Directory containing LLVM ebuilds",
+ type=Path,
+ default=my_dir.parent.parent / "chromiumos-overlay/sys-devel/llvm",
+ )
+ parser.add_argument(
+ "--llvm-head",
+ default="cros/upstream/main",
+ help="ref to treat as 'origin/main' in the given LLVM dir.",
+ )
+ opts = parser.parse_args(argv)
+
+ symbolic_sha = "llvm-next" if opts.llvm_next else "llvm"
+ llvm_sha = get_upstream_patch.resolve_symbolic_sha(
+ symbolic_sha,
+ opts.llvm_dir,
+ )
+ logging.info("Resolved %r as the LLVM SHA to check.", llvm_sha)
+
+ in_tree_cherrypicks = list_upstream_cherrypicks(
+ opts.llvm_dir / "files/PATCHES.json"
+ )
+ logging.info("Identified %d local cherrypicks.", len(in_tree_cherrypicks))
+
+ raw_reverts = revert_checker.find_reverts(
+ opts.git_dir,
+ llvm_sha,
+ opts.llvm_head,
+ )
+
+ llvm_dir = Path(opts.git_dir)
+ # Sort by `has_in_patches`, since that ordering is easier to visually scan.
+ # Note that `sorted` is stable, so any ordering in `find_reverts` will be
+ # preserved secondary to the `has_in_patches` ordering. Reverts not in
+ # PATCHES.json will appear earlier than those that are.
+ reverts = sorted(
+ (
+ RevertInfo(
+ revert=revert,
+ subject=fetch_commit_subject(llvm_dir, revert.sha),
+ has_in_patches=revert.sha in in_tree_cherrypicks,
+ )
+ for revert in raw_reverts
+ ),
+ key=lambda x: x.has_in_patches,
+ )
+
+ print()
+ print("CSV summary of reverts:")
+ write_reverts_as_csv(sys.stdout, reverts)
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])