xref: /aosp_15_r20/external/toolchain-utils/llvm_tools/get_llvm_hash.py (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
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"""Returns the latest LLVM version's hash."""
7*760c253cSXin Li
8*760c253cSXin Liimport argparse
9*760c253cSXin Liimport contextlib
10*760c253cSXin Liimport functools
11*760c253cSXin Liimport os
12*760c253cSXin Lifrom pathlib import Path
13*760c253cSXin Liimport re
14*760c253cSXin Liimport shutil
15*760c253cSXin Liimport subprocess
16*760c253cSXin Liimport sys
17*760c253cSXin Liimport tempfile
18*760c253cSXin Lifrom typing import Iterator, Optional, Tuple, Union
19*760c253cSXin Li
20*760c253cSXin Liimport chroot
21*760c253cSXin Liimport git_llvm_rev
22*760c253cSXin Liimport llvm_next
23*760c253cSXin Liimport manifest_utils
24*760c253cSXin Liimport subprocess_helpers
25*760c253cSXin Li
26*760c253cSXin Li
27*760c253cSXin Li_LLVM_GIT_URL = (
28*760c253cSXin Li    "https://chromium.googlesource.com/external/github.com/llvm/llvm-project"
29*760c253cSXin Li)
30*760c253cSXin Li
31*760c253cSXin LiKNOWN_HASH_SOURCES = (
32*760c253cSXin Li    "google3",
33*760c253cSXin Li    "google3-unstable",
34*760c253cSXin Li    "llvm",
35*760c253cSXin Li    "llvm-next",
36*760c253cSXin Li    "tot",
37*760c253cSXin Li)
38*760c253cSXin Li
39*760c253cSXin Li
40*760c253cSXin Lidef GetVersionFrom(src_dir: Union[Path, str], git_hash: str) -> int:
41*760c253cSXin Li    """Obtain an SVN-style version number based on the LLVM git hash passed in.
42*760c253cSXin Li
43*760c253cSXin Li    Args:
44*760c253cSXin Li        src_dir: LLVM's source directory.
45*760c253cSXin Li        git_hash: The git hash.
46*760c253cSXin Li
47*760c253cSXin Li    Returns:
48*760c253cSXin Li        An SVN-style version number associated with the git hash.
49*760c253cSXin Li    """
50*760c253cSXin Li
51*760c253cSXin Li    version = git_llvm_rev.translate_sha_to_rev(
52*760c253cSXin Li        git_llvm_rev.LLVMConfig(remote="origin", dir=src_dir), git_hash
53*760c253cSXin Li    )
54*760c253cSXin Li    # Note: branches aren't supported
55*760c253cSXin Li    assert version.branch == git_llvm_rev.MAIN_BRANCH, version.branch
56*760c253cSXin Li    return version.number
57*760c253cSXin Li
58*760c253cSXin Li
59*760c253cSXin Lidef GetGitHashFrom(src_dir: Union[Path, str], version: int) -> str:
60*760c253cSXin Li    """Finds the commit hash(es) of the LLVM version in the git log history.
61*760c253cSXin Li
62*760c253cSXin Li    Args:
63*760c253cSXin Li        src_dir: The LLVM source tree.
64*760c253cSXin Li        version: The version number.
65*760c253cSXin Li
66*760c253cSXin Li    Returns:
67*760c253cSXin Li        A git hash string corresponding to the version number.
68*760c253cSXin Li
69*760c253cSXin Li    Raises:
70*760c253cSXin Li        subprocess.CalledProcessError: Failed to find a git hash.
71*760c253cSXin Li    """
72*760c253cSXin Li
73*760c253cSXin Li    return git_llvm_rev.translate_rev_to_sha(
74*760c253cSXin Li        git_llvm_rev.LLVMConfig(remote="origin", dir=src_dir),
75*760c253cSXin Li        git_llvm_rev.Rev(branch=git_llvm_rev.MAIN_BRANCH, number=version),
76*760c253cSXin Li    )
77*760c253cSXin Li
78*760c253cSXin Li
79*760c253cSXin Lidef CheckoutBranch(src_dir: Union[Path, str], branch: str) -> None:
80*760c253cSXin Li    """Checks out and pulls from a branch in a git repo.
81*760c253cSXin Li
82*760c253cSXin Li    Args:
83*760c253cSXin Li        src_dir: The LLVM source tree.
84*760c253cSXin Li        branch: The git branch to checkout in src_dir.
85*760c253cSXin Li
86*760c253cSXin Li    Raises:
87*760c253cSXin Li        ValueError: Failed to checkout or pull branch version
88*760c253cSXin Li    """
89*760c253cSXin Li    subprocess_helpers.CheckCommand(["git", "-C", src_dir, "checkout", branch])
90*760c253cSXin Li    subprocess_helpers.CheckCommand(["git", "-C", src_dir, "pull"])
91*760c253cSXin Li
92*760c253cSXin Li
93*760c253cSXin Lidef ParseLLVMMajorVersion(cmakelist: str) -> Optional[str]:
94*760c253cSXin Li    """Reads CMakeList.txt file contents for LLVMMajor Version.
95*760c253cSXin Li
96*760c253cSXin Li    Args:
97*760c253cSXin Li        cmakelist: contents of CMakeList.txt
98*760c253cSXin Li
99*760c253cSXin Li    Returns:
100*760c253cSXin Li        The major version number as a string, or None if it couldn't be found.
101*760c253cSXin Li    """
102*760c253cSXin Li    match = re.search(
103*760c253cSXin Li        r"\n\s+set\(LLVM_VERSION_MAJOR (?P<major>\d+)\)", cmakelist
104*760c253cSXin Li    )
105*760c253cSXin Li    if not match:
106*760c253cSXin Li        return None
107*760c253cSXin Li    return match.group("major")
108*760c253cSXin Li
109*760c253cSXin Li
110*760c253cSXin Li@functools.lru_cache(maxsize=1)
111*760c253cSXin Lidef GetLLVMMajorVersion(git_hash: Optional[str] = None) -> str:
112*760c253cSXin Li    """Reads llvm/CMakeList.txt file contents for LLVMMajor Version.
113*760c253cSXin Li
114*760c253cSXin Li    Args:
115*760c253cSXin Li        git_hash: git hash of llvm version as string or None for top of trunk
116*760c253cSXin Li
117*760c253cSXin Li    Returns:
118*760c253cSXin Li        The major version number as a string
119*760c253cSXin Li
120*760c253cSXin Li    Raises:
121*760c253cSXin Li        ValueError: The major version cannot be parsed from cmakelist or
122*760c253cSXin Li          there was a failure to checkout git_hash version
123*760c253cSXin Li        FileExistsError: The src directory doe not contain CMakeList.txt
124*760c253cSXin Li    """
125*760c253cSXin Li    src_dir = GetAndUpdateLLVMProjectInLLVMTools()
126*760c253cSXin Li
127*760c253cSXin Li    # b/325895866#comment36: the LLVM version number was moved from
128*760c253cSXin Li    # `llvm/CMakeLists.txt` to `cmake/Modules/LLVMVersion.cmake` in upstream
129*760c253cSXin Li    # commit 81e20472a0c5a4a8edc5ec38dc345d580681af81 (r530225). Until we no
130*760c253cSXin Li    # longer care about looking before that, we need to support searching both
131*760c253cSXin Li    # files.
132*760c253cSXin Li    cmakelists_paths = (
133*760c253cSXin Li        Path(src_dir) / "llvm" / "CMakeLists.txt",
134*760c253cSXin Li        Path(src_dir) / "cmake" / "Modules" / "LLVMVersion.cmake",
135*760c253cSXin Li    )
136*760c253cSXin Li
137*760c253cSXin Li    with contextlib.ExitStack() as on_exit:
138*760c253cSXin Li        if git_hash:
139*760c253cSXin Li            subprocess_helpers.CheckCommand(
140*760c253cSXin Li                ["git", "-C", src_dir, "checkout", git_hash]
141*760c253cSXin Li            )
142*760c253cSXin Li            on_exit.callback(CheckoutBranch, src_dir, git_llvm_rev.MAIN_BRANCH)
143*760c253cSXin Li
144*760c253cSXin Li        for path in cmakelists_paths:
145*760c253cSXin Li            try:
146*760c253cSXin Li                file_contents = path.read_text(encoding="utf-8")
147*760c253cSXin Li            except FileNotFoundError:
148*760c253cSXin Li                # If this file DNE (yet), ignore it.
149*760c253cSXin Li                continue
150*760c253cSXin Li
151*760c253cSXin Li            if version := ParseLLVMMajorVersion(file_contents):
152*760c253cSXin Li                return version
153*760c253cSXin Li
154*760c253cSXin Li    raise ValueError(
155*760c253cSXin Li        f"Major version could not be parsed from any of {cmakelists_paths}"
156*760c253cSXin Li    )
157*760c253cSXin Li
158*760c253cSXin Li
159*760c253cSXin Li@contextlib.contextmanager
160*760c253cSXin Lidef CreateTempLLVMRepo(temp_dir: str) -> Iterator[str]:
161*760c253cSXin Li    """Adds a LLVM worktree to 'temp_dir'.
162*760c253cSXin Li
163*760c253cSXin Li    Creating a worktree because the LLVM source tree in
164*760c253cSXin Li    '../toolchain-utils/llvm_tools/llvm-project-copy' should not be modified.
165*760c253cSXin Li
166*760c253cSXin Li    This is useful for applying patches to a source tree but do not want to
167*760c253cSXin Li    modify the actual LLVM source tree in 'llvm-project-copy'.
168*760c253cSXin Li
169*760c253cSXin Li    Args:
170*760c253cSXin Li        temp_dir: An absolute path to the temporary directory to put the
171*760c253cSXin Li        worktree in (obtained via 'tempfile.mkdtemp()').
172*760c253cSXin Li
173*760c253cSXin Li    Yields:
174*760c253cSXin Li        The absolute path to 'temp_dir'.
175*760c253cSXin Li
176*760c253cSXin Li    Raises:
177*760c253cSXin Li        subprocess.CalledProcessError: Failed to remove the worktree.
178*760c253cSXin Li        ValueError: Failed to add a worktree.
179*760c253cSXin Li    """
180*760c253cSXin Li
181*760c253cSXin Li    abs_path_to_llvm_project_dir = GetAndUpdateLLVMProjectInLLVMTools()
182*760c253cSXin Li    subprocess_helpers.CheckCommand(
183*760c253cSXin Li        [
184*760c253cSXin Li            "git",
185*760c253cSXin Li            "-C",
186*760c253cSXin Li            abs_path_to_llvm_project_dir,
187*760c253cSXin Li            "worktree",
188*760c253cSXin Li            "add",
189*760c253cSXin Li            "--detach",
190*760c253cSXin Li            temp_dir,
191*760c253cSXin Li            "origin/%s" % git_llvm_rev.MAIN_BRANCH,
192*760c253cSXin Li        ]
193*760c253cSXin Li    )
194*760c253cSXin Li
195*760c253cSXin Li    try:
196*760c253cSXin Li        yield temp_dir
197*760c253cSXin Li    finally:
198*760c253cSXin Li        if os.path.isdir(temp_dir):
199*760c253cSXin Li            subprocess_helpers.check_output(
200*760c253cSXin Li                [
201*760c253cSXin Li                    "git",
202*760c253cSXin Li                    "-C",
203*760c253cSXin Li                    abs_path_to_llvm_project_dir,
204*760c253cSXin Li                    "worktree",
205*760c253cSXin Li                    "remove",
206*760c253cSXin Li                    "-f",
207*760c253cSXin Li                    temp_dir,
208*760c253cSXin Li                ]
209*760c253cSXin Li            )
210*760c253cSXin Li
211*760c253cSXin Li
212*760c253cSXin Lidef GetAndUpdateLLVMProjectInLLVMTools() -> str:
213*760c253cSXin Li    """Gets the absolute path to 'llvm-project-copy' directory in 'llvm_tools'.
214*760c253cSXin Li
215*760c253cSXin Li    The intent of this function is to avoid cloning the LLVM repo and then
216*760c253cSXin Li    discarding the contents of the repo. The function will create a directory
217*760c253cSXin Li    in '../toolchain-utils/llvm_tools' called 'llvm-project-copy' if this
218*760c253cSXin Li    directory does not exist yet. If it does not exist, then it will use the
219*760c253cSXin Li    LLVMHash() class to clone the LLVM repo into 'llvm-project-copy'.
220*760c253cSXin Li    Otherwise, it will clean the contents of that directory and then fetch from
221*760c253cSXin Li    the chromium LLVM mirror. In either case, this function will return the
222*760c253cSXin Li    absolute path to 'llvm-project-copy' directory.
223*760c253cSXin Li
224*760c253cSXin Li    Returns:
225*760c253cSXin Li        Absolute path to 'llvm-project-copy' directory in 'llvm_tools'
226*760c253cSXin Li
227*760c253cSXin Li    Raises:
228*760c253cSXin Li        ValueError: LLVM repo (in 'llvm-project-copy' dir.) has changes or
229*760c253cSXin Li        failed to checkout to main or failed to fetch from chromium mirror of
230*760c253cSXin Li        LLVM.
231*760c253cSXin Li    """
232*760c253cSXin Li
233*760c253cSXin Li    abs_path_to_llvm_tools_dir = os.path.dirname(os.path.abspath(__file__))
234*760c253cSXin Li
235*760c253cSXin Li    abs_path_to_llvm_project_dir = os.path.join(
236*760c253cSXin Li        abs_path_to_llvm_tools_dir, "llvm-project-copy"
237*760c253cSXin Li    )
238*760c253cSXin Li
239*760c253cSXin Li    if not os.path.isdir(abs_path_to_llvm_project_dir):
240*760c253cSXin Li        print(
241*760c253cSXin Li            f"Checking out LLVM to {abs_path_to_llvm_project_dir}\n"
242*760c253cSXin Li            "so that we can map between commit hashes and revision numbers.\n"
243*760c253cSXin Li            "This may take a while, but only has to be done once.",
244*760c253cSXin Li            file=sys.stderr,
245*760c253cSXin Li        )
246*760c253cSXin Li        os.mkdir(abs_path_to_llvm_project_dir)
247*760c253cSXin Li
248*760c253cSXin Li        LLVMHash().CloneLLVMRepo(abs_path_to_llvm_project_dir)
249*760c253cSXin Li    else:
250*760c253cSXin Li        # `git status` has a '-s'/'--short' option that shortens the output.
251*760c253cSXin Li        # With the '-s' option, if no changes were made to the LLVM repo, then
252*760c253cSXin Li        # the output (assigned to 'repo_status') would be empty.
253*760c253cSXin Li        repo_status = subprocess_helpers.check_output(
254*760c253cSXin Li            ["git", "-C", abs_path_to_llvm_project_dir, "status", "-s"]
255*760c253cSXin Li        )
256*760c253cSXin Li
257*760c253cSXin Li        if repo_status.rstrip():
258*760c253cSXin Li            raise ValueError(
259*760c253cSXin Li                "LLVM repo in %s has changes, please remove."
260*760c253cSXin Li                % abs_path_to_llvm_project_dir
261*760c253cSXin Li            )
262*760c253cSXin Li
263*760c253cSXin Li        CheckoutBranch(abs_path_to_llvm_project_dir, git_llvm_rev.MAIN_BRANCH)
264*760c253cSXin Li
265*760c253cSXin Li    return abs_path_to_llvm_project_dir
266*760c253cSXin Li
267*760c253cSXin Li
268*760c253cSXin Lidef GetGoogle3LLVMVersion(stable: bool) -> int:
269*760c253cSXin Li    """Gets the latest google3 LLVM version.
270*760c253cSXin Li
271*760c253cSXin Li    Args:
272*760c253cSXin Li        stable: boolean, use the stable version or the unstable version
273*760c253cSXin Li
274*760c253cSXin Li    Returns:
275*760c253cSXin Li        The latest LLVM SVN version as an integer.
276*760c253cSXin Li
277*760c253cSXin Li    Raises:
278*760c253cSXin Li        subprocess.CalledProcessError: An invalid path has been provided to the
279*760c253cSXin Li        `cat` command.
280*760c253cSXin Li    """
281*760c253cSXin Li
282*760c253cSXin Li    subdir = "stable" if stable else "llvm_unstable"
283*760c253cSXin Li
284*760c253cSXin Li    # Cmd to get latest google3 LLVM version.
285*760c253cSXin Li    cmd = [
286*760c253cSXin Li        "cat",
287*760c253cSXin Li        os.path.join(
288*760c253cSXin Li            "/google/src/head/depot/google3/third_party/crosstool/v18",
289*760c253cSXin Li            subdir,
290*760c253cSXin Li            "installs/llvm/git_origin_rev_id",
291*760c253cSXin Li        ),
292*760c253cSXin Li    ]
293*760c253cSXin Li
294*760c253cSXin Li    # Get latest version.
295*760c253cSXin Li    git_hash = subprocess_helpers.check_output(cmd)
296*760c253cSXin Li
297*760c253cSXin Li    # Change type to an integer
298*760c253cSXin Li    return GetVersionFrom(
299*760c253cSXin Li        GetAndUpdateLLVMProjectInLLVMTools(), git_hash.rstrip()
300*760c253cSXin Li    )
301*760c253cSXin Li
302*760c253cSXin Li
303*760c253cSXin Lidef IsSvnOption(svn_option: str) -> Union[int, str]:
304*760c253cSXin Li    """Validates whether the argument (string) is a git hash option.
305*760c253cSXin Li
306*760c253cSXin Li    The argument is used to find the git hash of LLVM.
307*760c253cSXin Li
308*760c253cSXin Li    Args:
309*760c253cSXin Li        svn_option: The option passed in as a command line argument.
310*760c253cSXin Li
311*760c253cSXin Li    Returns:
312*760c253cSXin Li        lowercase svn_option if it is a known hash source, otherwise the
313*760c253cSXin Li        svn_option as an int
314*760c253cSXin Li
315*760c253cSXin Li    Raises:
316*760c253cSXin Li        ValueError: Invalid svn option provided.
317*760c253cSXin Li    """
318*760c253cSXin Li
319*760c253cSXin Li    if svn_option.lower() in KNOWN_HASH_SOURCES:
320*760c253cSXin Li        return svn_option.lower()
321*760c253cSXin Li
322*760c253cSXin Li    try:
323*760c253cSXin Li        svn_version = int(svn_option)
324*760c253cSXin Li
325*760c253cSXin Li        return svn_version
326*760c253cSXin Li
327*760c253cSXin Li    # Unable to convert argument to an int, so the option is invalid.
328*760c253cSXin Li    #
329*760c253cSXin Li    # Ex: 'one'.
330*760c253cSXin Li    except ValueError:
331*760c253cSXin Li        pass
332*760c253cSXin Li
333*760c253cSXin Li    raise ValueError("Invalid LLVM git hash option provided: %s" % svn_option)
334*760c253cSXin Li
335*760c253cSXin Li
336*760c253cSXin Lidef GetLLVMHashAndVersionFromSVNOption(
337*760c253cSXin Li    svn_option: Union[int, str]
338*760c253cSXin Li) -> Tuple[str, int]:
339*760c253cSXin Li    """Gets the LLVM hash and LLVM version based off of the svn option.
340*760c253cSXin Li
341*760c253cSXin Li    Args:
342*760c253cSXin Li        svn_option: A valid svn option obtained from the command line.
343*760c253cSXin Li          Ex. 'google3', 'tot', or <svn_version> such as 365123.
344*760c253cSXin Li
345*760c253cSXin Li    Returns:
346*760c253cSXin Li        A tuple that is the LLVM git hash and LLVM version.
347*760c253cSXin Li    """
348*760c253cSXin Li
349*760c253cSXin Li    new_llvm_hash = LLVMHash()
350*760c253cSXin Li
351*760c253cSXin Li    # Determine which LLVM git hash to retrieve.
352*760c253cSXin Li    if svn_option == "tot":
353*760c253cSXin Li        git_hash = new_llvm_hash.GetTopOfTrunkGitHash()
354*760c253cSXin Li        version = GetVersionFrom(GetAndUpdateLLVMProjectInLLVMTools(), git_hash)
355*760c253cSXin Li    elif isinstance(svn_option, int):
356*760c253cSXin Li        version = svn_option
357*760c253cSXin Li        git_hash = GetGitHashFrom(GetAndUpdateLLVMProjectInLLVMTools(), version)
358*760c253cSXin Li    else:
359*760c253cSXin Li        assert svn_option in ("google3", "google3-unstable")
360*760c253cSXin Li        version = GetGoogle3LLVMVersion(stable=svn_option == "google3")
361*760c253cSXin Li
362*760c253cSXin Li        git_hash = GetGitHashFrom(GetAndUpdateLLVMProjectInLLVMTools(), version)
363*760c253cSXin Li
364*760c253cSXin Li    return git_hash, version
365*760c253cSXin Li
366*760c253cSXin Li
367*760c253cSXin Lidef GetCrOSCurrentLLVMHash(chromeos_tree: Path) -> str:
368*760c253cSXin Li    """Retrieves the current ChromeOS LLVM hash.
369*760c253cSXin Li
370*760c253cSXin Li    Args:
371*760c253cSXin Li        chromeos_tree: A ChromeOS source tree. This is allowed to be
372*760c253cSXin Li        arbitrary subdirectory of an actual ChromeOS tree, for convenience.
373*760c253cSXin Li
374*760c253cSXin Li    Raises:
375*760c253cSXin Li        ManifestValueError if the toolchain manifest doesn't match the
376*760c253cSXin Li        expected structure.
377*760c253cSXin Li    """
378*760c253cSXin Li    chromeos_root = chroot.FindChromeOSRootAbove(chromeos_tree)
379*760c253cSXin Li    return manifest_utils.extract_current_llvm_hash(chromeos_root)
380*760c253cSXin Li
381*760c253cSXin Li
382*760c253cSXin Liclass LLVMHash:
383*760c253cSXin Li    """Provides methods to retrieve a LLVM hash."""
384*760c253cSXin Li
385*760c253cSXin Li    @staticmethod
386*760c253cSXin Li    @contextlib.contextmanager
387*760c253cSXin Li    def CreateTempDirectory() -> Iterator:
388*760c253cSXin Li        temp_dir = tempfile.mkdtemp()
389*760c253cSXin Li
390*760c253cSXin Li        try:
391*760c253cSXin Li            yield temp_dir
392*760c253cSXin Li        finally:
393*760c253cSXin Li            if os.path.isdir(temp_dir):
394*760c253cSXin Li                shutil.rmtree(temp_dir, ignore_errors=True)
395*760c253cSXin Li
396*760c253cSXin Li    def CloneLLVMRepo(self, temp_dir: str) -> None:
397*760c253cSXin Li        """Clones the LLVM repo.
398*760c253cSXin Li
399*760c253cSXin Li        Args:
400*760c253cSXin Li            temp_dir: The temporary directory to clone the repo to.
401*760c253cSXin Li
402*760c253cSXin Li        Raises:
403*760c253cSXin Li            ValueError: Failed to clone the LLVM repo.
404*760c253cSXin Li        """
405*760c253cSXin Li        clone_cmd = ["git", "clone", _LLVM_GIT_URL, temp_dir]
406*760c253cSXin Li        clone_cmd_obj = subprocess.run(
407*760c253cSXin Li            clone_cmd, check=False, stderr=subprocess.PIPE
408*760c253cSXin Li        )
409*760c253cSXin Li        if clone_cmd_obj.returncode:
410*760c253cSXin Li            raise ValueError(
411*760c253cSXin Li                "Failed to clone the LLVM repo; stderr: "
412*760c253cSXin Li                f"{repr(clone_cmd_obj.stderr)}"
413*760c253cSXin Li            )
414*760c253cSXin Li
415*760c253cSXin Li    def GetLLVMHash(self, version: int) -> str:
416*760c253cSXin Li        """Retrieves the LLVM hash corresponding to the LLVM version passed in.
417*760c253cSXin Li
418*760c253cSXin Li        Args:
419*760c253cSXin Li            version: The LLVM version to use as a delimiter.
420*760c253cSXin Li
421*760c253cSXin Li        Returns:
422*760c253cSXin Li            The hash as a string that corresponds to the LLVM version.
423*760c253cSXin Li        """
424*760c253cSXin Li        hash_value = GetGitHashFrom(
425*760c253cSXin Li            GetAndUpdateLLVMProjectInLLVMTools(), version
426*760c253cSXin Li        )
427*760c253cSXin Li        return hash_value
428*760c253cSXin Li
429*760c253cSXin Li    def GetCrOSCurrentLLVMHash(self, chromeos_tree: Path) -> str:
430*760c253cSXin Li        """Retrieves the current ChromeOS LLVM hash."""
431*760c253cSXin Li        return GetCrOSCurrentLLVMHash(chromeos_tree)
432*760c253cSXin Li
433*760c253cSXin Li    def GetCrOSLLVMNextHash(self) -> str:
434*760c253cSXin Li        """Retrieves the current ChromeOS llvm-next hash."""
435*760c253cSXin Li        return llvm_next.LLVM_NEXT_HASH
436*760c253cSXin Li
437*760c253cSXin Li    def GetGoogle3LLVMHash(self) -> str:
438*760c253cSXin Li        """Retrieves the google3 LLVM hash."""
439*760c253cSXin Li        return self.GetLLVMHash(GetGoogle3LLVMVersion(stable=True))
440*760c253cSXin Li
441*760c253cSXin Li    def GetGoogle3UnstableLLVMHash(self) -> str:
442*760c253cSXin Li        """Retrieves the LLVM hash of google3's unstable compiler."""
443*760c253cSXin Li        return self.GetLLVMHash(GetGoogle3LLVMVersion(stable=False))
444*760c253cSXin Li
445*760c253cSXin Li    def GetTopOfTrunkGitHash(self) -> str:
446*760c253cSXin Li        """Gets the latest git hash from top of trunk of LLVM."""
447*760c253cSXin Li
448*760c253cSXin Li        path_to_main_branch = "refs/heads/main"
449*760c253cSXin Li        llvm_tot_git_hash = subprocess_helpers.check_output(
450*760c253cSXin Li            ["git", "ls-remote", _LLVM_GIT_URL, path_to_main_branch]
451*760c253cSXin Li        )
452*760c253cSXin Li        return llvm_tot_git_hash.rstrip().split()[0]
453*760c253cSXin Li
454*760c253cSXin Li
455*760c253cSXin Lidef main() -> None:
456*760c253cSXin Li    """Prints the git hash of LLVM.
457*760c253cSXin Li
458*760c253cSXin Li    Parses the command line for the optional command line
459*760c253cSXin Li    arguments.
460*760c253cSXin Li    """
461*760c253cSXin Li    my_dir = Path(__file__).parent.resolve()
462*760c253cSXin Li
463*760c253cSXin Li    # Create parser and add optional command-line arguments.
464*760c253cSXin Li    parser = argparse.ArgumentParser(description="Finds the LLVM hash.")
465*760c253cSXin Li    parser.add_argument(
466*760c253cSXin Li        "--llvm_version",
467*760c253cSXin Li        type=IsSvnOption,
468*760c253cSXin Li        required=True,
469*760c253cSXin Li        help="which git hash of LLVM to find. Either a svn revision, or one "
470*760c253cSXin Li        "of %s" % sorted(KNOWN_HASH_SOURCES),
471*760c253cSXin Li    )
472*760c253cSXin Li    parser.add_argument(
473*760c253cSXin Li        "--chromeos_tree",
474*760c253cSXin Li        type=Path,
475*760c253cSXin Li        required=True,
476*760c253cSXin Li        help="""
477*760c253cSXin Li        Path to a ChromeOS tree. If not passed, one will be inferred. If none
478*760c253cSXin Li        can be inferred, this script will fail.
479*760c253cSXin Li        """,
480*760c253cSXin Li    )
481*760c253cSXin Li
482*760c253cSXin Li    # Parse command-line arguments.
483*760c253cSXin Li    args_output = parser.parse_args()
484*760c253cSXin Li
485*760c253cSXin Li    cur_llvm_version = args_output.llvm_version
486*760c253cSXin Li    chromeos_tree = args_output.chromeos_tree
487*760c253cSXin Li    if not chromeos_tree:
488*760c253cSXin Li        # Try to infer this unconditionally, so mishandling of this script can
489*760c253cSXin Li        # be more easily detected (which allows more flexibility in the
490*760c253cSXin Li        # implementation in the future for things outside of what directly
491*760c253cSXin Li        # needs this value).
492*760c253cSXin Li        chromeos_tree = chroot.FindChromeOSRootAbove(my_dir)
493*760c253cSXin Li
494*760c253cSXin Li    new_llvm_hash = LLVMHash()
495*760c253cSXin Li    if isinstance(cur_llvm_version, int):
496*760c253cSXin Li        # Find the git hash of the specific LLVM version.
497*760c253cSXin Li        print(new_llvm_hash.GetLLVMHash(cur_llvm_version))
498*760c253cSXin Li    elif cur_llvm_version == "llvm":
499*760c253cSXin Li        print(new_llvm_hash.GetCrOSCurrentLLVMHash(chromeos_tree))
500*760c253cSXin Li    elif cur_llvm_version == "llvm-next":
501*760c253cSXin Li        print(new_llvm_hash.GetCrOSLLVMNextHash())
502*760c253cSXin Li    elif cur_llvm_version == "google3":
503*760c253cSXin Li        print(new_llvm_hash.GetGoogle3LLVMHash())
504*760c253cSXin Li    elif cur_llvm_version == "google3-unstable":
505*760c253cSXin Li        print(new_llvm_hash.GetGoogle3UnstableLLVMHash())
506*760c253cSXin Li    else:
507*760c253cSXin Li        assert cur_llvm_version == "tot"
508*760c253cSXin Li        print(new_llvm_hash.GetTopOfTrunkGitHash())
509*760c253cSXin Li
510*760c253cSXin Li
511*760c253cSXin Liif __name__ == "__main__":
512*760c253cSXin Li    main()
513