| #!/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. | |
| """Packages prebuilts and SDK sources for GitHub releases. | |
| Usage: ./tools/release/package-github-release-artifacts v20.0 | |
| IMPORTANT: This script must be run from the git tag being packaged. The SDK | |
| source files are generated from the current working directory, so running this | |
| script from a different commit will result in mismatched SDK sources. | |
| This will generate: | |
| - One .zip file for every os-arch combo (e.g. android-arm.zip) | |
| - SDK source zips (perfetto-cpp-sdk-src.zip, perfetto-c-sdk-src.zip) | |
| All files will be placed into /tmp/perfetto-v20.0-github-release/ . | |
| """ | |
| import argparse | |
| import subprocess | |
| import os | |
| import sys | |
| import tempfile | |
| import shutil | |
| def exec(*args): | |
| print(' '.join(args)) | |
| subprocess.check_call(args) | |
| def get_repo_root(): | |
| """Returns the root directory of the Perfetto repository.""" | |
| script_dir = os.path.dirname(os.path.abspath(__file__)) | |
| return os.path.abspath(os.path.join(script_dir, '..', '..')) | |
| def verify_git_state(expected_version): | |
| """Verifies git is on the correct tag with no uncommitted changes.""" | |
| warnings = [] | |
| # Check for uncommitted changes | |
| try: | |
| result = subprocess.run(['git', 'status', '--porcelain'], | |
| capture_output=True, | |
| text=True) | |
| if result.returncode == 0 and result.stdout.strip(): | |
| warnings.append( | |
| f'Working directory has uncommitted changes:\n{result.stdout}') | |
| except Exception as e: | |
| warnings.append(f'Could not check git status: {e}') | |
| # Check current tag | |
| try: | |
| result = subprocess.run(['git', 'describe', '--exact-match', '--tags'], | |
| capture_output=True, | |
| text=True) | |
| if result.returncode == 0: | |
| current_tag = result.stdout.strip() | |
| if current_tag != expected_version: | |
| warnings.append( | |
| f'On tag {current_tag}, but packaging {expected_version}') | |
| else: | |
| warnings.append(f'Not on a git tag (expected {expected_version})') | |
| except Exception as e: | |
| warnings.append(f'Could not check git tag: {e}') | |
| if warnings: | |
| print('WARNING: SDK sources may not match the release tag:') | |
| for warning in warnings: | |
| print(f' - {warning}') | |
| return input('\nContinue anyway? [y/N] ').lower().strip() in ['y', 'yes'] | |
| print(f'✓ On tag {expected_version} with clean working directory') | |
| return True | |
| def generate_sdk_sources(tmpdir): | |
| """Generates SDK source zips using gen_amalgamated.""" | |
| repo_root = get_repo_root() | |
| gen_amalgamated = os.path.join(repo_root, 'tools', 'gen_amalgamated') | |
| sdk_zips = [] | |
| # Generate C++ SDK (default targets) | |
| print('\n--- Generating C++ SDK amalgamated sources ---') | |
| cpp_sdk_staging = tempfile.mkdtemp(prefix='perfetto-cpp-sdk-') | |
| try: | |
| exec('python3', gen_amalgamated, '--output', | |
| os.path.join(cpp_sdk_staging, 'perfetto')) | |
| os.chdir(tmpdir) | |
| exec('zip', '-9rj', 'perfetto-cpp-sdk-src.zip', cpp_sdk_staging) | |
| sdk_zips.append('perfetto-cpp-sdk-src.zip') | |
| print('C++ SDK source zip created: perfetto-cpp-sdk-src.zip') | |
| finally: | |
| shutil.rmtree(cpp_sdk_staging, ignore_errors=True) | |
| # Generate C SDK | |
| # TODO(lalitm): determine the correct target for C SDK | |
| print('\n--- Generating C SDK amalgamated sources ---') | |
| c_sdk_staging = tempfile.mkdtemp(prefix='perfetto-c-sdk-') | |
| try: | |
| # For now, use the same targets as C++ SDK | |
| # This should be updated with the correct C SDK target | |
| exec('python3', gen_amalgamated, '--output', | |
| os.path.join(c_sdk_staging, 'perfetto')) | |
| os.chdir(tmpdir) | |
| exec('zip', '-9rj', 'perfetto-c-sdk-src.zip', c_sdk_staging) | |
| sdk_zips.append('perfetto-c-sdk-src.zip') | |
| print('C SDK source zip created: perfetto-c-sdk-src.zip') | |
| finally: | |
| shutil.rmtree(c_sdk_staging, ignore_errors=True) | |
| return sdk_zips | |
| def main(): | |
| parser = argparse.ArgumentParser(epilog='Example: %s v19.0' % __file__) | |
| parser.add_argument('version', help='Version tag (e.g., v20.0)') | |
| args = parser.parse_args() | |
| # Verify we're on the correct tag with no uncommitted changes | |
| if not verify_git_state(args.version): | |
| print('Aborted.') | |
| return 1 | |
| tmpdir = '/tmp/perfetto-%s-github-release' % args.version | |
| src = 'gs://perfetto-luci-artifacts/%s/' % args.version | |
| os.makedirs(tmpdir, exist_ok=True) | |
| # Download and package prebuilts | |
| print('--- Downloading prebuilts from GCS ---') | |
| os.chdir(tmpdir) | |
| exec('gsutil', '-m', 'rsync', '-rc', src, tmpdir + '/') | |
| zips = [] | |
| for arch in os.listdir(tmpdir): | |
| if not os.path.isdir(arch): | |
| continue | |
| exec('zip', '-9r', '%s.zip' % arch, arch) | |
| zips.append(arch + '.zip') | |
| # Generate SDK source zips | |
| sdk_zips = generate_sdk_sources(tmpdir) | |
| zips.extend(sdk_zips) | |
| print('') | |
| print('=' * 70) | |
| print('%d zip files saved in %s' % (len(zips), tmpdir)) | |
| print('Prebuilt binaries: %d' % (len(zips) - len(sdk_zips))) | |
| print('SDK sources: %d' % len(sdk_zips)) | |
| print('Files: %s' % ', '.join(sorted(zips))) | |
| print('=' * 70) | |
| if __name__ == '__main__': | |
| sys.exit(main()) |