blob: 552ded822d0b49dedaa181c7ef0e5a2f0b3e1fb0 [file] [log] [blame]
Peter Kotwicz2b85bd3d2020-10-01 22:29:491#!/usr/bin/env python3
2
Avi Drissman3f1ea442022-09-08 15:42:013# Copyright 2020 The Chromium Authors
Peter Kotwicz2b85bd3d2020-10-01 22:29:494# 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
8More specifically, to generate build.gradle:
9 - It downloads the BUILD_INFO file for the latest androidx snapshot from
Haiyang Panace500932022-01-26 19:49:0310 https://androidx.dev/snapshots/builds
Peter Kotwicz2b85bd3d2020-10-01 22:29:4911 - 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 Panace500932022-01-26 19:49:0316import argparse
Peter Kotwicz2b85bd3d2020-10-01 22:29:4917import contextlib
18import json
Haiyang Panace500932022-01-26 19:49:0319import logging
Peter Kotwicz2b85bd3d2020-10-01 22:29:4920import os
Andrew Grieveb5255af2024-06-06 17:03:1821import pathlib
Peter Kotwicz2b85bd3d2020-10-01 22:29:4922import re
Peter Kotwicz2b85bd3d2020-10-01 22:29:4923import shutil
Peter Kotwicza43e5172021-01-26 22:05:0524import stat
Peter Kotwicz2b85bd3d2020-10-01 22:29:4925import subprocess
26import tempfile
Peter Kotwicz4676d062020-10-22 06:04:1327import urllib
28from urllib import request
Peter Kotwicz2b85bd3d2020-10-01 22:29:4929
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 Wencb988a12022-06-23 18:04:3836_ANDROIDX_LATEST_SNAPSHOT_BUILD_INFO_URL = 'https://androidx.dev/snapshots/latest/artifacts/BUILD_INFO'
Peter Kotwicz2b85bd3d2020-10-01 22:29:4937
Peter Kotwicz3fb0070e2021-02-11 07:37:3538# Snapshot repository URL with {{version}} placeholder.
39_SNAPSHOT_REPOSITORY_URL = 'https://androidx.dev/snapshots/builds/{{version}}/artifacts/repository'
40
Andrew Grieve2515bcbd2022-05-06 03:14:0741# 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 Grievea03217f2022-05-12 16:05:2743# * 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 Grieve2515bcbd2022-05-06 03:14:0749_OVERRIDES = [
Andrew Grievea03217f2022-05-12 16:05:2750 # Example:
Andrew Grieve2515bcbd2022-05-06 03:14:0751 #('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 Huang4a63fe32023-03-07 22:09:0254 ('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 Grieve2515bcbd2022-05-06 03:14:0758]
59
Peter Kotwicz3fb0070e2021-02-11 07:37:3560
61def _build_snapshot_repository_url(version):
62 return _SNAPSHOT_REPOSITORY_URL.replace('{{version}}', version)
63
Peter Kotwicz2b85bd3d2020-10-01 22:29:4964
Peter Kotwicza43e5172021-01-26 22:05:0565def _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 Kotwicz2b85bd3d2020-10-01 22:29:4972def _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 Kotwicz4d3c39702021-03-25 21:48:2588 dependency_package = 'androidx.' + '.'.join(dir_components[2:-3])
89 dependency_module = '{}:{}'.format(dependency_package,
90 dir_components[-3])
Peter Kotwicz2b85bd3d2020-10-01 22:29:4991 if dependency_module not in dependency_version_map:
Peter Kotwicz4d3c39702021-03-25 21:48:2592 dependency_version_map[dependency_module] = dir_components[-2]
Peter Kotwicz2b85bd3d2020-10-01 22:29:4993 return dependency_version_map
94
95
Peter Kotwicz2b85bd3d2020-10-01 22:29:4996@contextlib.contextmanager
97def _build_dir():
98 dirname = tempfile.mkdtemp()
99 try:
100 yield dirname
101 finally:
102 shutil.rmtree(dirname)
103
104
105def _download_and_parse_build_info():
106 """Downloads and parses BUILD_INFO file."""
107 with _build_dir() as build_dir:
Peter Kotwicz4676d062020-10-22 06:04:13108 androidx_build_info_response = request.urlopen(
Peter Kotwicz2b85bd3d2020-10-01 22:29:49109 _ANDROIDX_LATEST_SNAPSHOT_BUILD_INFO_URL)
Haiyang Panace500932022-01-26 19:49:03110 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 Kotwicz2b85bd3d2020-10-01 22:29:49113 androidx_build_info_path = os.path.join(build_dir, 'BUILD_INFO')
114 with open(androidx_build_info_path, 'w') as f:
Peter Kotwicz4676d062020-10-22 06:04:13115 f.write(androidx_build_info_response.read().decode('utf-8'))
Peter Kotwicz2b85bd3d2020-10-01 22:29:49116
Peter Kotwicz3fb0070e2021-02-11 07:37:35117 # 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 Panace500932022-01-26 19:49:03122 androidx_build_info_url).group(1)
Peter Kotwicz2b85bd3d2020-10-01 22:29:49123
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 Grieve10bbd552022-05-05 18:10:26128 return dir_list, version
129
130
131def _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 Kotwicz2b85bd3d2020-10-01 22:29:49139
140
141def _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 Grieveb5255af2024-06-06 17:03:18148 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 Kotwicz2b85bd3d2020-10-01 22:29:49176 # .gitignore and thus will be excluded from uploaded CLs.
Andrew Grieveb5255af2024-06-06 17:03:18177 (androidx_path / 'build.gradle').write_text(''.join(sb))
Peter Kotwicz2b85bd3d2020-10-01 22:29:49178
179
Andrew Grieve9c7d4ac2f2022-08-24 17:38:59180def _write_cipd_yaml(libs_dir, version, cipd_yaml_path, experimental=False):
Peter Kotwicza31339a2020-10-20 16:36:23181 """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 Kotwicz1ea22e12021-02-22 19:01:59187 data_files = [
188 'BUILD.gn', 'VERSION.txt', 'additional_readme_paths.json',
189 'build.gradle'
190 ]
Peter Kotwicza31339a2020-10-20 16:36:23191 for lib_dir in lib_dirs:
192 abs_lib_dir = os.path.join(libs_dir, lib_dir)
Peter Kotwicz17702172020-10-29 21:43:02193 androidx_rel_lib_dir = os.path.relpath(abs_lib_dir, _ANDROIDX_PATH)
Peter Kotwicza31339a2020-10-20 16:36:23194 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 Kotwicza6b3b9a2021-01-28 21:40:12200 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 Kotwicza31339a2020-10-20 16:36:23204
Andrew Grieve9c7d4ac2f2022-08-24 17:38:59205 if experimental:
206 package = 'experimental/google.com/' + os.getlogin() + '/androidx'
207 else:
208 package = 'chromium/third_party/androidx'
Peter Kotwicza31339a2020-10-20 16:36:23209 contents = [
mark a. foltzf87dfea2023-09-06 23:49:03210 '# Copyright 2021 The Chromium Authors',
Peter Kotwicza31339a2020-10-20 16:36:23211 '# Use of this source code is governed by a BSD-style license that can be',
212 '# found in the LICENSE file.',
Peter Kotwicz92548aa2021-02-18 15:12:58213 '# version: ' + version,
Andrew Grieve9c7d4ac2f2022-08-24 17:38:59214 'package: ' + package,
Peter Kotwicz3fb0070e2021-02-11 07:37:35215 'description: androidx',
216 'data:',
Peter Kotwicza31339a2020-10-20 16:36:23217 ]
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 Kotwicz2b85bd3d2020-10-01 22:29:49224def main():
Haiyang Panace500932022-01-26 19:49:03225 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 Grieve10bbd552022-05-05 18:10:26232 parser.add_argument('--local-repo',
233 help='Path to a locally androidx maven repo to use '
234 'instead of fetching the latest.')
Haiyang Panace500932022-01-26 19:49:03235 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 Kotwicza31339a2020-10-20 16:36:23241 libs_dir = os.path.join(_ANDROIDX_PATH, 'libs')
Andrew Grieve10bbd552022-05-05 18:10:26242 if os.path.exists(libs_dir):
243 shutil.rmtree(libs_dir)
Peter Kotwicza31339a2020-10-20 16:36:23244
Peter Kotwicza43e5172021-01-26 22:05:05245 # 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 Kotwicz1ea22e12021-02-22 19:01:59249 os.path.join(_ANDROIDX_PATH, 'VERSION.txt'),
Peter Kotwicza43e5172021-01-26 22:05:05250 os.path.join(_ANDROIDX_PATH, 'additional_readme_paths.json'),
Peter Kotwicz1ea22e12021-02-22 19:01:59251 os.path.join(_ANDROIDX_PATH, 'build.gradle'),
Peter Kotwicza43e5172021-01-26 22:05:05252 ])
253
Andrew Grieve10bbd552022-05-05 18:10:26254 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 Grieve9c7d4ac2f2022-08-24 17:38:59263 # Prepend '0' to version to avoid conflicts with previous version format.
264 version = 'cr-0' + version
265
Andrew Grieve10bbd552022-05-05 18:10:26266 dependency_version_map = _parse_dir_list(dir_list)
Peter Kotwicz2b85bd3d2020-10-01 22:29:49267 _process_build_gradle(dependency_version_map,
268 androidx_snapshot_repository_url)
Andrew Grieve15c476e2023-02-10 19:57:31269 shutil.copyfile(os.path.join(_ANDROIDX_PATH, 'BUILD.gn.template'),
270 os.path.join(_ANDROIDX_PATH, 'BUILD.gn'))
Peter Kotwicz2b85bd3d2020-10-01 22:29:49271
272 fetch_all_cmd = [
Peter Kotwiczdabb89d072020-10-22 16:30:03273 _FETCH_ALL_PATH, '--android-deps-dir', _ANDROIDX_PATH,
274 '--ignore-vulnerabilities'
Andrew Grievee9bccea42023-02-10 19:56:25275 ] + ['-v'] * args.verbose_count
Andrew Grieve9c7d4ac2f2022-08-24 17:38:59276 # 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 Kotwicza31339a2020-10-20 16:36:23281 subprocess.run(fetch_all_cmd, check=True)
282
Andrew Grieve9c7d4ac2f2022-08-24 17:38:59283 version_txt_path = os.path.join(_ANDROIDX_PATH, 'VERSION.txt')
284 with open(version_txt_path, 'w') as f:
285 f.write(version)
Peter Kotwicz92548aa2021-02-18 15:12:58286
Andrew Grieve9c7d4ac2f2022-08-24 17:38:59287 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 Kotwicz2b85bd3d2020-10-01 22:29:49290
291
292if __name__ == '__main__':
293 main()