| #! /usr/bin/env python |
| # Copyright 2020 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """Updates the chromium binaries used by devil. |
| |
| This currently must be called from the top-level chromium src directory. |
| """ |
| |
| import argparse |
| import collections |
| import json |
| import logging |
| import os |
| import sys |
| |
| _DEVIL_ROOT_DIR = os.path.abspath( |
| os.path.join(os.path.dirname(__file__), '..', '..')) |
| |
| sys.path.append(_DEVIL_ROOT_DIR) |
| from devil import base_error |
| from devil import devil_env |
| from devil.utils import cmd_helper |
| |
| _DEVICE_ARCHS = [ |
| { |
| 'cpu': 'arm', |
| 'platform': 'android_armeabi-v7a', |
| }, |
| { |
| 'cpu': 'arm64', |
| 'platform': 'android_arm64-v8a', |
| }, |
| { |
| 'cpu': 'x86', |
| 'platform': 'android_x86', |
| }, |
| { |
| 'cpu': 'x64', |
| 'platform': 'android_x86_64', |
| }, |
| ] |
| _HOST_ARCH = [{ |
| # Host binaries use x86_64, not arm, but they build with the |
| # host toolchain within a target_cpu="arm" build. |
| 'cpu': 'arm', |
| 'platform': 'linux2_x86_64', |
| }] |
| |
| _CHROMIUM_DEPS = { |
| 'chromium_commands': { |
| 'archs': _HOST_ARCH, |
| 'build_path': 'lib.java/chromium_commands.dex.jar', |
| 'target_name': 'chromium_commands_java', |
| }, |
| 'forwarder_device': { |
| 'archs': _DEVICE_ARCHS, |
| 'build_path': 'device_forwarder', |
| 'target_name': 'forwarder2', |
| }, |
| 'forwarder_host': { |
| 'archs': _HOST_ARCH, |
| 'build_path': 'clang_x64/host_forwarder', |
| 'target_name': 'forwarder2', |
| }, |
| 'md5sum_device': { |
| 'archs': _DEVICE_ARCHS, |
| 'build_path': 'md5sum_bin', |
| 'target_name': 'md5sum', |
| }, |
| 'md5sum_host': { |
| 'archs': _HOST_ARCH, |
| 'build_path': 'clang_x64/md5sum_bin', |
| 'target_name': 'md5sum', |
| }, |
| } |
| |
| |
| def BuildTargetsForCpu(targets, cpu, output_dir): |
| logging.info('Building %s', cpu) |
| |
| gn_args = [ |
| 'ffmpeg_branding="Chrome"', |
| 'is_component_build=false', |
| 'is_debug=false', |
| 'proprietary_codecs=true', |
| 'symbol_level=1', |
| 'target_cpu="%s"' % cpu, |
| 'target_os="android"', |
| 'use_goma=true', |
| ] |
| |
| cmd = ['gn', 'gen', '--args=%s' % (' '.join(gn_args)), output_dir] |
| ec = cmd_helper.RunCmd(cmd) |
| if ec: |
| raise base_error.BaseError('%s failed with %d' % (cmd, ec)) |
| |
| ec = cmd_helper.RunCmd(['autoninja', '-C', output_dir] + targets) |
| if ec: |
| raise base_error.BaseError('building %s failed with %d' % (cpu, ec)) |
| |
| |
| def UpdateDependency(dependency_name, dependency_info, local_path, platform): |
| bucket = dependency_info['cloud_storage_bucket'] |
| folder = dependency_info['cloud_storage_base_folder'] |
| |
| # determine the hash |
| ec, sha1sum_output = cmd_helper.GetCmdStatusAndOutput(['sha1sum', local_path]) |
| if ec: |
| raise base_error.BaseError( |
| 'Failed to determine SHA1 for %s: %s' % (local_path, sha1sum_output)) |
| |
| dependency_sha1 = sha1sum_output.split()[0] |
| |
| # upload |
| remote_path = '%s_%s' % (dependency_name, dependency_sha1) |
| gs_dest = 'gs://%s/%s/%s' % (bucket, folder, remote_path) |
| ec, gsutil_output = cmd_helper.GetCmdStatusAndOutput( |
| ['gsutil.py', 'cp', local_path, gs_dest]) |
| if ec: |
| raise base_error.BaseError( |
| 'Failed to upload %s to %s: %s' % (remote_path, gs_dest, gsutil_output)) |
| |
| # update entry in json |
| file_info = dependency_info['file_info'] |
| if platform not in file_info: |
| file_info[platform] = { |
| 'cloud_storage_hash': '', |
| # the user will need to manually update the download path after |
| # uploading a previously unknown dependency. |
| 'download_path': 'FIXME', |
| } |
| file_info[platform]['cloud_storage_hash'] = dependency_sha1 |
| |
| |
| def UpdateChromiumDependencies(dependencies, args): |
| deps_by_platform = collections.defaultdict(list) |
| for dep_name, dep_info in _CHROMIUM_DEPS.iteritems(): |
| archs = dep_info.get('archs', []) |
| for a in archs: |
| deps_by_platform[(a.get('cpu'), a.get('platform'))].append( |
| (dep_name, dep_info.get('build_path'), dep_info.get('target_name'))) |
| |
| for arch, arch_deps in deps_by_platform.iteritems(): |
| targets = [target_name for _n, _b, target_name in arch_deps] |
| cpu, platform = arch |
| output_dir = os.path.join(args.chromium_src_dir, 'out-devil-deps', platform) |
| BuildTargetsForCpu(targets, cpu, output_dir) |
| |
| for dep_name, build_path, _ in arch_deps: |
| local_path = os.path.abspath(os.path.join(output_dir, build_path)) |
| UpdateDependency(dep_name, |
| dependencies.get('dependencies', {}).get(dep_name, {}), |
| local_path, platform) |
| |
| return dependencies |
| |
| |
| def UpdateGivenDependency(dependencies, args): |
| dep_name = args.name or os.path.basename(args.path) |
| if not dep_name in dependencies.get('dependencies', {}): |
| raise base_error.BaseError('Could not find dependency "%s" in %s' % |
| (dep_name, args.dependencies_json)) |
| |
| UpdateDependency(dep_name, |
| dependencies.get('dependencies', {}).get(dep_name, {}), |
| args.path, args.platform) |
| |
| return dependencies |
| |
| |
| def main(raw_args): |
| parser = argparse.ArgumentParser(description=__doc__) |
| |
| # pylint: disable=protected-access |
| parser.add_argument( |
| '--dependencies-json', |
| type=os.path.abspath, |
| default=devil_env._DEVIL_DEFAULT_CONFIG, |
| help='Binary dependency configuration file to update.') |
| # pylint: enable=protected-access |
| |
| subparsers = parser.add_subparsers() |
| chromium_parser = subparsers.add_parser('chromium') |
| chromium_parser.add_argument( |
| '--chromium-src-dir', |
| type=os.path.realpath, |
| default=os.getcwd(), |
| help='Path to chromium/src checkout root.') |
| chromium_parser.set_defaults(update_dependencies=UpdateChromiumDependencies) |
| |
| dependency_parser = subparsers.add_parser('dependency') |
| dependency_parser.add_argument('--name', help='Name of dependency to update.') |
| dependency_parser.add_argument( |
| '--path', |
| type=os.path.abspath, |
| help='Path to file to upload as new version of dependency.') |
| dependency_parser.add_argument( |
| '--platform', help='Platform of dependency to update.') |
| dependency_parser.set_defaults(update_dependencies=UpdateGivenDependency) |
| |
| args = parser.parse_args(raw_args) |
| |
| logging.getLogger().setLevel(logging.INFO) |
| |
| with open(args.dependencies_json) as f: |
| dependencies = json.load(f) |
| |
| dependencies = args.update_dependencies(dependencies, args) |
| |
| with open(args.dependencies_json, 'w') as f: |
| json.dump(dependencies, f, indent=2, separators=(',', ': '), sort_keys=True) |
| |
| return 0 |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main(sys.argv[1:])) |