339 lines
9.8 KiB
Python
Executable File
339 lines
9.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
# Copyright 2019 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.
|
|
|
|
"""Returns the latest LLVM version's hash."""
|
|
|
|
from __future__ import print_function
|
|
|
|
import argparse
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from contextlib import contextmanager
|
|
|
|
import git_llvm_rev
|
|
from subprocess_helpers import CheckCommand
|
|
from subprocess_helpers import check_output
|
|
|
|
_LLVM_GIT_URL = ('https://chromium.googlesource.com/external/github.com/llvm'
|
|
'/llvm-project')
|
|
|
|
KNOWN_HASH_SOURCES = {'google3', 'google3-unstable', 'tot'}
|
|
|
|
|
|
def GetVersionFrom(src_dir, git_hash):
|
|
"""Obtain an SVN-style version number based on the LLVM git hash passed in.
|
|
|
|
Args:
|
|
src_dir: LLVM's source directory.
|
|
git_hash: The git hash.
|
|
|
|
Returns:
|
|
An SVN-style version number associated with the git hash.
|
|
"""
|
|
|
|
version = git_llvm_rev.translate_sha_to_rev(
|
|
git_llvm_rev.LLVMConfig(remote='origin', dir=src_dir), git_hash)
|
|
# Note: branches aren't supported
|
|
assert version.branch == git_llvm_rev.MAIN_BRANCH, version.branch
|
|
return version.number
|
|
|
|
|
|
def GetGitHashFrom(src_dir, version):
|
|
"""Finds the commit hash(es) of the LLVM version in the git log history.
|
|
|
|
Args:
|
|
src_dir: The LLVM source tree.
|
|
version: The version number.
|
|
|
|
Returns:
|
|
A git hash string corresponding to the version number.
|
|
|
|
Raises:
|
|
subprocess.CalledProcessError: Failed to find a git hash.
|
|
"""
|
|
|
|
return git_llvm_rev.translate_rev_to_sha(
|
|
git_llvm_rev.LLVMConfig(remote='origin', dir=src_dir),
|
|
git_llvm_rev.Rev(branch=git_llvm_rev.MAIN_BRANCH, number=version))
|
|
|
|
|
|
@contextmanager
|
|
def CreateTempLLVMRepo(temp_dir):
|
|
"""Adds a LLVM worktree to 'temp_dir'.
|
|
|
|
Creating a worktree because the LLVM source tree in
|
|
'../toolchain-utils/llvm_tools/llvm-project-copy' should not be modified.
|
|
|
|
This is useful for applying patches to a source tree but do not want to modify
|
|
the actual LLVM source tree in 'llvm-project-copy'.
|
|
|
|
Args:
|
|
temp_dir: An absolute path to the temporary directory to put the worktree in
|
|
(obtained via 'tempfile.mkdtemp()').
|
|
|
|
Returns:
|
|
The absolute path to 'temp_dir'.
|
|
|
|
Raises:
|
|
subprocess.CalledProcessError: Failed to remove the worktree.
|
|
ValueError: Failed to add a worktree.
|
|
"""
|
|
|
|
abs_path_to_llvm_project_dir = GetAndUpdateLLVMProjectInLLVMTools()
|
|
CheckCommand([
|
|
'git', '-C', abs_path_to_llvm_project_dir, 'worktree', 'add', '--detach',
|
|
temp_dir, git_llvm_rev.MAIN_BRANCH
|
|
])
|
|
|
|
try:
|
|
yield temp_dir
|
|
finally:
|
|
if os.path.isdir(temp_dir):
|
|
check_output([
|
|
'git', '-C', abs_path_to_llvm_project_dir, 'worktree', 'remove', '-f',
|
|
temp_dir
|
|
])
|
|
|
|
|
|
def GetAndUpdateLLVMProjectInLLVMTools():
|
|
"""Gets the absolute path to 'llvm-project-copy' directory in 'llvm_tools'.
|
|
|
|
The intent of this function is to avoid cloning the LLVM repo and then
|
|
discarding the contents of the repo. The function will create a directory
|
|
in '../toolchain-utils/llvm_tools' called 'llvm-project-copy' if this
|
|
directory does not exist yet. If it does not exist, then it will use the
|
|
LLVMHash() class to clone the LLVM repo into 'llvm-project-copy'. Otherwise,
|
|
it will clean the contents of that directory and then fetch from the chromium
|
|
LLVM mirror. In either case, this function will return the absolute path to
|
|
'llvm-project-copy' directory.
|
|
|
|
Raises:
|
|
ValueError: LLVM repo (in 'llvm-project-copy' dir.) has changes or failed to
|
|
checkout to main or failed to fetch from chromium mirror of LLVM.
|
|
"""
|
|
|
|
abs_path_to_llvm_tools_dir = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
abs_path_to_llvm_project_dir = os.path.join(abs_path_to_llvm_tools_dir,
|
|
'llvm-project-copy')
|
|
|
|
if not os.path.isdir(abs_path_to_llvm_project_dir):
|
|
print(
|
|
'Checking out LLVM from scratch. This could take a while...\n'
|
|
'(This should only need to be done once, though.)',
|
|
file=sys.stderr)
|
|
os.mkdir(abs_path_to_llvm_project_dir)
|
|
|
|
LLVMHash().CloneLLVMRepo(abs_path_to_llvm_project_dir)
|
|
else:
|
|
# `git status` has a '-s'/'--short' option that shortens the output.
|
|
# With the '-s' option, if no changes were made to the LLVM repo, then the
|
|
# output (assigned to 'repo_status') would be empty.
|
|
repo_status = check_output(
|
|
['git', '-C', abs_path_to_llvm_project_dir, 'status', '-s'])
|
|
|
|
if repo_status.rstrip():
|
|
raise ValueError('LLVM repo in %s has changes, please remove.' %
|
|
abs_path_to_llvm_project_dir)
|
|
|
|
CheckCommand([
|
|
'git', '-C', abs_path_to_llvm_project_dir, 'checkout',
|
|
git_llvm_rev.MAIN_BRANCH
|
|
])
|
|
CheckCommand(['git', '-C', abs_path_to_llvm_project_dir, 'pull'])
|
|
|
|
return abs_path_to_llvm_project_dir
|
|
|
|
|
|
def GetGoogle3LLVMVersion(stable):
|
|
"""Gets the latest google3 LLVM version.
|
|
|
|
Returns:
|
|
The latest LLVM SVN version as an integer.
|
|
|
|
Raises:
|
|
subprocess.CalledProcessError: An invalid path has been provided to the
|
|
`cat` command.
|
|
"""
|
|
|
|
subdir = 'stable' if stable else 'llvm_unstable'
|
|
|
|
# Cmd to get latest google3 LLVM version.
|
|
cmd = [
|
|
'cat',
|
|
os.path.join('/google/src/head/depot/google3/third_party/crosstool/v18',
|
|
subdir, 'installs/llvm/git_origin_rev_id')
|
|
]
|
|
|
|
# Get latest version.
|
|
git_hash = check_output(cmd)
|
|
|
|
# Change type to an integer
|
|
return GetVersionFrom(GetAndUpdateLLVMProjectInLLVMTools(), git_hash.rstrip())
|
|
|
|
|
|
def is_svn_option(svn_option):
|
|
"""Validates whether the argument (string) is a git hash option.
|
|
|
|
The argument is used to find the git hash of LLVM.
|
|
|
|
Args:
|
|
svn_option: The option passed in as a command line argument.
|
|
|
|
Raises:
|
|
ValueError: Invalid svn option provided.
|
|
"""
|
|
|
|
if svn_option.lower() in KNOWN_HASH_SOURCES:
|
|
return svn_option.lower()
|
|
|
|
try:
|
|
svn_version = int(svn_option)
|
|
|
|
return svn_version
|
|
|
|
# Unable to convert argument to an int, so the option is invalid.
|
|
#
|
|
# Ex: 'one'.
|
|
except ValueError:
|
|
pass
|
|
|
|
raise ValueError('Invalid LLVM git hash option provided: %s' % svn_option)
|
|
|
|
|
|
def GetLLVMHashAndVersionFromSVNOption(svn_option):
|
|
"""Gets the LLVM hash and LLVM version based off of the svn option.
|
|
|
|
Args:
|
|
svn_option: A valid svn option obtained from the command line.
|
|
Ex: 'google3', 'tot', or <svn_version> such as 365123.
|
|
|
|
Returns:
|
|
A tuple that is the LLVM git hash and LLVM version.
|
|
"""
|
|
|
|
new_llvm_hash = LLVMHash()
|
|
|
|
# Determine which LLVM git hash to retrieve.
|
|
if svn_option == 'tot':
|
|
git_hash = new_llvm_hash.GetTopOfTrunkGitHash()
|
|
version = GetVersionFrom(GetAndUpdateLLVMProjectInLLVMTools(), git_hash)
|
|
elif isinstance(svn_option, int):
|
|
version = svn_option
|
|
git_hash = GetGitHashFrom(GetAndUpdateLLVMProjectInLLVMTools(), version)
|
|
else:
|
|
assert svn_option in ('google3', 'google3-unstable')
|
|
version = GetGoogle3LLVMVersion(stable=svn_option == 'google3')
|
|
|
|
git_hash = GetGitHashFrom(GetAndUpdateLLVMProjectInLLVMTools(), version)
|
|
|
|
return git_hash, version
|
|
|
|
|
|
class LLVMHash(object):
|
|
"""Provides methods to retrieve a LLVM hash."""
|
|
|
|
@staticmethod
|
|
@contextmanager
|
|
def CreateTempDirectory():
|
|
temp_dir = tempfile.mkdtemp()
|
|
|
|
try:
|
|
yield temp_dir
|
|
finally:
|
|
if os.path.isdir(temp_dir):
|
|
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
|
|
def CloneLLVMRepo(self, temp_dir):
|
|
"""Clones the LLVM repo.
|
|
|
|
Args:
|
|
temp_dir: The temporary directory to clone the repo to.
|
|
|
|
Raises:
|
|
ValueError: Failed to clone the LLVM repo.
|
|
"""
|
|
|
|
clone_cmd = ['git', 'clone', _LLVM_GIT_URL, temp_dir]
|
|
|
|
clone_cmd_obj = subprocess.Popen(clone_cmd, stderr=subprocess.PIPE)
|
|
_, stderr = clone_cmd_obj.communicate()
|
|
|
|
if clone_cmd_obj.returncode:
|
|
raise ValueError('Failed to clone the LLVM repo: %s' % stderr)
|
|
|
|
def GetLLVMHash(self, version):
|
|
"""Retrieves the LLVM hash corresponding to the LLVM version passed in.
|
|
|
|
Args:
|
|
version: The LLVM version to use as a delimiter.
|
|
|
|
Returns:
|
|
The hash as a string that corresponds to the LLVM version.
|
|
"""
|
|
|
|
hash_value = GetGitHashFrom(GetAndUpdateLLVMProjectInLLVMTools(), version)
|
|
return hash_value
|
|
|
|
def GetGoogle3LLVMHash(self):
|
|
"""Retrieves the google3 LLVM hash."""
|
|
|
|
return self.GetLLVMHash(GetGoogle3LLVMVersion(stable=True))
|
|
|
|
def GetGoogle3UnstableLLVMHash(self):
|
|
"""Retrieves the LLVM hash of google3's unstable compiler."""
|
|
return self.GetLLVMHash(GetGoogle3LLVMVersion(stable=False))
|
|
|
|
def GetTopOfTrunkGitHash(self):
|
|
"""Gets the latest git hash from top of trunk of LLVM."""
|
|
|
|
path_to_main_branch = 'refs/heads/main'
|
|
llvm_tot_git_hash = check_output(
|
|
['git', 'ls-remote', _LLVM_GIT_URL, path_to_main_branch])
|
|
return llvm_tot_git_hash.rstrip().split()[0]
|
|
|
|
|
|
def main():
|
|
"""Prints the git hash of LLVM.
|
|
|
|
Parses the command line for the optional command line
|
|
arguments.
|
|
"""
|
|
|
|
# Create parser and add optional command-line arguments.
|
|
parser = argparse.ArgumentParser(description='Finds the LLVM hash.')
|
|
parser.add_argument(
|
|
'--llvm_version',
|
|
type=is_svn_option,
|
|
required=True,
|
|
help='which git hash of LLVM to find. Either a svn revision, or one '
|
|
'of %s' % sorted(KNOWN_HASH_SOURCES))
|
|
|
|
# Parse command-line arguments.
|
|
args_output = parser.parse_args()
|
|
|
|
cur_llvm_version = args_output.llvm_version
|
|
|
|
new_llvm_hash = LLVMHash()
|
|
|
|
if isinstance(cur_llvm_version, int):
|
|
# Find the git hash of the specific LLVM version.
|
|
print(new_llvm_hash.GetLLVMHash(cur_llvm_version))
|
|
elif cur_llvm_version == 'google3':
|
|
print(new_llvm_hash.GetGoogle3LLVMHash())
|
|
elif cur_llvm_version == 'google3-unstable':
|
|
print(new_llvm_hash.GetGoogle3UnstableLLVMHash())
|
|
else:
|
|
assert cur_llvm_version == 'tot'
|
|
print(new_llvm_hash.GetTopOfTrunkGitHash())
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|