blob: be05c9eb24577652f7683aa51e537a6aafabbb5b [file] [log] [blame]
#!/usr/bin/env python
#
# Copyright (C) 2017 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# pylint: disable=not-callable, relative-import
"""Update the prebuilt clang from the build server."""
import argparse
import inspect
import logging
import os
import shutil
import subprocess
import sys
import utils
BRANCH = 'aosp-llvm-toolchain'
def logger():
"""Returns the module level logger."""
return logging.getLogger(__name__)
def unchecked_call(cmd, *args, **kwargs):
"""subprocess.call with logging."""
logger().info('unchecked_call: %s', subprocess.list2cmdline(cmd))
return subprocess.call(cmd, *args, **kwargs)
def check_call(cmd, *args, **kwargs):
"""subprocess.check_call with logging."""
logger().info('check_call: %s', subprocess.list2cmdline(cmd))
subprocess.check_call(cmd, *args, **kwargs)
class ArgParser(argparse.ArgumentParser):
def __init__(self):
super(ArgParser, self).__init__(
description=inspect.getdoc(sys.modules[__name__]))
self.add_argument(
'build', metavar='BUILD',
help='Build number to pull from the build server.')
self.add_argument(
'-b', '--bug', type=int,
help='Bug to reference in commit message.')
self.add_argument(
'--use-current-branch', action='store_true',
help='Do not repo start a new branch for the update.')
self.add_argument(
'--skip-fetch',
'-sf',
action='store_true',
default=False,
help='Skip the fetch, and only do the extraction step')
self.add_argument(
'--skip-cleanup',
'-sc',
action='store_true',
default=False,
help='Skip the cleanup, and leave intermediate files')
def fetch_artifact(branch, target, build, pattern):
fetch_artifact_path = '/google/data/ro/projects/android/fetch_artifact'
cmd = [fetch_artifact_path, '--branch={}'.format(branch),
'--target={}'.format(target), '--bid={}'.format(build), pattern]
check_call(cmd)
def extract_package(package, install_dir):
cmd = ['tar', 'xf', package, '-C', install_dir]
check_call(cmd)
def extract_clang_info(clang_dir):
version_file_path = os.path.join(clang_dir, 'AndroidVersion.txt')
with open(version_file_path) as version_file:
# e.g. for contents: ['7.0.1', 'based on r326829']
contents = [l.strip() for l in version_file.readlines()]
version = contents[0]
revision = contents[1].split()[-1]
return version, revision
def symlink_to_linux_resource_dir(install_dir):
# Assume we're in a Darwin (non-linux) prebuilt dir. Find the Clang version
# string. Pick the longest string, if there's more than one.
version_dirs = os.listdir(os.path.join(install_dir, 'lib64', 'clang'))
if len(version_dirs) != 0:
version_dirs.sort(key=len)
version_dir = version_dirs[-1]
symlink_dir = os.path.join(install_dir, 'lib64', 'clang', version_dir, 'lib')
link_src = os.path.join('/'.join(['..'] * 6), 'linux-x86', symlink_dir, 'linux')
link_dst = 'linux'
# 'cd' to symlink_dir and create a symlink from link_dst to link_src
prebuilt_dir = os.getcwd()
os.chdir(symlink_dir)
os.symlink(link_src, link_dst)
os.chdir(prebuilt_dir)
def update_clang(host, build_number, use_current_branch, download_dir, bug,
manifest):
prebuilt_dir = utils.android_path('prebuilts/clang/host', host)
os.chdir(prebuilt_dir)
if not use_current_branch:
branch_name = 'update-clang-{}'.format(build_number)
unchecked_call(
['repo', 'abandon', branch_name, '.'])
check_call(
['repo', 'start', branch_name, '.'])
host_filename = host
# Get the correct host file for Windows. The git project names are
# different than the package created by build.py.
if host == 'windows-x86':
host_filename = 'windows-x86-64'
elif host == 'windows-x86_32':
host_filename = 'windows-x86'
package = '{}/clang-{}-{}.tar.bz2'.format(
download_dir, build_number, host_filename)
manifest_file = '{}/{}'.format(download_dir, manifest)
extract_package(package, prebuilt_dir)
extract_subdir = 'clang-' + build_number
clang_version, svn_revision = extract_clang_info(extract_subdir)
# Install into clang-<svn_revision>. Suffixes ('a', 'b', 'c' etc.), if any,
# are included in the svn_revision.
install_subdir = 'clang-' + svn_revision
os.rename(extract_subdir, install_subdir)
# Some platform tests (e.g. system/bt/profile/sdp) build directly with
# coverage instrumentation and rely on the driver to pick the correct
# profile runtime. Symlink the Linux resource dir from the Linux toolchain
# into the Darwin toolchain so the runtime is found by the Darwin Clang
# driver.
if host == 'darwin-x86':
symlink_to_linux_resource_dir(install_subdir)
shutil.copy(manifest_file, prebuilt_dir + '/' + install_subdir)
check_call(['git', 'add', install_subdir])
# If there is no difference with the new files, we are already done.
diff = unchecked_call(['git', 'diff', '--cached', '--quiet'])
if diff == 0:
logger().info('Bypassed commit with no diff')
return
message_lines = [
'Update prebuilt Clang to {}.'.format(svn_revision),
'', 'clang {} (based on {}) from build {}.'.format(
clang_version, svn_revision, build_number)
]
if bug is not None:
message_lines.append('')
message_lines.append('Bug: http://b/{}'.format(bug))
message_lines.append('Test: N/A')
message = '\n'.join(message_lines)
check_call(['git', 'commit', '-m', message])
def main():
args = ArgParser().parse_args()
logging.basicConfig(level=logging.INFO)
do_fetch = not args.skip_fetch
do_cleanup = not args.skip_cleanup
download_dir = os.path.realpath('.download')
if do_fetch:
if os.path.isdir(download_dir):
shutil.rmtree(download_dir)
os.makedirs(download_dir)
os.chdir(download_dir)
targets = ['linux', 'darwin_mac', 'windows_x86_64', 'windows_x86']
hosts = ['darwin-x86', 'linux-x86', 'windows-x86', 'windows-x86_32']
clang_pattern = 'clang-*.tar.bz2'
manifest = 'manifest_{}.xml'.format(args.build)
branch = 'aosp-llvm-toolchain'
try:
if do_fetch:
fetch_artifact(branch, targets[0], args.build, manifest)
for target in targets:
fetch_artifact(branch, target, args.build, clang_pattern)
for host in hosts:
update_clang(host, args.build, args.use_current_branch,
download_dir, args.bug, manifest)
finally:
if do_cleanup:
shutil.rmtree(download_dir)
return 0
if __name__ == '__main__':
main()