aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan R Abrahams-Whitehead <ajordanr@google.com>2024-05-06 23:27:43 +0000
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2024-05-07 17:31:08 +0000
commit33c01563aa3effa4294ceea4a12ff134cb1e1470 (patch)
tree3937c6f042a59f7f4d0de6295cb8a93404cfe66d
parent9490b3ba035c335def6a0b136b47e34e25efdd1a (diff)
downloadtoolchain-utils-33c01563aa3effa4294ceea4a12ff134cb1e1470.tar.gz
llvm_tools: Fully remove get_upstream_patch.py
This is now fully obsolete with the existence of get_patch.py and has no dependendents. It is fully safe to remove. BUG=b:339010069 TEST=repo upload; ./run_python_tests.py Change-Id: Iab379399cb11399098f2f9271840aabd90c89120 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/5515906 Tested-by: Jordan Abrahams-Whitehead <ajordanr@google.com> Reviewed-by: George Burgess <gbiv@chromium.org> Commit-Queue: Jordan Abrahams-Whitehead <ajordanr@google.com>
-rw-r--r--llvm_tools/get_upstream_patch.py598
l---------py/bin/llvm_tools/get_upstream_patch.py1
2 files changed, 0 insertions, 599 deletions
diff --git a/llvm_tools/get_upstream_patch.py b/llvm_tools/get_upstream_patch.py
deleted file mode 100644
index 85dacaa4..00000000
--- a/llvm_tools/get_upstream_patch.py
+++ /dev/null
@@ -1,598 +0,0 @@
-# Copyright 2020 The ChromiumOS Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Get an upstream patch to LLVM's PATCHES.json."""
-
-import argparse
-import dataclasses
-import datetime
-import json
-import logging
-import os
-from pathlib import Path
-import subprocess
-import typing as t
-
-from llvm_tools import chroot
-from llvm_tools import get_llvm_hash
-from llvm_tools import git
-from llvm_tools import git_llvm_rev
-from llvm_tools import patch_utils
-
-
-__DOC_EPILOGUE = """
-Example Usage:
- get_upstream_patch --chromeos_path ~/chromiumos --platform chromiumos \
---sha 1234567 --sha 890abdc
-"""
-
-
-class CherrypickError(ValueError):
- """A ValueError that highlights the cherry-pick has been seen before"""
-
-
-class CherrypickVersionError(ValueError):
- """A ValueError that highlights the cherry-pick is before the start_sha"""
-
-
-class PatchApplicationError(ValueError):
- """A ValueError indicating that a test patch application was unsuccessful"""
-
-
-def validate_patch_application(
- llvm_dir: Path, svn_version: int, patches_json_fp: Path, patch_props
-):
- start_sha = get_llvm_hash.GetGitHashFrom(llvm_dir, svn_version)
- subprocess.run(["git", "-C", llvm_dir, "checkout", start_sha], check=True)
-
- predecessor_apply_results = patch_utils.apply_all_from_json(
- svn_version, llvm_dir, patches_json_fp, continue_on_failure=True
- )
-
- if predecessor_apply_results.failed_patches:
- logging.error("Failed to apply patches from PATCHES.json:")
- for p in predecessor_apply_results.failed_patches:
- logging.error("Patch title: %s", p.title())
- raise PatchApplicationError("Failed to apply patch from PATCHES.json")
-
- patch_entry = patch_utils.PatchEntry.from_dict(
- patches_json_fp.parent, patch_props
- )
- test_apply_result = patch_entry.test_apply(Path(llvm_dir))
-
- if not test_apply_result:
- logging.error("Could not apply requested patch")
- logging.error(test_apply_result.failure_info())
- raise PatchApplicationError(
- f'Failed to apply patch: {patch_props["metadata"]["title"]}'
- )
-
-
-def add_patch(
- patches_json_path: str,
- patches_dir: str,
- relative_patches_dir: str,
- start_version: git_llvm_rev.Rev,
- llvm_dir: t.Union[Path, str],
- rev: t.Union[git_llvm_rev.Rev, str],
- sha: str,
- package: str,
- platforms: t.Iterable[str],
- skip_application_test: bool,
-):
- """Gets the start and end intervals in 'json_file'.
-
- Args:
- patches_json_path: The absolute path to PATCHES.json.
- patches_dir: The aboslute path to the directory patches are in.
- relative_patches_dir: The relative path to PATCHES.json.
- start_version: The base LLVM revision this patch applies to.
- llvm_dir: The path to LLVM checkout.
- rev: An LLVM revision (git_llvm_rev.Rev) for a cherrypicking, or a
- differential revision (str) otherwise.
- sha: The LLVM git sha that corresponds to the patch. For differential
- revisions, the git sha from the local commit created by 'arc patch'
- is used.
- package: The LLVM project name this patch applies to.
- platforms: List of platforms this patch applies to.
- skip_application_test: If True, no attempt will be made to apply the
- patch. It's expected that a later step (e.g., the CQ) will try to apply
- it.
-
- Raises:
- CherrypickError: A ValueError that highlights the cherry-pick has been
- seen before.
- CherrypickRangeError: A ValueError that's raised when the given patch
- is from before the start_sha.
- """
-
- is_cherrypick = isinstance(rev, git_llvm_rev.Rev)
- if is_cherrypick:
- file_name = f"{sha}.patch"
- else:
- file_name = f"{rev}.patch"
- rel_patch_path = os.path.join(relative_patches_dir, file_name)
-
- # Check that we haven't grabbed a patch range that's nonsensical.
- end_vers = rev.number if isinstance(rev, git_llvm_rev.Rev) else None
- if end_vers is not None and end_vers <= start_version.number:
- raise CherrypickVersionError(
- f"`until` version {end_vers} is earlier or equal to"
- f" `from` version {start_version.number} for patch"
- f" {rel_patch_path}"
- )
-
- with open(patches_json_path, encoding="utf-8") as f:
- contents = f.read()
- indent_len = patch_utils.predict_indent(contents.splitlines())
- patches_json = json.loads(contents)
-
- for p in patches_json:
- rel_path = p["rel_patch_path"]
- if rel_path == rel_patch_path:
- raise CherrypickError(
- f"Patch at {rel_path} already exists in PATCHES.json"
- )
- if is_cherrypick:
- if sha in rel_path:
- logging.warning(
- "Similarly-named patch already exists in PATCHES.json: %r",
- rel_path,
- )
-
- with open(os.path.join(patches_dir, file_name), "wb") as f:
- cmd = ["git", "show", sha]
- # Only apply the part of the patch that belongs to this package, expect
- # LLVM. This is because some packages are built with LLVM ebuild on X86
- # but not on the other architectures. e.g. compiler-rt. Therefore
- # always apply the entire patch to LLVM ebuild as a workaround.
- if package != "llvm":
- cmd.append(package_to_project(package))
- subprocess.check_call(cmd, stdout=f, cwd=llvm_dir)
-
- commit_subject = subprocess.check_output(
- ["git", "log", "-n1", "--format=%s", sha],
- cwd=llvm_dir,
- encoding="utf-8",
- )
- patch_props = {
- "rel_patch_path": rel_patch_path,
- "metadata": {
- "title": commit_subject.strip(),
- "info": [],
- },
- "platforms": sorted(platforms),
- "version_range": {
- "from": start_version.number,
- "until": end_vers,
- },
- }
-
- if not skip_application_test:
- with patch_utils.git_clean_context(Path(llvm_dir)):
- validate_patch_application(
- Path(llvm_dir),
- start_version.number,
- Path(patches_json_path),
- patch_props,
- )
-
- patches_json.append(patch_props)
-
- temp_file = patches_json_path + ".tmp"
- with open(temp_file, "w", encoding="utf-8") as f:
- json.dump(
- patches_json,
- f,
- indent=indent_len,
- separators=(",", ": "),
- sort_keys=True,
- )
- f.write("\n")
- os.rename(temp_file, patches_json_path)
-
-
-# Resolves a git ref (or similar) to a LLVM SHA.
-def resolve_llvm_ref(llvm_dir: t.Union[Path, str], sha: str) -> str:
- return subprocess.check_output(
- ["git", "rev-parse", sha],
- encoding="utf-8",
- cwd=llvm_dir,
- ).strip()
-
-
-# Get the package name of an LLVM project
-def project_to_package(project: str) -> str:
- if project == "libunwind":
- return "llvm-libunwind"
- return project
-
-
-# Get the LLVM project name of a package
-def package_to_project(package: str) -> str:
- if package == "llvm-libunwind":
- return "libunwind"
- return package
-
-
-# Get the LLVM projects change in the specifed sha
-def get_package_names(sha: str, llvm_dir: t.Union[Path, str]) -> list:
- paths = subprocess.check_output(
- ["git", "show", "--name-only", "--format=", sha],
- cwd=llvm_dir,
- encoding="utf-8",
- ).splitlines()
- # Some LLVM projects are built by LLVM ebuild on X86, so always apply the
- # patch to LLVM ebuild
- packages = {"llvm"}
- # Detect if there are more packages to apply the patch to
- for path in paths:
- package = project_to_package(path.split("/")[0])
- if package in ("compiler-rt", "libcxx", "libcxxabi", "llvm-libunwind"):
- packages.add(package)
- return list(sorted(packages))
-
-
-def create_patch_for_packages(
- packages: t.List[str],
- symlinks: t.List[str],
- start_rev: git_llvm_rev.Rev,
- rev: t.Union[git_llvm_rev.Rev, str],
- sha: str,
- llvm_dir: t.Union[Path, str],
- platforms: t.Iterable[str],
- skip_application_test: bool,
-):
- """Create a patch and add its metadata for each package"""
- for package, symlink in zip(packages, symlinks):
- symlink_dir = os.path.dirname(symlink)
- patches_json_path = os.path.join(symlink_dir, "files/PATCHES.json")
- relative_patches_dir = "cherry" if package == "llvm" else ""
- patches_dir = os.path.join(symlink_dir, "files", relative_patches_dir)
- logging.info("Getting %s (%s) into %s", rev, sha, package)
- add_patch(
- patches_json_path,
- patches_dir,
- relative_patches_dir,
- start_rev,
- llvm_dir,
- rev,
- sha,
- package,
- platforms=platforms,
- skip_application_test=skip_application_test,
- )
-
-
-def make_cl(
- llvm_symlink_dir: str,
- branch: str,
- commit_messages: t.List[str],
- reviewers: t.Optional[t.List[str]],
- cc: t.Optional[t.List[str]],
-):
- subprocess.check_output(["git", "add", "--all"], cwd=llvm_symlink_dir)
- git.CommitChanges(llvm_symlink_dir, commit_messages)
- git.UploadChanges(llvm_symlink_dir, branch, reviewers, cc)
- git.DeleteBranch(llvm_symlink_dir, branch)
-
-
-def resolve_symbolic_sha(start_sha: str, chromeos_path: Path) -> str:
- if start_sha == "llvm":
- return get_llvm_hash.LLVMHash().GetCrOSCurrentLLVMHash(chromeos_path)
-
- if start_sha == "llvm-next":
- return get_llvm_hash.LLVMHash().GetCrOSLLVMNextHash()
-
- return start_sha
-
-
-def find_patches_and_make_cl(
- chromeos_path: str,
- patches: t.List[str],
- start_rev: git_llvm_rev.Rev,
- llvm_config: git_llvm_rev.LLVMConfig,
- llvm_symlink_dir: str,
- allow_failures: bool,
- create_cl: bool,
- skip_dependencies: bool,
- reviewers: t.Optional[t.List[str]],
- cc: t.Optional[t.List[str]],
- platforms: t.Iterable[str],
- skip_application_test: bool,
-):
- converted_patches = [
- _convert_patch(llvm_config, skip_dependencies, p) for p in patches
- ]
- potential_duplicates = _get_duplicate_shas(converted_patches)
- if potential_duplicates:
- err_msg = "\n".join(
- f"{a.patch} == {b.patch}" for a, b in potential_duplicates
- )
- raise RuntimeError(f"Found Duplicate SHAs:\n{err_msg}")
-
- # CL Related variables, only used if `create_cl`
- commit_messages = [
- "llvm: get patches from upstream\n",
- ]
- branch = (
- f'get-upstream-{datetime.datetime.now().strftime("%Y%m%d%H%M%S%f")}'
- )
-
- if create_cl:
- git.CreateBranch(llvm_symlink_dir, branch)
-
- successes = []
- failures = []
- for parsed_patch in converted_patches:
- # Find out the llvm projects changed in this commit
- packages = get_package_names(parsed_patch.sha, llvm_config.dir)
- # Find out the ebuild of the corresponding ChromeOS packages
- ebuild_paths = chroot.GetChrootEbuildPaths(
- chromeos_path,
- [
- "sys-devel/llvm" if package == "llvm" else "sys-libs/" + package
- for package in packages
- ],
- )
- ebuild_paths = chroot.ConvertChrootPathsToAbsolutePaths(
- chromeos_path, ebuild_paths
- )
- # Create a local patch for all the affected llvm projects
- try:
- create_patch_for_packages(
- packages,
- ebuild_paths,
- start_rev,
- parsed_patch.rev,
- parsed_patch.sha,
- llvm_config.dir,
- platforms=platforms,
- skip_application_test=skip_application_test,
- )
- except PatchApplicationError as e:
- if allow_failures:
- logging.warning(e)
- failures.append(parsed_patch.sha)
- continue
- else:
- raise e
- successes.append(parsed_patch.sha)
-
- if create_cl:
- commit_messages.extend(
- [
- parsed_patch.git_msg(),
- subprocess.check_output(
- ["git", "log", "-n1", "--oneline", parsed_patch.sha],
- cwd=llvm_config.dir,
- encoding="utf-8",
- ),
- ]
- )
-
- if parsed_patch.is_differential:
- subprocess.check_output(
- ["git", "reset", "--hard", "HEAD^"], cwd=llvm_config.dir
- )
-
- if allow_failures:
- success_list = (":\n\t" + "\n\t".join(successes)) if successes else "."
- logging.info(
- "Successfully applied %d patches%s", len(successes), success_list
- )
- failure_list = (":\n\t" + "\n\t".join(failures)) if failures else "."
- logging.info(
- "Failed to apply %d patches%s", len(failures), failure_list
- )
-
- if successes and create_cl:
- make_cl(
- llvm_symlink_dir,
- branch,
- commit_messages,
- reviewers,
- cc,
- )
-
-
-@dataclasses.dataclass(frozen=True)
-class ParsedPatch:
- """Class to keep track of bundled patch info."""
-
- patch: str
- sha: str
- is_differential: bool
- rev: t.Union[git_llvm_rev.Rev, str]
-
- def git_msg(self) -> str:
- # TODO(b/339010069): Clean `differential` up.
- assert not self.is_differential, "differential CLs aren't supported."
- return f"\n\nhttps://github.com/llvm/llvm-project/commit/{self.sha}\n"
-
-
-def _convert_patch(
- llvm_config: git_llvm_rev.LLVMConfig, skip_dependencies: bool, patch: str
-) -> ParsedPatch:
- """Extract git revision info from a patch.
-
- Args:
- llvm_config: LLVM configuration object.
- skip_dependencies: Pass --skip-dependecies for to `arc`
- patch: A single patch referent string.
-
- Returns:
- A [ParsedPatch] object.
- """
-
- # git hash should only have lower-case letters
- is_differential = patch.startswith("D")
- if is_differential:
- subprocess.check_output(
- [
- "arc",
- "patch",
- "--nobranch",
- "--skip-dependencies" if skip_dependencies else "--revision",
- patch,
- ],
- cwd=llvm_config.dir,
- )
- sha = resolve_llvm_ref(llvm_config.dir, "HEAD")
- rev: t.Union[git_llvm_rev.Rev, str] = patch
- else:
- sha = resolve_llvm_ref(llvm_config.dir, patch)
- rev = git_llvm_rev.translate_sha_to_rev(llvm_config, sha)
- return ParsedPatch(
- patch=patch, sha=sha, rev=rev, is_differential=is_differential
- )
-
-
-def _get_duplicate_shas(
- patches: t.List[ParsedPatch],
-) -> t.List[t.Tuple[ParsedPatch, ParsedPatch]]:
- """Return a list of Patches which have duplicate SHA's"""
- return [
- (left, right)
- for i, left in enumerate(patches)
- for right in patches[i + 1 :]
- if left.sha == right.sha
- ]
-
-
-def get_from_upstream(
- chromeos_path: str,
- create_cl: bool,
- start_sha: str,
- patches: t.List[str],
- platforms: t.Iterable[str],
- allow_failures: bool = False,
- skip_dependencies: bool = False,
- reviewers: t.Optional[t.List[str]] = None,
- cc: t.Optional[t.List[str]] = None,
- skip_application_test: bool = False,
-):
- llvm_symlink = chroot.ConvertChrootPathsToAbsolutePaths(
- chromeos_path,
- chroot.GetChrootEbuildPaths(chromeos_path, ["sys-devel/llvm"]),
- )[0]
- llvm_symlink_dir = os.path.dirname(llvm_symlink)
-
- git_status = subprocess.check_output(
- ["git", "status", "-s"], cwd=llvm_symlink_dir, encoding="utf-8"
- )
-
- if git_status:
- error_path = os.path.dirname(os.path.dirname(llvm_symlink_dir))
- raise ValueError(f"Uncommited changes detected in {error_path}")
-
- start_sha = resolve_symbolic_sha(start_sha, Path(chromeos_path))
- logging.info("Base llvm hash == %s", start_sha)
-
- llvm_config = git_llvm_rev.LLVMConfig(
- remote="origin", dir=get_llvm_hash.GetAndUpdateLLVMProjectInLLVMTools()
- )
- start_sha = resolve_llvm_ref(llvm_config.dir, start_sha)
-
- find_patches_and_make_cl(
- chromeos_path=chromeos_path,
- patches=patches,
- platforms=platforms,
- start_rev=git_llvm_rev.translate_sha_to_rev(llvm_config, start_sha),
- llvm_config=llvm_config,
- llvm_symlink_dir=llvm_symlink_dir,
- create_cl=create_cl,
- skip_dependencies=skip_dependencies,
- reviewers=reviewers,
- cc=cc,
- allow_failures=allow_failures,
- skip_application_test=skip_application_test,
- )
-
- logging.info("Complete.")
-
-
-def main():
- chroot.VerifyOutsideChroot()
- logging.basicConfig(
- format="%(asctime)s: %(levelname)s: %(filename)s:%(lineno)d: "
- "%(message)s",
- level=logging.INFO,
- )
-
- parser = argparse.ArgumentParser(
- description=__doc__,
- formatter_class=argparse.RawDescriptionHelpFormatter,
- epilog=__DOC_EPILOGUE,
- )
- parser.add_argument(
- "--chromeos_path",
- default=os.path.join(os.path.expanduser("~"), "chromiumos"),
- help="the path to the chroot (default: %(default)s)",
- )
- parser.add_argument(
- "--start_sha",
- default="llvm-next",
- help="LLVM SHA that the patch should start applying at. You can "
- 'specify "llvm" or "llvm-next", as well. Defaults to %(default)s.',
- )
- parser.add_argument(
- "--sha",
- action="append",
- default=[],
- help="The LLVM git SHA to cherry-pick.",
- )
- parser.add_argument(
- "--differential",
- action="append",
- default=[],
- help="The LLVM differential revision to apply. Example: D1234."
- " Cannot be used for changes already merged upstream; use --sha"
- " instead for those.",
- )
- parser.add_argument(
- "--platform",
- action="append",
- required=True,
- help="Apply this patch to the give platform. Common options include "
- '"chromiumos" and "android". Can be specified multiple times to '
- "apply to multiple platforms",
- )
- parser.add_argument(
- "--allow_failures",
- action="store_true",
- help="Skip patches that fail to apply and continue.",
- )
- parser.add_argument(
- "--create_cl",
- action="store_true",
- help="Automatically create a CL if specified",
- )
- parser.add_argument(
- "--skip_dependencies",
- action="store_true",
- help="Skips a LLVM differential revision's dependencies. Only valid "
- "when --differential appears exactly once.",
- )
- args = parser.parse_args()
- chroot.VerifyChromeOSRoot(args.chromeos_path)
-
- if not (args.sha or args.differential):
- parser.error("--sha or --differential required")
-
- if args.skip_dependencies and len(args.differential) != 1:
- parser.error(
- "--skip_dependencies is only valid when there's exactly one "
- "supplied differential"
- )
-
- get_from_upstream(
- chromeos_path=args.chromeos_path,
- allow_failures=args.allow_failures,
- create_cl=args.create_cl,
- start_sha=args.start_sha,
- patches=args.sha + args.differential,
- skip_dependencies=args.skip_dependencies,
- platforms=args.platform,
- )
diff --git a/py/bin/llvm_tools/get_upstream_patch.py b/py/bin/llvm_tools/get_upstream_patch.py
deleted file mode 120000
index 0f1ca492..00000000
--- a/py/bin/llvm_tools/get_upstream_patch.py
+++ /dev/null
@@ -1 +0,0 @@
-../../../python_wrapper.py \ No newline at end of file