910e62b5创建于 1月15日历史提交
#!/usr/bin/env python3

# Copyright 2020 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""A script to generate build.gradle from template and run fetch_all.py

More specifically, to generate build.gradle:
  - It downloads the index page for the latest androidx snapshot from
    https://androidx.dev/snapshots/latest/artifacts
  - It replaces {{androidx_repository_url}} with the URL for the latest snapshot
  - For each dependency, it looks up the version in the index page's HTML and
    substitutes the version into {{androidx_dependency_version}}.

Extra args after -- are passed directly to fetch_all.py.
"""

import argparse
import contextlib
import json
import logging
import os
import pathlib
import re
import shutil
import sys
import subprocess
import tempfile
from urllib import request
import zipfile

_SRC_PATH = pathlib.Path(__file__).resolve().parents[2]
_ANDROIDX_PATH = _SRC_PATH / 'third_party/androidx'
_CIPD_PATH = _ANDROIDX_PATH / 'cipd'
_BOM_NAME = 'bill_of_materials.json'
_EXTRACT_SCRIPT_PATH = _ANDROIDX_PATH / 'extract_and_commit_extras.py'

sys.path.insert(1, str(_SRC_PATH / 'build/autoroll'))
import fetch_util

# URL to artifacts in latest androidx snapshot.
_ANDROIDX_LATEST_SNAPSHOT_ARTIFACTS_URL = 'https://androidx.dev/snapshots/latest/artifacts'

# When androidx roller is breaking, and a fix is not imminent, use this to pin a
# broken library to an old known-working version.
# * Find working versions from prior androidx roll commit descriptions.
# * The first element of each tuple is the path to the artifact of the latest
#   version of the library. It could change if the version is rev'ed in a new
#   snapshot.
# * The second element is a URL to replace the file with. Find the URL for older
#   versions of libraries by looking through the artifacts for the older version
#   (e.g.: https://androidx.dev/snapshots/builds/8545498/artifacts)
_OVERRIDES = [
    # Example:
    #('androidx_core_core/core-1.9.0-SNAPSHOT.aar',
    # 'https://androidx.dev/snapshots/builds/8545498/artifacts/repository/'
    # 'androidx/core/core/1.8.0-SNAPSHOT/core-1.8.0-20220505.122105-1.aar'),
]

# Set this to the build_id to pin all libraries to a given version.
# Useful when pinning a single library would cause issues, but you do not want
# to pause the auto-roller because other teams want to add / remove libraries.
# Example: '8545498'
_LATEST_VERSION_OVERRIDE = ''


_FILES_TO_COMMIT = [
    'additional_readme_paths.json',
    'bill_of_materials.json',
    'BUILD.gn',
    'build.gradle',
]


def _get_latest_androidx_version():
    androidx_artifacts_response = request.urlopen(
        _ANDROIDX_LATEST_SNAPSHOT_ARTIFACTS_URL)
    # Get the versioned url from the redirect destination.
    androidx_artifacts_url = androidx_artifacts_response.url
    androidx_artifacts_response.close()
    logging.info('URL for the latest build info: %s', androidx_artifacts_url)
    # Strip '/repository' from pattern.
    resolved_snapshot_repository_url_pattern = (
        fetch_util.make_androidx_maven_url('([0-9]*)').rsplit('/', 1)[0])
    match = re.match(resolved_snapshot_repository_url_pattern,
                     androidx_artifacts_url)
    assert match is not None
    version = match.group(1)
    logging.info('Resolved latest androidx version to %s', version)
    return version


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('-v',
                        '--verbose',
                        dest='verbose_count',
                        default=0,
                        action='count',
                        help='Verbose level (multiple times for more)')
    parser.add_argument('--local-repo',
                        help='Path to a locally androidx maven repo to use '
                        'instead of fetching the latest.')
    parser.add_argument(
        '--local',
        action='store_true',
        help='If passed then we will run the extract_and_commit_extras.py '
        'script and will not try rolling to the latest snapshot but reprocess '
        'the project at the current androidx.dev snapshot.')
    parser.add_argument(
        '--use-bom',
        action='store_true',
        help='If passed then we will use the existing bill_of_materials.json '
        'instead of resolving the latest androidx (faster but might resolve '
        'incorrect versions if deps are added/removed).')
    args, extra_args = parser.parse_known_args()

    logging.basicConfig(
        level=logging.WARNING - 10 * args.verbose_count,
        format='%(levelname).1s %(relativeCreated)6d %(message)s')

    if args.local_repo:
        version = 'local'
        androidx_snapshot_repository_url = ('file://' +
                                            os.path.abspath(args.local_repo))
    else:
        if _LATEST_VERSION_OVERRIDE:
            version = _LATEST_VERSION_OVERRIDE
        elif args.local:
            version = fetch_util.get_current_androidx_version()
        else:
            version = _get_latest_androidx_version()

        androidx_snapshot_repository_url = (
            fetch_util.make_androidx_maven_url(version))
        # Prepend '0' to version to avoid conflicts with previous version format.
        version = 'cr-0' + version

    if args.use_bom:
        version_map_str = fetch_util.generate_version_map_str(_ANDROIDX_PATH /
                                                              _BOM_NAME)
    else:
        version_map_str = ''

    fetch_util.fill_template(
        _ANDROIDX_PATH / 'build.gradle.template',
        _ANDROIDX_PATH / 'build.gradle',
        version_overrides=version_map_str,
        androidx_repository_url=androidx_snapshot_repository_url)

    # Overrides do not work with local snapshots since the repository_url is
    # different.
    if not args.local_repo:
        for subpath, url in _OVERRIDES:
            extra_args += ['--override-artifact', f'{subpath}:{url}']

    os.makedirs(_CIPD_PATH, exist_ok=True)
    # gclient/cipd extract files as read only.
    subprocess.run(['chmod', '-R', '+w', _CIPD_PATH])

    fetch_util.run_fetch_all(android_deps_dir=_ANDROIDX_PATH,
                             output_subdir='cipd',
                             extra_args=extra_args,
                             verbose_count=args.verbose_count)

    version_map_str = fetch_util.generate_version_map_str(_CIPD_PATH /
                                                          _BOM_NAME)

    # Regenerate the build.gradle file filling in the the version map so that
    # runs of the main project do not have to revalutate androidx versions.
    fetch_util.fill_template(
        _ANDROIDX_PATH / 'build.gradle.template',
        _CIPD_PATH / 'build.gradle',
        version_overrides=version_map_str,
        androidx_repository_url=androidx_snapshot_repository_url)

    version_txt_path = os.path.join(_CIPD_PATH, 'VERSION.txt')
    with open(version_txt_path, 'w') as f:
        f.write(version)

    to_commit_zip_path = _CIPD_PATH / 'to_commit.zip'
    file_map = {f: f'third_party/androidx/{f}' for f in _FILES_TO_COMMIT}
    fetch_util.create_to_commit_zip(output_path=to_commit_zip_path,
                                    package_root=_CIPD_PATH,
                                    dirnames=['libs'],
                                    absolute_file_map=file_map)
    if args.local:
        subprocess.run([
            _EXTRACT_SCRIPT_PATH, '--cipd-package-path', _CIPD_PATH,
            '--no-git-add'
        ],
                       check=True)

    fetch_util.write_cipd_yaml(package_root=_CIPD_PATH,
                               package_name=fetch_util.ANDROIDX_CIPD_PACKAGE,
                               version=version,
                               output_path=_CIPD_PATH / 'cipd.yaml')

if __name__ == '__main__':
    main()