| #!/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()) |