1*760c253cSXin Li#!/usr/bin/env python3 2*760c253cSXin Li# Copyright 2019 The ChromiumOS Authors 3*760c253cSXin Li# Use of this source code is governed by a BSD-style license that can be 4*760c253cSXin Li# found in the LICENSE file. 5*760c253cSXin Li 6*760c253cSXin Li"""Maps LLVM git SHAs to synthetic revision numbers and back. 7*760c253cSXin Li 8*760c253cSXin LiRevision numbers are all of the form '(branch_name, r1234)'. As a shorthand, 9*760c253cSXin Lir1234 is parsed as '(main, 1234)'. 10*760c253cSXin Li""" 11*760c253cSXin Li 12*760c253cSXin Liimport argparse 13*760c253cSXin Lifrom pathlib import Path 14*760c253cSXin Liimport re 15*760c253cSXin Liimport subprocess 16*760c253cSXin Liimport sys 17*760c253cSXin Lifrom typing import IO, Iterable, List, NamedTuple, Optional, Tuple, Union 18*760c253cSXin Li 19*760c253cSXin Li 20*760c253cSXin LiMAIN_BRANCH = "main" 21*760c253cSXin Li 22*760c253cSXin Li# Note that after base_llvm_sha, we reach The Wild West(TM) of commits. 23*760c253cSXin Li# So reasonable input that could break us includes: 24*760c253cSXin Li# 25*760c253cSXin Li# Revert foo 26*760c253cSXin Li# 27*760c253cSXin Li# This reverts foo, which had the commit message: 28*760c253cSXin Li# 29*760c253cSXin Li# bar 30*760c253cSXin Li# llvm-svn: 375505 31*760c253cSXin Li# 32*760c253cSXin Li# While saddening, this is something we should probably try to handle 33*760c253cSXin Li# reasonably. 34*760c253cSXin Libase_llvm_revision = 375505 35*760c253cSXin Libase_llvm_sha = "186155b89c2d2a2f62337081e3ca15f676c9434b" 36*760c253cSXin Li 37*760c253cSXin Li# Known pairs of [revision, SHA] in ascending order. 38*760c253cSXin Li# The first element is the first non-`llvm-svn` commit that exists. Later ones 39*760c253cSXin Li# are functional nops, but speed this script up immensely, since `git` can take 40*760c253cSXin Li# quite a while to walk >100K commits. 41*760c253cSXin Liknown_llvm_rev_sha_pairs = ( 42*760c253cSXin Li (base_llvm_revision, base_llvm_sha), 43*760c253cSXin Li (425000, "af870e11aed7a5c475ae41a72e3015c4c88597d1"), 44*760c253cSXin Li (450000, "906ebd5830e6053b50c52bf098e3586b567e8499"), 45*760c253cSXin Li (475000, "530d14a99611a71f8f3eb811920fd7b5c4d4e1f8"), 46*760c253cSXin Li (500000, "173855f9b0bdfe45d71272596b510650bfc1ca33"), 47*760c253cSXin Li) 48*760c253cSXin Li 49*760c253cSXin Li# Represents an LLVM git checkout: 50*760c253cSXin Li# - |dir| is the directory of the LLVM checkout 51*760c253cSXin Li# - |remote| is the name of the LLVM remote. Generally it's "origin". 52*760c253cSXin LiLLVMConfig = NamedTuple( 53*760c253cSXin Li "LLVMConfig", (("remote", str), ("dir", Union[Path, str])) 54*760c253cSXin Li) 55*760c253cSXin Li 56*760c253cSXin Li 57*760c253cSXin Liclass Rev(NamedTuple("Rev", (("branch", str), ("number", int)))): 58*760c253cSXin Li """Represents a LLVM 'revision', a shorthand identifies a LLVM commit.""" 59*760c253cSXin Li 60*760c253cSXin Li @staticmethod 61*760c253cSXin Li def parse(rev: str) -> "Rev": 62*760c253cSXin Li """Parses a Rev from the given string. 63*760c253cSXin Li 64*760c253cSXin Li Raises a ValueError on a failed parse. 65*760c253cSXin Li """ 66*760c253cSXin Li # Revs are parsed into (${branch_name}, r${commits_since_base_commit}) 67*760c253cSXin Li # pairs. 68*760c253cSXin Li # 69*760c253cSXin Li # We support r${commits_since_base_commit} as shorthand for 70*760c253cSXin Li # (main, r${commits_since_base_commit}). 71*760c253cSXin Li if rev.startswith("r"): 72*760c253cSXin Li branch_name = MAIN_BRANCH 73*760c253cSXin Li rev_string = rev[1:] 74*760c253cSXin Li else: 75*760c253cSXin Li match = re.match(r"\((.+), r(\d+)\)", rev) 76*760c253cSXin Li if not match: 77*760c253cSXin Li raise ValueError("%r isn't a valid revision" % rev) 78*760c253cSXin Li 79*760c253cSXin Li branch_name, rev_string = match.groups() 80*760c253cSXin Li 81*760c253cSXin Li return Rev(branch=branch_name, number=int(rev_string)) 82*760c253cSXin Li 83*760c253cSXin Li def __str__(self) -> str: 84*760c253cSXin Li branch_name, number = self 85*760c253cSXin Li if branch_name == MAIN_BRANCH: 86*760c253cSXin Li return "r%d" % number 87*760c253cSXin Li return "(%s, r%d)" % (branch_name, number) 88*760c253cSXin Li 89*760c253cSXin Li 90*760c253cSXin Lidef is_git_sha(xs: str) -> bool: 91*760c253cSXin Li """Returns whether the given string looks like a valid git commit SHA.""" 92*760c253cSXin Li return ( 93*760c253cSXin Li len(xs) > 6 94*760c253cSXin Li and len(xs) <= 40 95*760c253cSXin Li and all(x.isdigit() or "a" <= x.lower() <= "f" for x in xs) 96*760c253cSXin Li ) 97*760c253cSXin Li 98*760c253cSXin Li 99*760c253cSXin Lidef check_output(command: List[str], cwd: Union[Path, str]) -> str: 100*760c253cSXin Li """Shorthand for subprocess.check_output. Auto-decodes any stdout.""" 101*760c253cSXin Li result = subprocess.run( 102*760c253cSXin Li command, 103*760c253cSXin Li cwd=cwd, 104*760c253cSXin Li check=True, 105*760c253cSXin Li stdin=subprocess.DEVNULL, 106*760c253cSXin Li stdout=subprocess.PIPE, 107*760c253cSXin Li encoding="utf-8", 108*760c253cSXin Li ) 109*760c253cSXin Li return result.stdout 110*760c253cSXin Li 111*760c253cSXin Li 112*760c253cSXin Lidef translate_prebase_sha_to_rev_number( 113*760c253cSXin Li llvm_config: LLVMConfig, sha: str 114*760c253cSXin Li) -> int: 115*760c253cSXin Li """Translates a sha to a revision number (e.g., "llvm-svn: 1234"). 116*760c253cSXin Li 117*760c253cSXin Li This function assumes that the given SHA is an ancestor of |base_llvm_sha|. 118*760c253cSXin Li """ 119*760c253cSXin Li commit_message = check_output( 120*760c253cSXin Li ["git", "log", "-n1", "--format=%B", sha, "--"], 121*760c253cSXin Li cwd=llvm_config.dir, 122*760c253cSXin Li ) 123*760c253cSXin Li last_line = commit_message.strip().splitlines()[-1] 124*760c253cSXin Li svn_match = re.match(r"^llvm-svn: (\d+)$", last_line) 125*760c253cSXin Li 126*760c253cSXin Li if not svn_match: 127*760c253cSXin Li raise ValueError( 128*760c253cSXin Li f"No llvm-svn line found for {sha}, which... shouldn't happen?" 129*760c253cSXin Li ) 130*760c253cSXin Li 131*760c253cSXin Li return int(svn_match.group(1)) 132*760c253cSXin Li 133*760c253cSXin Li 134*760c253cSXin Lidef translate_sha_to_rev(llvm_config: LLVMConfig, sha_or_ref: str) -> Rev: 135*760c253cSXin Li """Translates a sha or git ref to a Rev.""" 136*760c253cSXin Li 137*760c253cSXin Li if is_git_sha(sha_or_ref): 138*760c253cSXin Li sha = sha_or_ref 139*760c253cSXin Li else: 140*760c253cSXin Li sha = check_output( 141*760c253cSXin Li ["git", "rev-parse", "--revs-only", sha_or_ref, "--"], 142*760c253cSXin Li cwd=llvm_config.dir, 143*760c253cSXin Li ) 144*760c253cSXin Li sha = sha.strip() 145*760c253cSXin Li 146*760c253cSXin Li for base_rev, base_sha in reversed(known_llvm_rev_sha_pairs): 147*760c253cSXin Li merge_base = check_output( 148*760c253cSXin Li ["git", "merge-base", base_sha, sha, "--"], 149*760c253cSXin Li cwd=llvm_config.dir, 150*760c253cSXin Li ) 151*760c253cSXin Li merge_base = merge_base.strip() 152*760c253cSXin Li if merge_base == base_sha: 153*760c253cSXin Li result = check_output( 154*760c253cSXin Li [ 155*760c253cSXin Li "git", 156*760c253cSXin Li "rev-list", 157*760c253cSXin Li "--count", 158*760c253cSXin Li "--first-parent", 159*760c253cSXin Li f"{base_sha}..{sha}", 160*760c253cSXin Li "--", 161*760c253cSXin Li ], 162*760c253cSXin Li cwd=llvm_config.dir, 163*760c253cSXin Li ) 164*760c253cSXin Li count = int(result.strip()) 165*760c253cSXin Li return Rev(branch=MAIN_BRANCH, number=count + base_rev) 166*760c253cSXin Li 167*760c253cSXin Li # Otherwise, either: 168*760c253cSXin Li # - |merge_base| is |sha| (we have a guaranteed llvm-svn number on |sha|) 169*760c253cSXin Li # - |merge_base| is neither (we have a guaranteed llvm-svn number on 170*760c253cSXin Li # |merge_base|, but not |sha|) 171*760c253cSXin Li merge_base_number = translate_prebase_sha_to_rev_number( 172*760c253cSXin Li llvm_config, merge_base 173*760c253cSXin Li ) 174*760c253cSXin Li if merge_base == sha: 175*760c253cSXin Li return Rev(branch=MAIN_BRANCH, number=merge_base_number) 176*760c253cSXin Li 177*760c253cSXin Li distance_from_base = check_output( 178*760c253cSXin Li [ 179*760c253cSXin Li "git", 180*760c253cSXin Li "rev-list", 181*760c253cSXin Li "--count", 182*760c253cSXin Li "--first-parent", 183*760c253cSXin Li f"{merge_base}..{sha}", 184*760c253cSXin Li "--", 185*760c253cSXin Li ], 186*760c253cSXin Li cwd=llvm_config.dir, 187*760c253cSXin Li ) 188*760c253cSXin Li 189*760c253cSXin Li revision_number = merge_base_number + int(distance_from_base.strip()) 190*760c253cSXin Li branches_containing = check_output( 191*760c253cSXin Li ["git", "branch", "-r", "--contains", sha], 192*760c253cSXin Li cwd=llvm_config.dir, 193*760c253cSXin Li ) 194*760c253cSXin Li 195*760c253cSXin Li candidates = [] 196*760c253cSXin Li 197*760c253cSXin Li prefix = llvm_config.remote + "/" 198*760c253cSXin Li for branch in branches_containing.splitlines(): 199*760c253cSXin Li branch = branch.strip() 200*760c253cSXin Li if branch.startswith(prefix): 201*760c253cSXin Li candidates.append(branch[len(prefix) :]) 202*760c253cSXin Li 203*760c253cSXin Li if not candidates: 204*760c253cSXin Li raise ValueError( 205*760c253cSXin Li f"No viable branches found from {llvm_config.remote} with {sha}" 206*760c253cSXin Li ) 207*760c253cSXin Li 208*760c253cSXin Li # It seems that some `origin/release/.*` branches have 209*760c253cSXin Li # `origin/upstream/release/.*` equivalents, which is... awkward to deal 210*760c253cSXin Li # with. Prefer the latter, since that seems to have newer commits than the 211*760c253cSXin Li # former. Technically n^2, but len(elements) should be like, tens in the 212*760c253cSXin Li # worst case. 213*760c253cSXin Li candidates = [x for x in candidates if f"upstream/{x}" not in candidates] 214*760c253cSXin Li if len(candidates) != 1: 215*760c253cSXin Li raise ValueError( 216*760c253cSXin Li f"Ambiguity: multiple branches from {llvm_config.remote} have " 217*760c253cSXin Li f"{sha}: {sorted(candidates)}" 218*760c253cSXin Li ) 219*760c253cSXin Li 220*760c253cSXin Li return Rev(branch=candidates[0], number=revision_number) 221*760c253cSXin Li 222*760c253cSXin Li 223*760c253cSXin Lidef parse_git_commit_messages( 224*760c253cSXin Li stream: Union[Iterable[str], IO[str]], separator: str 225*760c253cSXin Li) -> Iterable[Tuple[str, str]]: 226*760c253cSXin Li """Parses a stream of git log messages. 227*760c253cSXin Li 228*760c253cSXin Li These are expected to be in the format: 229*760c253cSXin Li 230*760c253cSXin Li 40 character sha 231*760c253cSXin Li commit 232*760c253cSXin Li message 233*760c253cSXin Li body 234*760c253cSXin Li separator 235*760c253cSXin Li 40 character sha 236*760c253cSXin Li commit 237*760c253cSXin Li message 238*760c253cSXin Li body 239*760c253cSXin Li separator 240*760c253cSXin Li """ 241*760c253cSXin Li 242*760c253cSXin Li lines = iter(stream) 243*760c253cSXin Li while True: 244*760c253cSXin Li # Looks like a potential bug in pylint? crbug.com/1041148 245*760c253cSXin Li # pylint: disable=stop-iteration-return 246*760c253cSXin Li sha = next(lines, None) 247*760c253cSXin Li if sha is None: 248*760c253cSXin Li return 249*760c253cSXin Li 250*760c253cSXin Li sha = sha.strip() 251*760c253cSXin Li assert is_git_sha(sha), f"Invalid git SHA: {sha}" 252*760c253cSXin Li 253*760c253cSXin Li message = [] 254*760c253cSXin Li for line in lines: 255*760c253cSXin Li if line.strip() == separator: 256*760c253cSXin Li break 257*760c253cSXin Li message.append(line) 258*760c253cSXin Li 259*760c253cSXin Li yield sha, "".join(message) 260*760c253cSXin Li 261*760c253cSXin Li 262*760c253cSXin Lidef translate_prebase_rev_to_sha(llvm_config: LLVMConfig, rev: Rev) -> str: 263*760c253cSXin Li """Translates a Rev to a SHA. 264*760c253cSXin Li 265*760c253cSXin Li This function assumes that the given rev refers to a commit that's an 266*760c253cSXin Li ancestor of |base_llvm_sha|. 267*760c253cSXin Li """ 268*760c253cSXin Li # Because reverts may include reverted commit messages, we can't just |-n1| 269*760c253cSXin Li # and pick that. 270*760c253cSXin Li separator = ">!" * 80 271*760c253cSXin Li looking_for = f"llvm-svn: {rev.number}" 272*760c253cSXin Li 273*760c253cSXin Li git_command = [ 274*760c253cSXin Li "git", 275*760c253cSXin Li "log", 276*760c253cSXin Li "--grep", 277*760c253cSXin Li f"^{looking_for}$", 278*760c253cSXin Li f"--format=%H%n%B{separator}", 279*760c253cSXin Li base_llvm_sha, 280*760c253cSXin Li ] 281*760c253cSXin Li 282*760c253cSXin Li with subprocess.Popen( 283*760c253cSXin Li git_command, 284*760c253cSXin Li cwd=llvm_config.dir, 285*760c253cSXin Li stdin=subprocess.DEVNULL, 286*760c253cSXin Li stdout=subprocess.PIPE, 287*760c253cSXin Li encoding="utf-8", 288*760c253cSXin Li ) as subp: 289*760c253cSXin Li assert subp.stdout is not None 290*760c253cSXin Li for sha, message in parse_git_commit_messages(subp.stdout, separator): 291*760c253cSXin Li last_line = message.splitlines()[-1] 292*760c253cSXin Li if last_line.strip() == looking_for: 293*760c253cSXin Li subp.terminate() 294*760c253cSXin Li return sha 295*760c253cSXin Li if subp.wait() != 0: 296*760c253cSXin Li raise subprocess.CalledProcessError(subp.returncode, git_command) 297*760c253cSXin Li 298*760c253cSXin Li raise ValueError(f"No commit with revision {rev} found") 299*760c253cSXin Li 300*760c253cSXin Li 301*760c253cSXin Lidef translate_rev_to_sha_from_baseline( 302*760c253cSXin Li llvm_config: LLVMConfig, 303*760c253cSXin Li parent_sha: str, 304*760c253cSXin Li parent_rev: int, 305*760c253cSXin Li child_sha: str, 306*760c253cSXin Li child_rev: Optional[int], 307*760c253cSXin Li want_rev: int, 308*760c253cSXin Li branch_name: str, 309*760c253cSXin Li) -> str: 310*760c253cSXin Li """Translates a revision number between a parent & child to a SHA. 311*760c253cSXin Li 312*760c253cSXin Li Args: 313*760c253cSXin Li llvm_config: LLVM config to use. 314*760c253cSXin Li parent_sha: SHA of the parent that the revision number is a child of. 315*760c253cSXin Li parent_rev: Revision number of `parent_sha`. 316*760c253cSXin Li child_sha: A child of `parent_sha` to find a rev on. 317*760c253cSXin Li child_rev: Optional note of what the child's revision number is. 318*760c253cSXin Li want_rev: The desired revision number between child and parent. 319*760c253cSXin Li branch_name: Name of the branch to refer to if a ValueError is raised. 320*760c253cSXin Li 321*760c253cSXin Li Raises: 322*760c253cSXin Li ValueError if the given child isn't far enough away from the parent to 323*760c253cSXin Li find `want_rev`. 324*760c253cSXin Li """ 325*760c253cSXin Li # As a convenience, have a fast path for want_rev < parent_rev. In 326*760c253cSXin Li # particular, branches can hit this case. 327*760c253cSXin Li if want_rev < parent_rev: 328*760c253cSXin Li baseline_git_sha = parent_sha 329*760c253cSXin Li commits_behind_baseline = parent_rev - want_rev 330*760c253cSXin Li else: 331*760c253cSXin Li if child_rev is None: 332*760c253cSXin Li commits_between_parent_and_child = check_output( 333*760c253cSXin Li [ 334*760c253cSXin Li "git", 335*760c253cSXin Li "rev-list", 336*760c253cSXin Li "--count", 337*760c253cSXin Li "--first-parent", 338*760c253cSXin Li f"{parent_sha}..{child_sha}", 339*760c253cSXin Li "--", 340*760c253cSXin Li ], 341*760c253cSXin Li cwd=llvm_config.dir, 342*760c253cSXin Li ) 343*760c253cSXin Li child_rev = parent_rev + int( 344*760c253cSXin Li commits_between_parent_and_child.strip() 345*760c253cSXin Li ) 346*760c253cSXin Li if child_rev < want_rev: 347*760c253cSXin Li raise ValueError( 348*760c253cSXin Li "Revision {want_rev} is past " 349*760c253cSXin Li f"{llvm_config.remote}/{branch_name}. Try updating your tree?" 350*760c253cSXin Li ) 351*760c253cSXin Li baseline_git_sha = child_sha 352*760c253cSXin Li commits_behind_baseline = child_rev - want_rev 353*760c253cSXin Li 354*760c253cSXin Li if not commits_behind_baseline: 355*760c253cSXin Li return baseline_git_sha 356*760c253cSXin Li 357*760c253cSXin Li result = check_output( 358*760c253cSXin Li [ 359*760c253cSXin Li "git", 360*760c253cSXin Li "rev-parse", 361*760c253cSXin Li "--revs-only", 362*760c253cSXin Li f"{baseline_git_sha}~{commits_behind_baseline}", 363*760c253cSXin Li ], 364*760c253cSXin Li cwd=llvm_config.dir, 365*760c253cSXin Li ) 366*760c253cSXin Li return result.strip() 367*760c253cSXin Li 368*760c253cSXin Li 369*760c253cSXin Lidef translate_rev_to_sha(llvm_config: LLVMConfig, rev: Rev) -> str: 370*760c253cSXin Li """Translates a Rev to a SHA. 371*760c253cSXin Li 372*760c253cSXin Li Raises a ValueError if the given Rev doesn't exist in the given config. 373*760c253cSXin Li """ 374*760c253cSXin Li branch, number = rev 375*760c253cSXin Li 376*760c253cSXin Li branch_tip = check_output( 377*760c253cSXin Li ["git", "rev-parse", "--revs-only", f"{llvm_config.remote}/{branch}"], 378*760c253cSXin Li cwd=llvm_config.dir, 379*760c253cSXin Li ).strip() 380*760c253cSXin Li 381*760c253cSXin Li if branch != MAIN_BRANCH: 382*760c253cSXin Li main_merge_point = check_output( 383*760c253cSXin Li [ 384*760c253cSXin Li "git", 385*760c253cSXin Li "merge-base", 386*760c253cSXin Li f"{llvm_config.remote}/{MAIN_BRANCH}", 387*760c253cSXin Li branch_tip, 388*760c253cSXin Li ], 389*760c253cSXin Li cwd=llvm_config.dir, 390*760c253cSXin Li ) 391*760c253cSXin Li main_merge_point = main_merge_point.strip() 392*760c253cSXin Li main_rev = translate_sha_to_rev(llvm_config, main_merge_point) 393*760c253cSXin Li return translate_rev_to_sha_from_baseline( 394*760c253cSXin Li llvm_config, 395*760c253cSXin Li parent_sha=main_merge_point, 396*760c253cSXin Li parent_rev=main_rev.number, 397*760c253cSXin Li child_sha=branch_tip, 398*760c253cSXin Li child_rev=None, 399*760c253cSXin Li want_rev=number, 400*760c253cSXin Li branch_name=branch, 401*760c253cSXin Li ) 402*760c253cSXin Li 403*760c253cSXin Li if number < base_llvm_revision: 404*760c253cSXin Li return translate_prebase_rev_to_sha(llvm_config, rev) 405*760c253cSXin Li 406*760c253cSXin Li # Technically this could be a binary search, but the list has fewer than 10 407*760c253cSXin Li # elems, and won't grow fast. Linear is easier. 408*760c253cSXin Li last_cached_rev = None 409*760c253cSXin Li last_cached_sha = branch_tip 410*760c253cSXin Li for cached_rev, cached_sha in reversed(known_llvm_rev_sha_pairs): 411*760c253cSXin Li if cached_rev == number: 412*760c253cSXin Li return cached_sha 413*760c253cSXin Li 414*760c253cSXin Li if cached_rev < number: 415*760c253cSXin Li return translate_rev_to_sha_from_baseline( 416*760c253cSXin Li llvm_config, 417*760c253cSXin Li parent_sha=cached_sha, 418*760c253cSXin Li parent_rev=cached_rev, 419*760c253cSXin Li child_sha=last_cached_sha, 420*760c253cSXin Li child_rev=last_cached_rev, 421*760c253cSXin Li want_rev=number, 422*760c253cSXin Li branch_name=branch, 423*760c253cSXin Li ) 424*760c253cSXin Li 425*760c253cSXin Li last_cached_rev = cached_rev 426*760c253cSXin Li last_cached_sha = cached_sha 427*760c253cSXin Li 428*760c253cSXin Li # This is only hit if `number >= base_llvm_revision` _and_ there's no 429*760c253cSXin Li # coverage for `number` in `known_llvm_rev_sha_pairs`, which contains 430*760c253cSXin Li # `base_llvm_revision`. 431*760c253cSXin Li assert False, "Couldn't find a base SHA for a rev on main?" 432*760c253cSXin Li 433*760c253cSXin Li 434*760c253cSXin Lidef find_root_llvm_dir(root_dir: str = ".") -> str: 435*760c253cSXin Li """Finds the root of an LLVM directory starting at |root_dir|. 436*760c253cSXin Li 437*760c253cSXin Li Raises a subprocess.CalledProcessError if no git directory is found. 438*760c253cSXin Li """ 439*760c253cSXin Li result = check_output( 440*760c253cSXin Li ["git", "rev-parse", "--show-toplevel"], 441*760c253cSXin Li cwd=root_dir, 442*760c253cSXin Li ) 443*760c253cSXin Li return result.strip() 444*760c253cSXin Li 445*760c253cSXin Li 446*760c253cSXin Lidef main(argv: List[str]) -> None: 447*760c253cSXin Li parser = argparse.ArgumentParser(description=__doc__) 448*760c253cSXin Li parser.add_argument( 449*760c253cSXin Li "--llvm_dir", 450*760c253cSXin Li help="LLVM directory to consult for git history, etc. Autodetected " 451*760c253cSXin Li "if cwd is inside of an LLVM tree", 452*760c253cSXin Li ) 453*760c253cSXin Li parser.add_argument( 454*760c253cSXin Li "--upstream", 455*760c253cSXin Li default="origin", 456*760c253cSXin Li help="LLVM upstream's remote name. Defaults to %(default)s.", 457*760c253cSXin Li ) 458*760c253cSXin Li sha_or_rev = parser.add_mutually_exclusive_group(required=True) 459*760c253cSXin Li sha_or_rev.add_argument( 460*760c253cSXin Li "--sha", help="A git SHA (or ref) to convert to a rev" 461*760c253cSXin Li ) 462*760c253cSXin Li sha_or_rev.add_argument("--rev", help="A rev to convert into a sha") 463*760c253cSXin Li opts = parser.parse_args(argv) 464*760c253cSXin Li 465*760c253cSXin Li llvm_dir = opts.llvm_dir 466*760c253cSXin Li if llvm_dir is None: 467*760c253cSXin Li try: 468*760c253cSXin Li llvm_dir = find_root_llvm_dir() 469*760c253cSXin Li except subprocess.CalledProcessError: 470*760c253cSXin Li parser.error( 471*760c253cSXin Li "Couldn't autodetect an LLVM tree; please use --llvm_dir" 472*760c253cSXin Li ) 473*760c253cSXin Li 474*760c253cSXin Li config = LLVMConfig( 475*760c253cSXin Li remote=opts.upstream, 476*760c253cSXin Li dir=opts.llvm_dir or find_root_llvm_dir(), 477*760c253cSXin Li ) 478*760c253cSXin Li 479*760c253cSXin Li if opts.sha: 480*760c253cSXin Li rev = translate_sha_to_rev(config, opts.sha) 481*760c253cSXin Li print(rev) 482*760c253cSXin Li else: 483*760c253cSXin Li sha = translate_rev_to_sha(config, Rev.parse(opts.rev)) 484*760c253cSXin Li print(sha) 485*760c253cSXin Li 486*760c253cSXin Li 487*760c253cSXin Liif __name__ == "__main__": 488*760c253cSXin Li main(sys.argv[1:]) 489