#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright 2020 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. # pylint: disable=cros-logging-import """Adds a cherrypick to LLVM's PATCHES.json.""" from __future__ import print_function import argparse import json import logging import os import shlex import subprocess import sys import chroot import get_llvm_hash import git_llvm_rev def add_cherrypick(patches_json_path: str, patches_dir: str, relative_patches_dir: str, start_version: git_llvm_rev.Rev, llvm_dir: str, rev: git_llvm_rev.Rev, sha: str): with open(patches_json_path, encoding='utf-8') as f: patches_json = json.load(f) file_name = sha + '.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 ValueError('Patch at %r already exists in PATCHES.json' % rel_path) 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: subprocess.check_call(['git', 'show', sha], stdout=f, cwd=llvm_dir) commit_subject = subprocess.check_output( ['git', 'log', '-n1', '--format=%s', sha], cwd=llvm_dir, encoding='utf-8') patches_json.append({ 'comment': commit_subject.strip(), 'rel_patch_path': rel_patch_path, 'start_version': start_version.number, 'end_version': rev.number, }) temp_file = patches_json_path + '.tmp' with open(temp_file, 'w', encoding='utf-8') as f: json.dump(patches_json, f, indent=4, separators=(',', ': ')) 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)) # 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() 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__) 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('--package', help='target package to apply the patch to.') 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', help='LLVM git SHA. Either this or --sha must be specified.') args = parser.parse_args() symlink = chroot.GetChrootEbuildPaths(args.chroot_path, [args.package])[0] symlink = chroot.ConvertChrootPathsToAbsolutePaths(args.chroot_path, [symlink])[0] symlink_dir = os.path.dirname(symlink) patches_json_path = os.path.join(symlink_dir, 'files/PATCHES.json') relative_patches_dir = '' if 'llvm' in args.package: relative_patches_dir = 'cherry' patches_dir = os.path.join(symlink_dir, 'files', relative_patches_dir) llvm_config = git_llvm_rev.LLVMConfig( remote='origin', dir=get_llvm_hash.GetAndUpdateLLVMProjectInLLVMTools()) start_sha = args.start_sha if start_sha == 'llvm': start_sha = parse_ebuild_for_assignment(symlink_dir, 'LLVM_HASH') logging.info('Autodetected llvm hash == %s', start_sha) elif start_sha == 'llvm-next': start_sha = parse_ebuild_for_assignment(symlink_dir, 'LLVM_NEXT_HASH') logging.info('Autodetected llvm-next hash == %s', start_sha) start_sha = resolve_llvm_ref(llvm_config.dir, start_sha) start_rev = git_llvm_rev.translate_sha_to_rev(llvm_config, start_sha) sha = resolve_llvm_ref(llvm_config.dir, args.sha) rev = git_llvm_rev.translate_sha_to_rev(llvm_config, sha) logging.info('Will cherrypick %s (%s), with start == %s', rev, sha, start_sha) add_cherrypick(patches_json_path, patches_dir, relative_patches_dir, start_rev, llvm_config.dir, rev, sha) logging.info('Complete.') if __name__ == '__main__': sys.exit(main())