| #!/usr/bin/env python3 | |
| # Copyright (C) 2021 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. | |
| """Updates the python scripts in python/perfetto/prebuilts/manifests | |
| This script does the following, for each entry in MANIFESTS_TO_UPDATE: | |
| - Downloads the artifact by the LUCI infrastructure, one for each arch. | |
| - Computes the SHA-256 of each artifact. | |
| - Generates a manifest with URL, SHA-256 and other details. | |
| - Merges get_perfetto_prebuilt.py with the manifest and writes tools/xxx. | |
| This script is supposed to be run by Perfetto OWNERS after every monthly release | |
| after the LUCI jobs have completed. | |
| """ | |
| import argparse | |
| import hashlib | |
| import logging | |
| import os | |
| import subprocess | |
| import sys | |
| from concurrent.futures import ThreadPoolExecutor | |
| GCS_URL = 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts' | |
| ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| MANIFESTS_DIR = os.path.join(ROOT_DIR, 'python/perfetto/prebuilts/manifests') | |
| UNIX_ARCHS = [ | |
| 'mac-amd64', | |
| 'mac-arm64', | |
| 'linux-amd64', | |
| 'linux-arm', | |
| 'linux-arm64', | |
| 'android-arm', | |
| 'android-arm64', | |
| 'android-x86', | |
| 'android-x64', | |
| ] | |
| ALL_ARCHS = UNIX_ARCHS + ['windows-amd64'] | |
| MANIFESTS_TO_UPDATE = [ | |
| { | |
| 'tool': 'trace_processor_shell', | |
| 'archs': ALL_ARCHS | |
| }, | |
| { | |
| 'tool': 'traceconv', | |
| 'archs': ALL_ARCHS | |
| }, | |
| { | |
| 'tool': 'tracebox', | |
| 'archs': UNIX_ARCHS | |
| }, | |
| ] | |
| # Maps a 'os-arch' string (were arch follows LUCI conventions) into | |
| # corresponding tuples that match against python's platform / machine API | |
| # (see get_perfetto_prebuilt.py for usage). | |
| ARCH_TO_PYTHON = { | |
| 'mac-amd64': { | |
| 'platform': 'darwin', | |
| 'machine': ['x86_64'], | |
| }, | |
| 'mac-arm64': { | |
| 'platform': 'darwin', | |
| 'machine': ['arm64'], | |
| }, | |
| 'windows-amd64': { | |
| 'platform': 'win32', | |
| 'machine': ['amd64'], | |
| }, | |
| 'linux-amd64': { | |
| 'platform': 'linux', | |
| 'machine': ['x86_64'], | |
| }, | |
| 'linux-arm': { | |
| 'platform': 'linux', | |
| 'machine': ['armv6l', 'armv7l', 'armv8l'], | |
| }, | |
| 'linux-arm64': { | |
| 'platform': 'linux', | |
| 'machine': ['aarch64'], | |
| }, | |
| } | |
| def make_manifest(git_revision, tool, arch): | |
| ext = '.exe' if arch.startswith('windows') else '' | |
| file_name = tool + ext | |
| url = '%s/%s/%s/%s' % (GCS_URL, git_revision, arch, file_name) | |
| logging.info('Downloading %s', url) | |
| data = subprocess.check_output(['curl', '-fsL', '-o', '-', url]) | |
| manifest = { | |
| 'arch': arch, | |
| 'file_name': file_name, | |
| 'file_size': len(data), | |
| 'url': url, | |
| 'sha256': hashlib.sha256(data).hexdigest() | |
| } | |
| manifest.update(ARCH_TO_PYTHON.get(arch, {})) | |
| return manifest | |
| def update_manifest(git_revision, tool_name, archs): | |
| with ThreadPoolExecutor(max_workers=8) as executor: | |
| manifests = list( | |
| executor.map(lambda arch: make_manifest(git_revision, tool_name, arch), | |
| archs)) | |
| out_file = os.path.join(MANIFESTS_DIR, tool_name + '.py') | |
| content = '# This file has been generated by: {script} {git_revision}\n' | |
| content += '{tool_uppercase}_MANIFEST = {manifests}\n' | |
| content = content.format( | |
| script=os.path.relpath(__file__), | |
| tool_uppercase=tool_name.upper(), | |
| git_revision=git_revision, | |
| manifests=str(manifests)) | |
| with open(out_file + '.tmp', 'w') as f: | |
| f.write(content) | |
| os.rename(out_file + '.tmp', out_file) | |
| os.chmod(out_file, 0o755) | |
| def main(): | |
| usage = '%s v20.0 | 0a1b2c3d\n\n' % __file__ | |
| usage += 'To list available revisions run\n' | |
| usage += 'gsutil ls gs://perfetto-luci-artifacts/\n' | |
| usage += 'or visit https://chrome-infra-packages.appspot.com/p/perfetto\n' | |
| parser = argparse.ArgumentParser(usage=usage) | |
| parser.add_argument('version') | |
| args = parser.parse_args() | |
| git_revision = args.version | |
| for spec in MANIFESTS_TO_UPDATE: | |
| logging.info('Updating %s', spec['tool']) | |
| update_manifest(git_revision, spec['tool'], spec['archs']) | |
| if __name__ == '__main__': | |
| logging.basicConfig(level=logging.INFO) | |
| sys.exit(main()) |