xref: /aosp_15_r20/external/perfetto/tools/roll-prebuilts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1#!/usr/bin/env python3
2# Copyright (C) 2021 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15"""Updates the python scripts in python/perfetto/prebuilts/manifests
16
17This script does the following, for each entry in MANIFESTS_TO_UPDATE:
18  - Downloads the artifact by the LUCI infrastructure, one for each arch.
19  - Computes the SHA-256 of each artifact.
20  - Generates a manifest with URL, SHA-256 and other details.
21  - Merges get_perfetto_prebuilt.py with the manifest and writes tools/xxx.
22
23This script is supposed to be run by Perfetto OWNERS after every monthly release
24after the LUCI jobs have completed.
25"""
26
27import argparse
28import hashlib
29import logging
30import os
31import subprocess
32import sys
33
34from concurrent.futures import ThreadPoolExecutor
35
36GCS_URL = 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts'
37
38ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
39MANIFESTS_DIR = os.path.join(ROOT_DIR, 'python/perfetto/prebuilts/manifests')
40
41UNIX_ARCHS = [
42    'mac-amd64',
43    'mac-arm64',
44    'linux-amd64',
45    'linux-arm',
46    'linux-arm64',
47    'android-arm',
48    'android-arm64',
49    'android-x86',
50    'android-x64',
51]
52ALL_ARCHS = UNIX_ARCHS + ['windows-amd64']
53
54MANIFESTS_TO_UPDATE = [
55    {
56        'tool': 'trace_processor_shell',
57        'archs': ALL_ARCHS
58    },
59    {
60        'tool': 'traceconv',
61        'archs': ALL_ARCHS
62    },
63    {
64        'tool': 'tracebox',
65        'archs': UNIX_ARCHS
66    },
67]
68
69# Maps a 'os-arch' string (were arch follows LUCI conventions) into
70# corresponding tuples that match against python's platform / machine API
71# (see get_perfetto_prebuilt.py for usage).
72ARCH_TO_PYTHON = {
73    'mac-amd64': {
74        'platform': 'darwin',
75        'machine': ['x86_64'],
76    },
77    'mac-arm64': {
78        'platform': 'darwin',
79        'machine': ['arm64'],
80    },
81    'windows-amd64': {
82        'platform': 'win32',
83        'machine': ['amd64'],
84    },
85    'linux-amd64': {
86        'platform': 'linux',
87        'machine': ['x86_64'],
88    },
89    'linux-arm': {
90        'platform': 'linux',
91        'machine': ['armv6l', 'armv7l', 'armv8l'],
92    },
93    'linux-arm64': {
94        'platform': 'linux',
95        'machine': ['aarch64'],
96    },
97}
98
99
100def make_manifest(git_revision, tool, arch):
101  ext = '.exe' if arch.startswith('windows') else ''
102  file_name = tool + ext
103  url = '%s/%s/%s/%s' % (GCS_URL, git_revision, arch, file_name)
104  logging.info('Downloading %s', url)
105  data = subprocess.check_output(['curl', '-fsL', '-o', '-', url])
106  manifest = {
107      'arch': arch,
108      'file_name': file_name,
109      'file_size': len(data),
110      'url': url,
111      'sha256': hashlib.sha256(data).hexdigest()
112  }
113  manifest.update(ARCH_TO_PYTHON.get(arch, {}))
114  return manifest
115
116
117def update_manifest(git_revision, tool_name, archs):
118  with ThreadPoolExecutor(max_workers=8) as executor:
119    manifests = list(
120        executor.map(lambda arch: make_manifest(git_revision, tool_name, arch),
121                     archs))
122  out_file = os.path.join(MANIFESTS_DIR, tool_name + '.py')
123
124  content = '# This file has been generated by: {script} {git_revision}\n'
125  content += '{tool_uppercase}_MANIFEST = {manifests}\n'
126  content = content.format(
127      script=os.path.relpath(__file__),
128      tool_uppercase=tool_name.upper(),
129      git_revision=git_revision,
130      manifests=str(manifests))
131
132  with open(out_file + '.tmp', 'w') as f:
133    f.write(content)
134  subprocess.check_call(['yapf', '-i', out_file + '.tmp'])
135  os.rename(out_file + '.tmp', out_file)
136  os.chmod(out_file, 0o755)
137
138
139def main():
140  usage = '%s v20.0 | 0a1b2c3d\n\n' % __file__
141  usage += 'To list available revisions run\n'
142  usage += 'gsutil ls gs://perfetto-luci-artifacts/\n'
143  usage += 'or visit https://chrome-infra-packages.appspot.com/p/perfetto\n'
144  parser = argparse.ArgumentParser(usage=usage)
145  parser.add_argument('version')
146  args = parser.parse_args()
147
148  git_revision = args.version
149  for spec in MANIFESTS_TO_UPDATE:
150    logging.info('Updating %s', spec['tool'])
151    update_manifest(git_revision, spec['tool'], spec['archs'])
152
153
154if __name__ == '__main__':
155  logging.basicConfig(level=logging.INFO)
156  sys.exit(main())
157