aboutsummaryrefslogtreecommitdiff
path: root/llvm_tools/get_upstream_patch.py
diff options
context:
space:
mode:
Diffstat (limited to 'llvm_tools/get_upstream_patch.py')
-rwxr-xr-xllvm_tools/get_upstream_patch.py929
1 files changed, 532 insertions, 397 deletions
diff --git a/llvm_tools/get_upstream_patch.py b/llvm_tools/get_upstream_patch.py
index 5669b023..72aa16b6 100755
--- a/llvm_tools/get_upstream_patch.py
+++ b/llvm_tools/get_upstream_patch.py
@@ -1,29 +1,31 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# 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
+from datetime import datetime
import json
import logging
import os
+from pathlib import Path
import shlex
import subprocess
import sys
import typing as t
-from datetime import datetime
-
-import dataclasses
import chroot
import get_llvm_hash
import git
import git_llvm_rev
+import patch_utils
import update_chromeos_llvm_hash
+
__DOC_EPILOGUE = """
Example Usage:
get_upstream_patch --chroot_path ~/chromiumos --platform chromiumos \
@@ -32,434 +34,567 @@ Example Usage:
class CherrypickError(ValueError):
- """A ValueError that highlights the cherry-pick has been seen before"""
-
-
-def add_patch(patches_json_path: str, patches_dir: str,
- relative_patches_dir: str, start_version: git_llvm_rev.Rev,
- llvm_dir: str, rev: t.Union[git_llvm_rev.Rev, str], sha: str,
- package: str, platforms: t.List[str]):
- """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.
-
- Raises:
- CherrypickError: A ValueError that highlights the cherry-pick has been
- seen before.
- """
-
- with open(patches_json_path, encoding='utf-8') as f:
- patches_json = json.load(f)
-
- 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)
-
- 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')
+ """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(f"Patch title: {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: str,
+ rev: t.Union[git_llvm_rev.Rev, str],
+ sha: str,
+ package: str,
+ platforms: t.List[str],
+):
+ """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.
+
+ 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:
- 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')
-
- end_vers = rev.number if isinstance(rev, git_llvm_rev.Rev) else None
- 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,
- },
- }
- 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=4,
- separators=(',', ': '),
- sort_keys=True)
- f.write('\n')
- os.rename(temp_file, patches_json_path)
+ 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:
+ patches_json = json.load(f)
+
+ 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,
+ },
+ }
+
+ 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=4, separators=(",", ": "), sort_keys=True
+ )
+ f.write("\n")
+ os.rename(temp_file, patches_json_path)
def parse_ebuild_for_assignment(ebuild_path: str, var_name: str) -> str:
- # '_pre' filters the LLVM 9.0 ebuild, which we never want to target, from
- # this list.
- candidates = [
- x for x in os.listdir(ebuild_path)
- if x.endswith('.ebuild') and '_pre' in x
- ]
-
- if not candidates:
- raise ValueError('No ebuilds found under %r' % ebuild_path)
-
- ebuild = os.path.join(ebuild_path, max(candidates))
- with open(ebuild, encoding='utf-8') as f:
- var_name_eq = var_name + '='
- for orig_line in f:
- if not orig_line.startswith(var_name_eq):
- continue
-
- # We shouldn't see much variety here, so do the simplest thing possible.
- line = orig_line[len(var_name_eq):]
- # Remove comments
- line = line.split('#')[0]
- # Remove quotes
- line = shlex.split(line)
- if len(line) != 1:
- raise ValueError('Expected exactly one quoted value in %r' % orig_line)
- return line[0].strip()
-
- raise ValueError('No %s= line found in %r' % (var_name, ebuild))
+ # '_pre' filters the LLVM 9.0 ebuild, which we never want to target, from
+ # this list.
+ candidates = [
+ x
+ for x in os.listdir(ebuild_path)
+ if x.endswith(".ebuild") and "_pre" in x
+ ]
+
+ if not candidates:
+ raise ValueError("No ebuilds found under %r" % ebuild_path)
+
+ ebuild = os.path.join(ebuild_path, max(candidates))
+ with open(ebuild, encoding="utf-8") as f:
+ var_name_eq = var_name + "="
+ for orig_line in f:
+ if not orig_line.startswith(var_name_eq):
+ continue
+
+ # We shouldn't see much variety here, so do the simplest thing possible.
+ line = orig_line[len(var_name_eq) :]
+ # Remove comments
+ line = line.split("#")[0]
+ # Remove quotes
+ line = shlex.split(line)
+ if len(line) != 1:
+ raise ValueError(
+ "Expected exactly one quoted value in %r" % orig_line
+ )
+ return line[0].strip()
+
+ raise ValueError("No %s= line found in %r" % (var_name, ebuild))
# Resolves a git ref (or similar) to a LLVM SHA.
def resolve_llvm_ref(llvm_dir: str, sha: str) -> str:
- return subprocess.check_output(
- ['git', 'rev-parse', sha],
- encoding='utf-8',
- cwd=llvm_dir,
- ).strip()
+ 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
+ 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
+ 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: 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)
- packages = list(sorted(packages))
- return 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: str, platforms: t.List[str]):
- """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)
-
-
-def make_cl(symlinks_to_uprev: t.List[str], llvm_symlink_dir: str, branch: str,
- commit_messages: t.List[str], reviewers: t.Optional[t.List[str]],
- cc: t.Optional[t.List[str]]):
- symlinks_to_uprev = sorted(set(symlinks_to_uprev))
- for symlink in symlinks_to_uprev:
- update_chromeos_llvm_hash.UprevEbuildSymlink(symlink)
- subprocess.check_output(['git', 'add', '--all'],
- cwd=os.path.dirname(symlink))
- git.UploadChanges(llvm_symlink_dir, branch, commit_messages, reviewers, cc)
- git.DeleteBranch(llvm_symlink_dir, branch)
+ 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)
+ packages = list(sorted(packages))
+ return 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: str,
+ platforms: t.List[str],
+):
+ """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,
+ )
+
+
+def make_cl(
+ symlinks_to_uprev: t.List[str],
+ llvm_symlink_dir: str,
+ branch: str,
+ commit_messages: t.List[str],
+ reviewers: t.Optional[t.List[str]],
+ cc: t.Optional[t.List[str]],
+):
+ symlinks_to_uprev = sorted(set(symlinks_to_uprev))
+ for symlink in symlinks_to_uprev:
+ update_chromeos_llvm_hash.UprevEbuildSymlink(symlink)
+ subprocess.check_output(
+ ["git", "add", "--all"], cwd=os.path.dirname(symlink)
+ )
+ git.UploadChanges(llvm_symlink_dir, branch, commit_messages, reviewers, cc)
+ git.DeleteBranch(llvm_symlink_dir, branch)
def resolve_symbolic_sha(start_sha: str, llvm_symlink_dir: str) -> str:
- if start_sha == 'llvm':
- return parse_ebuild_for_assignment(llvm_symlink_dir, 'LLVM_HASH')
+ if start_sha == "llvm":
+ return parse_ebuild_for_assignment(llvm_symlink_dir, "LLVM_HASH")
- if start_sha == 'llvm-next':
- return parse_ebuild_for_assignment(llvm_symlink_dir, 'LLVM_NEXT_HASH')
+ if start_sha == "llvm-next":
+ return parse_ebuild_for_assignment(llvm_symlink_dir, "LLVM_NEXT_HASH")
- return start_sha
+ return start_sha
def find_patches_and_make_cl(
- chroot_path: str, patches: t.List[str], start_rev: git_llvm_rev.Rev,
- llvm_config: git_llvm_rev.LLVMConfig, llvm_symlink_dir: str,
- create_cl: bool, skip_dependencies: bool,
- reviewers: t.Optional[t.List[str]], cc: t.Optional[t.List[str]],
- platforms: t.List[str]):
-
- 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`
- symlinks_to_uprev = []
- commit_messages = [
- 'llvm: get patches from upstream\n',
- ]
- branch = f'get-upstream-{datetime.now().strftime("%Y%m%d%H%M%S%f")}'
-
- if create_cl:
- git.CreateBranch(llvm_symlink_dir, branch)
-
- 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 symlinks of the corresponding ChromeOS packages
- symlinks = chroot.GetChrootEbuildPaths(chroot_path, [
- 'sys-devel/llvm' if package == 'llvm' else 'sys-libs/' + package
- for package in packages
- ])
- symlinks = chroot.ConvertChrootPathsToAbsolutePaths(chroot_path, symlinks)
- # Create a local patch for all the affected llvm projects
- create_patch_for_packages(packages,
- symlinks,
- start_rev,
- parsed_patch.rev,
- parsed_patch.sha,
- llvm_config.dir,
- platforms=platforms)
- if create_cl:
- symlinks_to_uprev.extend(symlinks)
+ chroot_path: str,
+ patches: t.List[str],
+ start_rev: git_llvm_rev.Rev,
+ llvm_config: git_llvm_rev.LLVMConfig,
+ llvm_symlink_dir: str,
+ create_cl: bool,
+ skip_dependencies: bool,
+ reviewers: t.Optional[t.List[str]],
+ cc: t.Optional[t.List[str]],
+ platforms: t.List[str],
+):
+
+ 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`
+ symlinks_to_uprev = []
+ commit_messages = [
+ "llvm: get patches from upstream\n",
+ ]
+ branch = f'get-upstream-{datetime.now().strftime("%Y%m%d%H%M%S%f")}'
- 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 create_cl:
+ git.CreateBranch(llvm_symlink_dir, branch)
+
+ 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 symlinks of the corresponding ChromeOS packages
+ symlinks = chroot.GetChrootEbuildPaths(
+ chroot_path,
+ [
+ "sys-devel/llvm" if package == "llvm" else "sys-libs/" + package
+ for package in packages
+ ],
+ )
+ symlinks = chroot.ConvertChrootPathsToAbsolutePaths(
+ chroot_path, symlinks
+ )
+ # Create a local patch for all the affected llvm projects
+ create_patch_for_packages(
+ packages,
+ symlinks,
+ start_rev,
+ parsed_patch.rev,
+ parsed_patch.sha,
+ llvm_config.dir,
+ platforms=platforms,
+ )
+ if create_cl:
+ symlinks_to_uprev.extend(symlinks)
+
+ 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 create_cl:
- make_cl(symlinks_to_uprev, llvm_symlink_dir, branch, commit_messages,
- reviewers, cc)
+ if create_cl:
+ make_cl(
+ symlinks_to_uprev,
+ 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:
- if self.is_differential:
- return f'\n\nreviews.llvm.org/{self.patch}\n'
- return f'\n\nreviews.llvm.org/rG{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,
+ """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:
+ if self.is_differential:
+ return f"\n\nreviews.llvm.org/{self.patch}\n"
+ return f"\n\nreviews.llvm.org/rG{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 = 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(
+ chroot_path: str,
+ create_cl: bool,
+ start_sha: str,
+ patches: t.List[str],
+ platforms: t.List[str],
+ skip_dependencies: bool = False,
+ reviewers: t.List[str] = None,
+ cc: t.List[str] = None,
+):
+ llvm_symlink = chroot.ConvertChrootPathsToAbsolutePaths(
+ chroot_path,
+ chroot.GetChrootEbuildPaths(chroot_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"
)
- sha = resolve_llvm_ref(llvm_config.dir, 'HEAD')
- rev = 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(chroot_path: str,
- create_cl: bool,
- start_sha: str,
- patches: t.List[str],
- platforms: t.List[str],
- skip_dependencies: bool = False,
- reviewers: t.List[str] = None,
- cc: t.List[str] = None):
- llvm_symlink = chroot.ConvertChrootPathsToAbsolutePaths(
- chroot_path, chroot.GetChrootEbuildPaths(chroot_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, llvm_symlink_dir)
- 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(chroot_path=chroot_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)
- logging.info('Complete.')
+
+ 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, llvm_symlink_dir)
+ 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(
+ chroot_path=chroot_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,
+ )
+ 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('--chroot_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')
- 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('--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()
-
- 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(
- chroot_path=args.chroot_path,
- create_cl=args.create_cl,
- start_sha=args.start_sha,
- patches=args.sha + args.differential,
- skip_dependencies=args.skip_dependencies,
- platforms=args.platform,
- )
-
-
-if __name__ == '__main__':
- sys.exit(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(
+ "--chroot_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(
+ "--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()
+
+ 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(
+ chroot_path=args.chroot_path,
+ create_cl=args.create_cl,
+ start_sha=args.start_sha,
+ patches=args.sha + args.differential,
+ skip_dependencies=args.skip_dependencies,
+ platforms=args.platform,
+ )
+
+
+if __name__ == "__main__":
+ sys.exit(main())