OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # | 2 # |
3 # Copyright 2015 The Chromium Authors. All rights reserved. | 3 # Copyright 2015 The Chromium Authors. All rights reserved. |
4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 '''Prepares the Google Play services split client libraries before usage by | 7 '''Prepares the Google Play services split client libraries before usage by |
8 Chrome's build system. | 8 Chrome's build system. |
9 | 9 |
10 We need to preprocess Google Play services before using it in Chrome | 10 We need to preprocess Google Play services before using it in Chrome |
(...skipping 11 matching lines...) Expand all Loading... |
22 Manager. It also supports importing from already extracted AAR files using the | 22 Manager. It also supports importing from already extracted AAR files using the |
23 --is-extracted-repo flag. The expected directory structure in that case would | 23 --is-extracted-repo flag. The expected directory structure in that case would |
24 look like: | 24 look like: |
25 | 25 |
26 REPOSITORY_DIR | 26 REPOSITORY_DIR |
27 +-- CLIENT_1 | 27 +-- CLIENT_1 |
28 | +-- <content of the first AAR file> | 28 | +-- <content of the first AAR file> |
29 +-- CLIENT_2 | 29 +-- CLIENT_2 |
30 +-- etc. | 30 +-- etc. |
31 | 31 |
32 The json config (see the -c argument) file should provide the following fields: | |
33 | |
34 - lib_version: String. Used when building from the maven repository. It should | |
35 be the package's version (e.g. "7.3.0"). Unused with extracted repositories. | |
36 | |
37 - clients: String array. List of clients to pick. For example, when building | |
38 from the maven repository, it's the artifactId (e.g. "play-services-base") of | |
39 each client. With an extracted repository, it's the name of the | |
40 subdirectories. | |
41 | |
42 - locale_whitelist: String array. Locales that should be allowed in the final | |
43 resources. They are specified the same way they are appended to the `values` | |
44 directory in android resources (e.g. "us-GB", "it", "fil"). | |
45 | |
46 The output is a directory with the following structure: | 32 The output is a directory with the following structure: |
47 | 33 |
48 OUT_DIR | 34 OUT_DIR |
49 +-- google-play-services.jar | 35 +-- google-play-services.jar |
50 +-- res | 36 +-- res |
51 | +-- CLIENT_1 | 37 | +-- CLIENT_1 |
52 | | +-- color | 38 | | +-- color |
53 | | +-- values | 39 | | +-- values |
54 | | +-- etc. | 40 | | +-- etc. |
55 | +-- CLIENT_2 | 41 | +-- CLIENT_2 |
56 | +-- ... | 42 | +-- ... |
57 +-- stub | 43 +-- stub |
58 +-- res/[.git-keep-directory] | 44 +-- res/[.git-keep-directory] |
59 +-- src/android/UnusedStub.java | 45 +-- src/android/UnusedStub.java |
60 | 46 |
61 Requires the `jar` utility in the path. | 47 Requires the `jar` utility in the path. |
62 | 48 |
63 ''' | 49 ''' |
64 | 50 |
65 import argparse | 51 import argparse |
66 import glob | 52 import glob |
67 import itertools | 53 import itertools |
68 import json | |
69 import os | 54 import os |
70 import re | |
71 import shutil | 55 import shutil |
72 import stat | 56 import stat |
73 import sys | 57 import sys |
| 58 import tempfile |
| 59 import zipfile |
74 | 60 |
75 from datetime import datetime | 61 from datetime import datetime |
| 62 |
| 63 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir)) |
76 from devil.utils import cmd_helper | 64 from devil.utils import cmd_helper |
77 from pylib import constants | 65 from play_services import utils |
78 | 66 from pylib.utils import argparse_utils |
79 sys.path.append( | |
80 os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'android', 'gyp')) | |
81 from util import build_utils # pylint: disable=import-error | |
82 | 67 |
83 | 68 |
84 M2_PKG_PATH = os.path.join('com', 'google', 'android', 'gms') | 69 M2_PKG_PATH = os.path.join('com', 'google', 'android', 'gms') |
85 OUTPUT_FORMAT_VERSION = 1 | 70 |
86 VERSION_FILE_NAME = 'version_info.json' | |
87 VERSION_NUMBER_PATTERN = re.compile( | |
88 r'<integer name="google_play_services_version">(\d+)<\/integer>') | |
89 | 71 |
90 def main(): | 72 def main(): |
91 parser = argparse.ArgumentParser(description=("Prepares the Google Play " | 73 parser = argparse.ArgumentParser(description=( |
92 "services split client libraries before usage by Chrome's build system. " | 74 "Prepares the Google Play services split client libraries before usage " |
93 "See the script's documentation for more a detailed help.")) | 75 "by Chrome's build system. See the script's documentation for more a " |
94 parser.add_argument('-r', | 76 "detailed help.")) |
95 '--repository', | 77 argparse_utils.CustomHelpAction.EnableFor(parser) |
96 help='The Google Play services repository location', | 78 required_args = parser.add_argument_group('required named arguments') |
97 required=True, | 79 required_args.add_argument('-r', |
98 metavar='FILE') | 80 '--repository', |
99 parser.add_argument('-o', | 81 help=('the Google Play services repository ' |
100 '--out-dir', | 82 'location'), |
101 help='The output directory', | 83 required=True, |
102 required=True, | 84 metavar='FILE') |
103 metavar='FILE') | 85 required_args.add_argument('-o', |
104 parser.add_argument('-c', | 86 '--out-dir', |
105 '--config-file', | 87 help='the output directory', |
106 help='Config file path', | 88 required=True, |
107 required=True, | 89 metavar='FILE') |
108 metavar='FILE') | 90 required_args.add_argument('-c', |
| 91 '--config-file', |
| 92 help='the config file path', |
| 93 required=True, |
| 94 metavar='FILE') |
109 parser.add_argument('-x', | 95 parser.add_argument('-x', |
110 '--is-extracted-repo', | 96 '--is-extracted-repo', |
111 action='store_true', | 97 action='store_true', |
112 default=False, | 98 help='the provided repository is not made of AAR files') |
113 help='The provided repository is not made of AAR files.') | 99 parser.add_argument('--config-help', |
| 100 action='custom_help', |
| 101 custom_help_text=utils.ConfigParser.__doc__, |
| 102 help='show the configuration file format help') |
114 | 103 |
115 args = parser.parse_args() | 104 args = parser.parse_args() |
116 | 105 |
117 ProcessGooglePlayServices(args.repository, | 106 return ProcessGooglePlayServices(args.repository, |
118 args.out_dir, | 107 args.out_dir, |
119 args.config_file, | 108 args.config_file, |
120 args.is_extracted_repo) | 109 args.is_extracted_repo) |
121 | 110 |
122 | 111 |
123 def ProcessGooglePlayServices(repo, out_dir, config_path, is_extracted_repo): | 112 def ProcessGooglePlayServices(repo, out_dir, config_path, is_extracted_repo): |
124 with open(config_path, 'r') as json_file: | 113 config = utils.ConfigParser(config_path) |
125 config = json.load(json_file) | |
126 | 114 |
127 with build_utils.TempDir() as tmp_root: | 115 tmp_root = tempfile.mkdtemp() |
| 116 try: |
128 tmp_paths = _SetupTempDir(tmp_root) | 117 tmp_paths = _SetupTempDir(tmp_root) |
129 | 118 |
130 if is_extracted_repo: | 119 if is_extracted_repo: |
131 _ImportFromExtractedRepo(config, tmp_paths, repo) | 120 _ImportFromExtractedRepo(config, tmp_paths, repo) |
132 else: | 121 else: |
133 _ImportFromAars(config, tmp_paths, repo) | 122 _ImportFromAars(config, tmp_paths, repo) |
134 | 123 |
135 _GenerateCombinedJar(tmp_paths) | 124 _GenerateCombinedJar(tmp_paths) |
136 _ProcessResources(config, tmp_paths) | 125 _ProcessResources(config, tmp_paths) |
| 126 _BuildOutput(config, tmp_paths, out_dir) |
| 127 finally: |
| 128 shutil.rmtree(tmp_root) |
137 | 129 |
138 if is_extracted_repo: | 130 return 0 |
139 printable_repo = repo | |
140 else: | |
141 printable_repo = 'm2repository - ' + config['lib_version'] | |
142 _BuildOutput(config, tmp_paths, out_dir, printable_repo) | |
143 | 131 |
144 | 132 |
145 def _SetupTempDir(tmp_root): | 133 def _SetupTempDir(tmp_root): |
146 tmp_paths = { | 134 tmp_paths = { |
147 'root': tmp_root, | 135 'root': tmp_root, |
148 'imported_clients': os.path.join(tmp_root, 'imported_clients'), | 136 'imported_clients': os.path.join(tmp_root, 'imported_clients'), |
149 'extracted_jars': os.path.join(tmp_root, 'jar'), | 137 'extracted_jars': os.path.join(tmp_root, 'jar'), |
150 'combined_jar': os.path.join(tmp_root, 'google-play-services.jar'), | 138 'combined_jar': os.path.join(tmp_root, 'google-play-services.jar'), |
151 } | 139 } |
152 os.mkdir(tmp_paths['imported_clients']) | 140 os.mkdir(tmp_paths['imported_clients']) |
153 os.mkdir(tmp_paths['extracted_jars']) | 141 os.mkdir(tmp_paths['extracted_jars']) |
154 | 142 |
155 return tmp_paths | 143 return tmp_paths |
156 | 144 |
157 | 145 |
158 def _SetupOutputDir(out_dir): | 146 def _SetupOutputDir(out_dir): |
159 out_paths = { | 147 out_paths = { |
160 'root': out_dir, | 148 'root': out_dir, |
161 'res': os.path.join(out_dir, 'res'), | 149 'res': os.path.join(out_dir, 'res'), |
162 'jar': os.path.join(out_dir, 'google-play-services.jar'), | 150 'jar': os.path.join(out_dir, 'google-play-services.jar'), |
163 'stub': os.path.join(out_dir, 'stub'), | 151 'stub': os.path.join(out_dir, 'stub'), |
164 } | 152 } |
165 | 153 |
166 shutil.rmtree(out_paths['jar'], ignore_errors=True) | 154 shutil.rmtree(out_paths['jar'], ignore_errors=True) |
167 shutil.rmtree(out_paths['res'], ignore_errors=True) | 155 shutil.rmtree(out_paths['res'], ignore_errors=True) |
168 shutil.rmtree(out_paths['stub'], ignore_errors=True) | 156 shutil.rmtree(out_paths['stub'], ignore_errors=True) |
169 | 157 |
170 return out_paths | 158 return out_paths |
171 | 159 |
172 | 160 |
173 def _MakeWritable(dir_path): | 161 def _MakeWritable(dir_path): |
174 for root, dirs, files in os.walk(dir_path): | 162 for root, dirs, files in os.walk(dir_path): |
175 for path in itertools.chain(dirs, files): | 163 for path in itertools.chain(dirs, files): |
176 st = os.stat(os.path.join(root, path)) | 164 st = os.stat(os.path.join(root, path)) |
177 os.chmod(os.path.join(root, path), st.st_mode | stat.S_IWUSR) | 165 os.chmod(os.path.join(root, path), st.st_mode | stat.S_IWUSR) |
178 | 166 |
179 | 167 |
180 def _ImportFromAars(config, tmp_paths, repo): | 168 def _ImportFromAars(config, tmp_paths, repo): |
181 for client in config['clients']: | 169 for client in config.clients: |
182 aar_name = '%s-%s.aar' % (client, config['lib_version']) | 170 aar_name = '%s-%s.aar' % (client, config.sdk_version) |
183 aar_path = os.path.join(repo, M2_PKG_PATH, client, | 171 aar_path = os.path.join(repo, M2_PKG_PATH, client, |
184 config['lib_version'], aar_name) | 172 config.sdk_version, aar_name) |
185 aar_out_path = os.path.join(tmp_paths['imported_clients'], client) | 173 aar_out_path = os.path.join(tmp_paths['imported_clients'], client) |
186 build_utils.ExtractAll(aar_path, aar_out_path) | 174 _ExtractAll(aar_path, aar_out_path) |
187 | 175 |
188 client_jar_path = os.path.join(aar_out_path, 'classes.jar') | 176 client_jar_path = os.path.join(aar_out_path, 'classes.jar') |
189 build_utils.ExtractAll(client_jar_path, tmp_paths['extracted_jars'], | 177 _ExtractAll(client_jar_path, tmp_paths['extracted_jars']) |
190 no_clobber=False) | |
191 | 178 |
192 | 179 |
193 def _ImportFromExtractedRepo(config, tmp_paths, repo): | 180 def _ImportFromExtractedRepo(config, tmp_paths, repo): |
194 # Import the clients | 181 # Import the clients |
195 try: | 182 try: |
196 for client in config['clients']: | 183 for client in config.clients: |
197 client_out_dir = os.path.join(tmp_paths['imported_clients'], client) | 184 client_out_dir = os.path.join(tmp_paths['imported_clients'], client) |
198 shutil.copytree(os.path.join(repo, client), client_out_dir) | 185 shutil.copytree(os.path.join(repo, client), client_out_dir) |
199 | 186 |
200 client_jar_path = os.path.join(client_out_dir, 'classes.jar') | 187 client_jar_path = os.path.join(client_out_dir, 'classes.jar') |
201 build_utils.ExtractAll(client_jar_path, tmp_paths['extracted_jars'], | 188 _ExtractAll(client_jar_path, tmp_paths['extracted_jars']) |
202 no_clobber=False) | |
203 finally: | 189 finally: |
204 _MakeWritable(tmp_paths['imported_clients']) | 190 _MakeWritable(tmp_paths['imported_clients']) |
205 | 191 |
206 | 192 |
207 def _GenerateCombinedJar(tmp_paths): | 193 def _GenerateCombinedJar(tmp_paths): |
208 out_file_name = tmp_paths['combined_jar'] | 194 out_file_name = tmp_paths['combined_jar'] |
209 working_dir = tmp_paths['extracted_jars'] | 195 working_dir = tmp_paths['extracted_jars'] |
210 cmd_helper.Call(['jar', '-cf', out_file_name, '-C', working_dir, '.']) | 196 cmd_helper.Call(['jar', '-cf', out_file_name, '-C', working_dir, '.']) |
211 | 197 |
212 | 198 |
213 def _ProcessResources(config, tmp_paths): | 199 def _ProcessResources(config, tmp_paths): |
214 LOCALIZED_VALUES_BASE_NAME = 'values-' | 200 LOCALIZED_VALUES_BASE_NAME = 'values-' |
215 locale_whitelist = set(config['locale_whitelist']) | 201 locale_whitelist = set(config.locale_whitelist) |
216 | 202 |
217 glob_pattern = os.path.join(tmp_paths['imported_clients'], '*', 'res', '*') | 203 glob_pattern = os.path.join(tmp_paths['imported_clients'], '*', 'res', '*') |
218 for res_dir in glob.glob(glob_pattern): | 204 for res_dir in glob.glob(glob_pattern): |
219 dir_name = os.path.basename(res_dir) | 205 dir_name = os.path.basename(res_dir) |
220 | 206 |
221 if dir_name.startswith('drawable'): | 207 if dir_name.startswith('drawable'): |
222 shutil.rmtree(res_dir) | 208 shutil.rmtree(res_dir) |
223 continue | 209 continue |
224 | 210 |
225 if dir_name.startswith(LOCALIZED_VALUES_BASE_NAME): | 211 if dir_name.startswith(LOCALIZED_VALUES_BASE_NAME): |
226 dir_locale = dir_name[len(LOCALIZED_VALUES_BASE_NAME):] | 212 dir_locale = dir_name[len(LOCALIZED_VALUES_BASE_NAME):] |
227 if dir_locale not in locale_whitelist: | 213 if dir_locale not in locale_whitelist: |
228 shutil.rmtree(res_dir) | 214 shutil.rmtree(res_dir) |
229 | 215 |
230 | 216 |
231 def _GetVersionNumber(config, tmp_paths): | 217 def _BuildOutput(config, tmp_paths, out_dir): |
232 version_file_path = os.path.join(tmp_paths['imported_clients'], | |
233 config['base_client'], | |
234 'res', | |
235 'values', | |
236 'version.xml') | |
237 | |
238 with open(version_file_path, 'r') as version_file: | |
239 version_file_content = version_file.read() | |
240 | |
241 match = VERSION_NUMBER_PATTERN.search(version_file_content) | |
242 if not match: | |
243 raise AttributeError('A value for google_play_services_version was not ' | |
244 'found in ' + version_file_path) | |
245 | |
246 return match.group(1) | |
247 | |
248 | |
249 def _BuildOutput(config, tmp_paths, out_dir, printable_repo): | |
250 generation_date = datetime.utcnow() | 218 generation_date = datetime.utcnow() |
251 play_services_full_version = _GetVersionNumber(config, tmp_paths) | 219 version_xml_path = os.path.join(tmp_paths['imported_clients'], |
252 | 220 config.version_xml_path) |
253 # Create a version text file to allow quickly checking the version | 221 play_services_full_version = utils.GetVersionNumberFromLibraryResources( |
254 gen_info = { | 222 version_xml_path) |
255 '@Description@': 'Preprocessed Google Play services clients for chrome', | |
256 'Generator script': os.path.basename(__file__), | |
257 'Repository source': printable_repo, | |
258 'Library version': play_services_full_version, | |
259 'Directory format version': OUTPUT_FORMAT_VERSION, | |
260 'Generation Date (UTC)': str(generation_date) | |
261 } | |
262 tmp_version_file_path = os.path.join(tmp_paths['root'], VERSION_FILE_NAME) | |
263 with open(tmp_version_file_path, 'w') as version_file: | |
264 json.dump(gen_info, version_file, indent=2, sort_keys=True) | |
265 | 223 |
266 out_paths = _SetupOutputDir(out_dir) | 224 out_paths = _SetupOutputDir(out_dir) |
267 | 225 |
268 # Copy the resources to the output dir | 226 # Copy the resources to the output dir |
269 for client in config['clients']: | 227 for client in config.clients: |
270 res_in_tmp_dir = os.path.join(tmp_paths['imported_clients'], client, 'res') | 228 res_in_tmp_dir = os.path.join(tmp_paths['imported_clients'], client, 'res') |
271 if os.path.isdir(res_in_tmp_dir) and os.listdir(res_in_tmp_dir): | 229 if os.path.isdir(res_in_tmp_dir) and os.listdir(res_in_tmp_dir): |
272 res_in_final_dir = os.path.join(out_paths['res'], client) | 230 res_in_final_dir = os.path.join(out_paths['res'], client) |
273 shutil.copytree(res_in_tmp_dir, res_in_final_dir) | 231 shutil.copytree(res_in_tmp_dir, res_in_final_dir) |
274 | 232 |
275 # Copy the jar | 233 # Copy the jar |
276 shutil.copyfile(tmp_paths['combined_jar'], out_paths['jar']) | 234 shutil.copyfile(tmp_paths['combined_jar'], out_paths['jar']) |
277 | 235 |
278 # Write the java dummy stub. Needed for gyp to create the resource jar | 236 # Write the java dummy stub. Needed for gyp to create the resource jar |
279 stub_location = os.path.join(out_paths['stub'], 'src', 'android') | 237 stub_location = os.path.join(out_paths['stub'], 'src', 'android') |
280 os.makedirs(stub_location) | 238 os.makedirs(stub_location) |
281 with open(os.path.join(stub_location, 'UnusedStub.java'), 'w') as stub: | 239 with open(os.path.join(stub_location, 'UnusedStub.java'), 'w') as stub: |
282 stub.write('package android;' | 240 stub.write('package android;' |
283 'public final class UnusedStub {' | 241 'public final class UnusedStub {' |
284 ' private UnusedStub() {}' | 242 ' private UnusedStub() {}' |
285 '}') | 243 '}') |
286 | 244 |
287 # Create the main res directory. It is needed by gyp | 245 # Create the main res directory. It is needed by gyp |
288 stub_res_location = os.path.join(out_paths['stub'], 'res') | 246 stub_res_location = os.path.join(out_paths['stub'], 'res') |
289 os.makedirs(stub_res_location) | 247 os.makedirs(stub_res_location) |
290 with open(os.path.join(stub_res_location, '.res-stamp'), 'w') as stamp: | 248 with open(os.path.join(stub_res_location, '.res-stamp'), 'w') as stamp: |
291 content_str = 'google_play_services_version: %s\nutc_date: %s\n' | 249 content_str = 'google_play_services_version: %s\nutc_date: %s\n' |
292 stamp.write(content_str % (play_services_full_version, generation_date)) | 250 stamp.write(content_str % (play_services_full_version, generation_date)) |
293 | 251 |
294 shutil.copyfile(tmp_version_file_path, | 252 config.UpdateVersionNumber(play_services_full_version) |
295 os.path.join(out_paths['root'], VERSION_FILE_NAME)) | |
296 | 253 |
297 | 254 |
| 255 def _ExtractAll(zip_path, out_path): |
| 256 with zipfile.ZipFile(zip_path, 'r') as zip_file: |
| 257 zip_file.extractall(out_path) |
| 258 |
298 if __name__ == '__main__': | 259 if __name__ == '__main__': |
299 sys.exit(main()) | 260 sys.exit(main()) |
OLD | NEW |