1#!/usr/bin/env python3 2import argparse 3import os 4import re 5import shlex 6import subprocess 7import sys 8import textwrap 9 10 11def get_git_ref_or_rev(dir: str) -> str: 12 # Run 'git symbolic-ref -q --short HEAD || git rev-parse --short HEAD' 13 cmd_ref = "git symbolic-ref -q --short HEAD" 14 ref = subprocess.run( 15 shlex.split(cmd_ref), cwd=dir, text=True, stdout=subprocess.PIPE 16 ) 17 if not ref.returncode: 18 return ref.stdout.strip() 19 cmd_rev = "git rev-parse --short HEAD" 20 return subprocess.check_output(shlex.split(cmd_rev), cwd=dir, text=True).strip() 21 22 23def main(): 24 parser = argparse.ArgumentParser( 25 description=textwrap.dedent( 26 """ 27 This script builds two versions of BOLT (with the current and 28 previous revision) and sets up symlink for llvm-bolt-wrapper. 29 Passes the options through to llvm-bolt-wrapper. 30 """ 31 ) 32 ) 33 parser.add_argument( 34 "build_dir", 35 nargs="?", 36 default=os.getcwd(), 37 help="Path to BOLT build directory, default is current " "directory", 38 ) 39 parser.add_argument( 40 "--switch-back", 41 default=False, 42 action="store_true", 43 help="Checkout back to the starting revision", 44 ) 45 parser.add_argument( 46 "--cmp-rev", 47 default="HEAD^", 48 help="Revision to checkout to compare vs HEAD", 49 ) 50 args, wrapper_args = parser.parse_known_args() 51 bolt_path = f"{args.build_dir}/bin/llvm-bolt" 52 53 source_dir = None 54 # find the repo directory 55 with open(f"{args.build_dir}/CMakeCache.txt") as f: 56 for line in f: 57 m = re.match(r"LLVM_SOURCE_DIR:STATIC=(.*)", line) 58 if m: 59 source_dir = m.groups()[0] 60 if not source_dir: 61 sys.exit("Source directory is not found") 62 63 script_dir = os.path.dirname(os.path.abspath(__file__)) 64 wrapper_path = f"{script_dir}/llvm-bolt-wrapper.py" 65 # build the current commit 66 subprocess.run( 67 shlex.split("cmake --build . --target llvm-bolt"), cwd=args.build_dir 68 ) 69 # rename llvm-bolt 70 os.replace(bolt_path, f"{bolt_path}.new") 71 # memorize the old hash for logging 72 old_ref = get_git_ref_or_rev(source_dir) 73 74 # determine whether a stash is needed 75 stash = subprocess.run( 76 shlex.split("git status --porcelain"), 77 cwd=source_dir, 78 stdout=subprocess.PIPE, 79 stderr=subprocess.STDOUT, 80 text=True, 81 ).stdout 82 if stash: 83 # save local changes before checkout 84 subprocess.run(shlex.split("git stash push -u"), cwd=source_dir) 85 # check out the previous/cmp commit 86 subprocess.run(shlex.split(f"git checkout -f {args.cmp_rev}"), cwd=source_dir) 87 # get the parent commit hash for logging 88 new_ref = get_git_ref_or_rev(source_dir) 89 # build the previous commit 90 subprocess.run( 91 shlex.split("cmake --build . --target llvm-bolt"), cwd=args.build_dir 92 ) 93 # rename llvm-bolt 94 os.replace(bolt_path, f"{bolt_path}.old") 95 # set up llvm-bolt-wrapper.ini 96 ini = subprocess.check_output( 97 shlex.split(f"{wrapper_path} {bolt_path}.old {bolt_path}.new") + wrapper_args, 98 text=True, 99 ) 100 with open(f"{args.build_dir}/bin/llvm-bolt-wrapper.ini", "w") as f: 101 f.write(ini) 102 # symlink llvm-bolt-wrapper 103 os.symlink(wrapper_path, bolt_path) 104 if args.switch_back: 105 if stash: 106 subprocess.run(shlex.split("git stash pop"), cwd=source_dir) 107 subprocess.run(shlex.split(f"git checkout {old_ref}"), cwd=source_dir) 108 else: 109 print( 110 f"The repository {source_dir} has been switched from {old_ref} " 111 f"to {new_ref}. Local changes were stashed. Switch back using\n\t" 112 f"git checkout {old_ref}\n" 113 ) 114 print( 115 f"Build directory {args.build_dir} is ready to run BOLT tests, e.g.\n" 116 "\tbin/llvm-lit -sv tools/bolt/test\nor\n" 117 "\tbin/llvm-lit -sv tools/bolttests" 118 ) 119 120 121if __name__ == "__main__": 122 main() 123