Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | |
Avi Drissman | 3f1ea44 | 2022-09-08 15:42:01 | [diff] [blame] | 3 | # Copyright 2020 The Chromium Authors |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 4 | # Use of this source code is governed by a BSD-style license that can be |
| 5 | # found in the LICENSE file. |
| 6 | """A script to generate build.gradle from template and run fetch_all.py |
| 7 | |
| 8 | More specifically, to generate build.gradle: |
| 9 | - It downloads the BUILD_INFO file for the latest androidx snapshot from |
Haiyang Pan | ace50093 | 2022-01-26 19:49:03 | [diff] [blame] | 10 | https://androidx.dev/snapshots/builds |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 11 | - It replaces {{androidx_repository_url}} with the URL for the latest snapshot |
| 12 | - For each dependency, it looks up the version in the BUILD_INFO file and |
| 13 | substitutes the version into {{androidx_dependency_version}}. |
| 14 | """ |
| 15 | |
Haiyang Pan | ace50093 | 2022-01-26 19:49:03 | [diff] [blame] | 16 | import argparse |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 17 | import contextlib |
| 18 | import json |
Haiyang Pan | ace50093 | 2022-01-26 19:49:03 | [diff] [blame] | 19 | import logging |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 20 | import os |
Andrew Grieve | b5255af | 2024-06-06 17:03:18 | [diff] [blame^] | 21 | import pathlib |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 22 | import re |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 23 | import shutil |
Peter Kotwicz | a43e517 | 2021-01-26 22:05:05 | [diff] [blame] | 24 | import stat |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 25 | import subprocess |
| 26 | import tempfile |
Peter Kotwicz | 4676d06 | 2020-10-22 06:04:13 | [diff] [blame] | 27 | import urllib |
| 28 | from urllib import request |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 29 | |
| 30 | _ANDROIDX_PATH = os.path.normpath(os.path.join(__file__, '..')) |
| 31 | |
| 32 | _FETCH_ALL_PATH = os.path.normpath( |
| 33 | os.path.join(_ANDROIDX_PATH, '..', 'android_deps', 'fetch_all.py')) |
| 34 | |
| 35 | # URL to BUILD_INFO in latest androidx snapshot. |
Peter Wen | cb988a1 | 2022-06-23 18:04:38 | [diff] [blame] | 36 | _ANDROIDX_LATEST_SNAPSHOT_BUILD_INFO_URL = 'https://androidx.dev/snapshots/latest/artifacts/BUILD_INFO' |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 37 | |
Peter Kotwicz | 3fb0070e | 2021-02-11 07:37:35 | [diff] [blame] | 38 | # Snapshot repository URL with {{version}} placeholder. |
| 39 | _SNAPSHOT_REPOSITORY_URL = 'https://androidx.dev/snapshots/builds/{{version}}/artifacts/repository' |
| 40 | |
Andrew Grieve | 2515bcbd | 2022-05-06 03:14:07 | [diff] [blame] | 41 | # When androidx roller is breaking, and a fix is not immenent, use this to pin a |
| 42 | # broken library to an old known-working version. |
Andrew Grieve | a03217f | 2022-05-12 16:05:27 | [diff] [blame] | 43 | # * The first element of each tuple is the path to the artifact of the latest |
| 44 | # version of the library. It could change if the version is rev'ed in a new |
| 45 | # snapshot. |
| 46 | # * The second element is a URL to replace the file with. Find the URL for older |
| 47 | # versions of libraries by looking in the BUILD_INFO for the older version |
| 48 | # (e.g.: https://androidx.dev/snapshots/builds/8545498/artifacts/BUILD_INFO) |
Andrew Grieve | 2515bcbd | 2022-05-06 03:14:07 | [diff] [blame] | 49 | _OVERRIDES = [ |
Andrew Grieve | a03217f | 2022-05-12 16:05:27 | [diff] [blame] | 50 | # Example: |
Andrew Grieve | 2515bcbd | 2022-05-06 03:14:07 | [diff] [blame] | 51 | #('androidx_core_core/core-1.9.0-SNAPSHOT.aar', |
| 52 | # 'https://androidx.dev/snapshots/builds/8545498/artifacts/repository/' |
| 53 | # 'androidx/core/core/1.8.0-SNAPSHOT/core-1.8.0-20220505.122105-1.aar'), |
Samuel Huang | 4a63fe3 | 2023-03-07 22:09:02 | [diff] [blame] | 54 | ('androidx_recyclerview_recyclerview/recyclerview-1.4.0-SNAPSHOT.aar', |
| 55 | 'https://androidx.dev/snapshots/builds/9668027/artifacts/repository/' |
| 56 | 'androidx/recyclerview/recyclerview/1.4.0-SNAPSHOT/' |
| 57 | 'recyclerview-1.4.0-20230228.234124-1.aar'), |
Andrew Grieve | 2515bcbd | 2022-05-06 03:14:07 | [diff] [blame] | 58 | ] |
| 59 | |
Peter Kotwicz | 3fb0070e | 2021-02-11 07:37:35 | [diff] [blame] | 60 | |
| 61 | def _build_snapshot_repository_url(version): |
| 62 | return _SNAPSHOT_REPOSITORY_URL.replace('{{version}}', version) |
| 63 | |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 64 | |
Peter Kotwicz | a43e517 | 2021-01-26 22:05:05 | [diff] [blame] | 65 | def _delete_readonly_files(paths): |
| 66 | for path in paths: |
| 67 | if os.path.exists(path): |
| 68 | os.chmod(path, stat.S_IRUSR | stat.S_IRGRP | stat.S_IWUSR) |
| 69 | os.remove(path) |
| 70 | |
| 71 | |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 72 | def _parse_dir_list(dir_list): |
| 73 | """Computes 'library_group:library_name'->library_version mapping. |
| 74 | |
| 75 | Args: |
| 76 | dir_list: List of androidx library directories. |
| 77 | """ |
| 78 | dependency_version_map = dict() |
| 79 | for dir_entry in dir_list: |
| 80 | stripped_dir = dir_entry.strip() |
| 81 | if not stripped_dir.startswith('repository/androidx/'): |
| 82 | continue |
| 83 | dir_components = stripped_dir.split('/') |
| 84 | # Expected format: |
| 85 | # "repository/androidx/library_group/library_name/library_version/pom_or_jar" |
| 86 | if len(dir_components) < 6: |
| 87 | continue |
Peter Kotwicz | 4d3c3970 | 2021-03-25 21:48:25 | [diff] [blame] | 88 | dependency_package = 'androidx.' + '.'.join(dir_components[2:-3]) |
| 89 | dependency_module = '{}:{}'.format(dependency_package, |
| 90 | dir_components[-3]) |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 91 | if dependency_module not in dependency_version_map: |
Peter Kotwicz | 4d3c3970 | 2021-03-25 21:48:25 | [diff] [blame] | 92 | dependency_version_map[dependency_module] = dir_components[-2] |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 93 | return dependency_version_map |
| 94 | |
| 95 | |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 96 | @contextlib.contextmanager |
| 97 | def _build_dir(): |
| 98 | dirname = tempfile.mkdtemp() |
| 99 | try: |
| 100 | yield dirname |
| 101 | finally: |
| 102 | shutil.rmtree(dirname) |
| 103 | |
| 104 | |
| 105 | def _download_and_parse_build_info(): |
| 106 | """Downloads and parses BUILD_INFO file.""" |
| 107 | with _build_dir() as build_dir: |
Peter Kotwicz | 4676d06 | 2020-10-22 06:04:13 | [diff] [blame] | 108 | androidx_build_info_response = request.urlopen( |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 109 | _ANDROIDX_LATEST_SNAPSHOT_BUILD_INFO_URL) |
Haiyang Pan | ace50093 | 2022-01-26 19:49:03 | [diff] [blame] | 110 | androidx_build_info_url = androidx_build_info_response.geturl() |
| 111 | logging.info('URL for the latest build info: %s', |
| 112 | androidx_build_info_url) |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 113 | androidx_build_info_path = os.path.join(build_dir, 'BUILD_INFO') |
| 114 | with open(androidx_build_info_path, 'w') as f: |
Peter Kotwicz | 4676d06 | 2020-10-22 06:04:13 | [diff] [blame] | 115 | f.write(androidx_build_info_response.read().decode('utf-8')) |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 116 | |
Peter Kotwicz | 3fb0070e | 2021-02-11 07:37:35 | [diff] [blame] | 117 | # Strip '/repository' from pattern. |
| 118 | resolved_snapshot_repository_url_pattern = ( |
| 119 | _build_snapshot_repository_url('([0-9]*)').rsplit('/', 1)[0]) |
| 120 | |
| 121 | version = re.match(resolved_snapshot_repository_url_pattern, |
Haiyang Pan | ace50093 | 2022-01-26 19:49:03 | [diff] [blame] | 122 | androidx_build_info_url).group(1) |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 123 | |
| 124 | with open(androidx_build_info_path, 'r') as f: |
| 125 | build_info_dict = json.loads(f.read()) |
| 126 | dir_list = build_info_dict['target']['dir_list'] |
| 127 | |
Andrew Grieve | 10bbd55 | 2022-05-05 18:10:26 | [diff] [blame] | 128 | return dir_list, version |
| 129 | |
| 130 | |
| 131 | def _create_local_dir_list(repo_path): |
| 132 | repo_path = repo_path.rstrip('/') |
| 133 | prefix_len = len(repo_path) + 1 |
| 134 | ret = [] |
| 135 | for dirpath, _, filenames in os.walk(repo_path): |
| 136 | for name in filenames: |
| 137 | ret.append(os.path.join('repository', dirpath[prefix_len:], name)) |
| 138 | return ret |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 139 | |
| 140 | |
| 141 | def _process_build_gradle(dependency_version_map, androidx_repository_url): |
| 142 | """Generates build.gradle from template. |
| 143 | |
| 144 | Args: |
| 145 | dependency_version_map: An "dependency_group:dependency_name"->dependency_version mapping. |
| 146 | androidx_repository_url: URL of the maven repository. |
| 147 | """ |
Andrew Grieve | b5255af | 2024-06-06 17:03:18 | [diff] [blame^] | 148 | version_re = re.compile(r'\s*\w+ompile\s+[\'"]([^:]+:[^:]+):(.+?)[\'"]') |
| 149 | androidx_path = pathlib.Path(_ANDROIDX_PATH) |
| 150 | template_text = (androidx_path / 'build.gradle.template').read_text() |
| 151 | deps_with_custom_versions = set() |
| 152 | sb = [] |
| 153 | for line in template_text.splitlines(keepends=True): |
| 154 | line = line.replace('{{androidx_repository_url}}', |
| 155 | androidx_repository_url) |
| 156 | if m := version_re.search(line): |
| 157 | name, version = m.groups() |
| 158 | if version == '{{androidx_dependency_version}}': |
| 159 | new_version = dependency_version_map.get(name) |
| 160 | if version is None: |
| 161 | raise Exception(f'Version for {name} not found.') |
| 162 | line = line.replace(version, new_version) |
| 163 | else: |
| 164 | deps_with_custom_versions.add(name) |
| 165 | elif line.strip() == '{{version_overrides}}': |
| 166 | sb.append('versionOverrideMap = [:]\n') |
| 167 | for name, version in sorted(dependency_version_map.items()): |
| 168 | if name not in deps_with_custom_versions: |
| 169 | sb.append(f"versionOverrideMap['{name}'] = '{version}'\n") |
| 170 | deps_with_custom_versions = None |
| 171 | continue |
| 172 | |
| 173 | sb.append(line) |
| 174 | |
| 175 | # build.gradle is not deleted after script has finished running. The file is in |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 176 | # .gitignore and thus will be excluded from uploaded CLs. |
Andrew Grieve | b5255af | 2024-06-06 17:03:18 | [diff] [blame^] | 177 | (androidx_path / 'build.gradle').write_text(''.join(sb)) |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 178 | |
| 179 | |
Andrew Grieve | 9c7d4ac2f | 2022-08-24 17:38:59 | [diff] [blame] | 180 | def _write_cipd_yaml(libs_dir, version, cipd_yaml_path, experimental=False): |
Peter Kotwicz | a31339a | 2020-10-20 16:36:23 | [diff] [blame] | 181 | """Writes cipd.yaml file at the passed-in path.""" |
| 182 | |
| 183 | lib_dirs = os.listdir(libs_dir) |
| 184 | if not lib_dirs: |
| 185 | raise Exception('No generated libraries in {}'.format(libs_dir)) |
| 186 | |
Peter Kotwicz | 1ea22e1 | 2021-02-22 19:01:59 | [diff] [blame] | 187 | data_files = [ |
| 188 | 'BUILD.gn', 'VERSION.txt', 'additional_readme_paths.json', |
| 189 | 'build.gradle' |
| 190 | ] |
Peter Kotwicz | a31339a | 2020-10-20 16:36:23 | [diff] [blame] | 191 | for lib_dir in lib_dirs: |
| 192 | abs_lib_dir = os.path.join(libs_dir, lib_dir) |
Peter Kotwicz | 1770217 | 2020-10-29 21:43:02 | [diff] [blame] | 193 | androidx_rel_lib_dir = os.path.relpath(abs_lib_dir, _ANDROIDX_PATH) |
Peter Kotwicz | a31339a | 2020-10-20 16:36:23 | [diff] [blame] | 194 | if not os.path.isdir(abs_lib_dir): |
| 195 | continue |
| 196 | lib_files = os.listdir(abs_lib_dir) |
| 197 | if not 'cipd.yaml' in lib_files: |
| 198 | continue |
| 199 | |
Peter Kotwicz | a6b3b9a | 2021-01-28 21:40:12 | [diff] [blame] | 200 | for lib_file in lib_files: |
| 201 | if lib_file == 'cipd.yaml' or lib_file == 'OWNERS': |
| 202 | continue |
| 203 | data_files.append(os.path.join(androidx_rel_lib_dir, lib_file)) |
Peter Kotwicz | a31339a | 2020-10-20 16:36:23 | [diff] [blame] | 204 | |
Andrew Grieve | 9c7d4ac2f | 2022-08-24 17:38:59 | [diff] [blame] | 205 | if experimental: |
| 206 | package = 'experimental/google.com/' + os.getlogin() + '/androidx' |
| 207 | else: |
| 208 | package = 'chromium/third_party/androidx' |
Peter Kotwicz | a31339a | 2020-10-20 16:36:23 | [diff] [blame] | 209 | contents = [ |
mark a. foltz | f87dfea | 2023-09-06 23:49:03 | [diff] [blame] | 210 | '# Copyright 2021 The Chromium Authors', |
Peter Kotwicz | a31339a | 2020-10-20 16:36:23 | [diff] [blame] | 211 | '# Use of this source code is governed by a BSD-style license that can be', |
| 212 | '# found in the LICENSE file.', |
Peter Kotwicz | 92548aa | 2021-02-18 15:12:58 | [diff] [blame] | 213 | '# version: ' + version, |
Andrew Grieve | 9c7d4ac2f | 2022-08-24 17:38:59 | [diff] [blame] | 214 | 'package: ' + package, |
Peter Kotwicz | 3fb0070e | 2021-02-11 07:37:35 | [diff] [blame] | 215 | 'description: androidx', |
| 216 | 'data:', |
Peter Kotwicz | a31339a | 2020-10-20 16:36:23 | [diff] [blame] | 217 | ] |
| 218 | contents.extend('- file: ' + f for f in data_files) |
| 219 | |
| 220 | with open(cipd_yaml_path, 'w') as out: |
| 221 | out.write('\n'.join(contents)) |
| 222 | |
| 223 | |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 224 | def main(): |
Haiyang Pan | ace50093 | 2022-01-26 19:49:03 | [diff] [blame] | 225 | parser = argparse.ArgumentParser(description=__doc__) |
| 226 | parser.add_argument('-v', |
| 227 | '--verbose', |
| 228 | dest='verbose_count', |
| 229 | default=0, |
| 230 | action='count', |
| 231 | help='Verbose level (multiple times for more)') |
Andrew Grieve | 10bbd55 | 2022-05-05 18:10:26 | [diff] [blame] | 232 | parser.add_argument('--local-repo', |
| 233 | help='Path to a locally androidx maven repo to use ' |
| 234 | 'instead of fetching the latest.') |
Haiyang Pan | ace50093 | 2022-01-26 19:49:03 | [diff] [blame] | 235 | args = parser.parse_args() |
| 236 | |
| 237 | logging.basicConfig( |
| 238 | level=logging.WARNING - 10 * args.verbose_count, |
| 239 | format='%(levelname).1s %(relativeCreated)6d %(message)s') |
| 240 | |
Peter Kotwicz | a31339a | 2020-10-20 16:36:23 | [diff] [blame] | 241 | libs_dir = os.path.join(_ANDROIDX_PATH, 'libs') |
Andrew Grieve | 10bbd55 | 2022-05-05 18:10:26 | [diff] [blame] | 242 | if os.path.exists(libs_dir): |
| 243 | shutil.rmtree(libs_dir) |
Peter Kotwicz | a31339a | 2020-10-20 16:36:23 | [diff] [blame] | 244 | |
Peter Kotwicz | a43e517 | 2021-01-26 22:05:05 | [diff] [blame] | 245 | # Files uploaded to cipd are read-only. Delete them because they will be |
| 246 | # re-generated. |
| 247 | _delete_readonly_files([ |
| 248 | os.path.join(_ANDROIDX_PATH, 'BUILD.gn'), |
Peter Kotwicz | 1ea22e1 | 2021-02-22 19:01:59 | [diff] [blame] | 249 | os.path.join(_ANDROIDX_PATH, 'VERSION.txt'), |
Peter Kotwicz | a43e517 | 2021-01-26 22:05:05 | [diff] [blame] | 250 | os.path.join(_ANDROIDX_PATH, 'additional_readme_paths.json'), |
Peter Kotwicz | 1ea22e1 | 2021-02-22 19:01:59 | [diff] [blame] | 251 | os.path.join(_ANDROIDX_PATH, 'build.gradle'), |
Peter Kotwicz | a43e517 | 2021-01-26 22:05:05 | [diff] [blame] | 252 | ]) |
| 253 | |
Andrew Grieve | 10bbd55 | 2022-05-05 18:10:26 | [diff] [blame] | 254 | if args.local_repo: |
| 255 | version = 'local' |
| 256 | dir_list = _create_local_dir_list(args.local_repo) |
| 257 | androidx_snapshot_repository_url = ('file://' + |
| 258 | os.path.abspath(args.local_repo)) |
| 259 | else: |
| 260 | dir_list, version = _download_and_parse_build_info() |
| 261 | androidx_snapshot_repository_url = _build_snapshot_repository_url( |
| 262 | version) |
Andrew Grieve | 9c7d4ac2f | 2022-08-24 17:38:59 | [diff] [blame] | 263 | # Prepend '0' to version to avoid conflicts with previous version format. |
| 264 | version = 'cr-0' + version |
| 265 | |
Andrew Grieve | 10bbd55 | 2022-05-05 18:10:26 | [diff] [blame] | 266 | dependency_version_map = _parse_dir_list(dir_list) |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 267 | _process_build_gradle(dependency_version_map, |
| 268 | androidx_snapshot_repository_url) |
Andrew Grieve | 15c476e | 2023-02-10 19:57:31 | [diff] [blame] | 269 | shutil.copyfile(os.path.join(_ANDROIDX_PATH, 'BUILD.gn.template'), |
| 270 | os.path.join(_ANDROIDX_PATH, 'BUILD.gn')) |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 271 | |
| 272 | fetch_all_cmd = [ |
Peter Kotwicz | dabb89d07 | 2020-10-22 16:30:03 | [diff] [blame] | 273 | _FETCH_ALL_PATH, '--android-deps-dir', _ANDROIDX_PATH, |
| 274 | '--ignore-vulnerabilities' |
Andrew Grieve | e9bccea4 | 2023-02-10 19:56:25 | [diff] [blame] | 275 | ] + ['-v'] * args.verbose_count |
Andrew Grieve | 9c7d4ac2f | 2022-08-24 17:38:59 | [diff] [blame] | 276 | # Overrides do not work with local snapshots since the repository_url is |
| 277 | # different. |
| 278 | if not args.local_repo: |
| 279 | for subpath, url in _OVERRIDES: |
| 280 | fetch_all_cmd += ['--override-artifact', f'{subpath}:{url}'] |
Peter Kotwicz | a31339a | 2020-10-20 16:36:23 | [diff] [blame] | 281 | subprocess.run(fetch_all_cmd, check=True) |
| 282 | |
Andrew Grieve | 9c7d4ac2f | 2022-08-24 17:38:59 | [diff] [blame] | 283 | version_txt_path = os.path.join(_ANDROIDX_PATH, 'VERSION.txt') |
| 284 | with open(version_txt_path, 'w') as f: |
| 285 | f.write(version) |
Peter Kotwicz | 92548aa | 2021-02-18 15:12:58 | [diff] [blame] | 286 | |
Andrew Grieve | 9c7d4ac2f | 2022-08-24 17:38:59 | [diff] [blame] | 287 | yaml_path = os.path.join(_ANDROIDX_PATH, 'cipd.yaml') |
| 288 | _write_cipd_yaml(libs_dir, version, yaml_path, |
| 289 | experimental=bool(args.local_repo)) |
Peter Kotwicz | 2b85bd3d | 2020-10-01 22:29:49 | [diff] [blame] | 290 | |
| 291 | |
| 292 | if __name__ == '__main__': |
| 293 | main() |