Chromium Code Reviews| 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: | |
|
dgn
2015/11/23 15:26:45
The description of this is now available when runn
| |
| 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 | |
| 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 | |
|
dgn
2015/11/23 15:26:45
Got rid of that, since it's supposed to be reserve
| |
| 82 | 66 |
| 83 | 67 |
| 84 M2_PKG_PATH = os.path.join('com', 'google', 'android', 'gms') | 68 M2_PKG_PATH = os.path.join('com', 'google', 'android', 'gms') |
| 85 OUTPUT_FORMAT_VERSION = 1 | 69 OUTPUT_FORMAT_VERSION = 1 |
| 86 VERSION_FILE_NAME = 'version_info.json' | 70 VERSION_FILE_NAME = 'version_info.json' |
| 87 VERSION_NUMBER_PATTERN = re.compile( | 71 |
| 88 r'<integer name="google_play_services_version">(\d+)<\/integer>') | |
| 89 | 72 |
| 90 def main(): | 73 def main(): |
| 91 parser = argparse.ArgumentParser(description=("Prepares the Google Play " | 74 if '--config-help' in sys.argv: |
| 92 "services split client libraries before usage by Chrome's build system. " | 75 # We do this test before parsing the argument to be able to still show the |
| 93 "See the script's documentation for more a detailed help.")) | 76 # help when mandatory arguments are missing |
| 77 print utils.ConfigParser.__doc__ | |
| 78 return 0 | |
| 79 | |
| 80 parser = argparse.ArgumentParser(description=( | |
| 81 "Prepares the Google Play services split client libraries before usage " | |
| 82 "by Chrome's build system. See the script's documentation for more a " | |
| 83 "detailed help.")) | |
| 94 parser.add_argument('-r', | 84 parser.add_argument('-r', |
| 95 '--repository', | 85 '--repository', |
| 96 help='The Google Play services repository location', | 86 help='The Google Play services repository location', |
| 97 required=True, | 87 required=True, |
| 98 metavar='FILE') | 88 metavar='FILE') |
| 99 parser.add_argument('-o', | 89 parser.add_argument('-o', |
| 100 '--out-dir', | 90 '--out-dir', |
| 101 help='The output directory', | 91 help='The output directory', |
| 102 required=True, | 92 required=True, |
| 103 metavar='FILE') | 93 metavar='FILE') |
| 104 parser.add_argument('-c', | 94 parser.add_argument('-c', |
| 105 '--config-file', | 95 '--config-file', |
| 106 help='Config file path', | 96 help='Config file path', |
| 107 required=True, | 97 required=True, |
| 108 metavar='FILE') | 98 metavar='FILE') |
| 109 parser.add_argument('-x', | 99 parser.add_argument('-x', |
| 110 '--is-extracted-repo', | 100 '--is-extracted-repo', |
| 111 action='store_true', | 101 action='store_true', |
| 112 default=False, | 102 default=False, |
| 113 help='The provided repository is not made of AAR files.') | 103 help='The provided repository is not made of AAR files.') |
| 104 parser.add_argument('--config-help', | |
| 105 action='store_true', | |
| 106 default=False, | |
| 107 help='Show the configuration file format help.') | |
| 114 | 108 |
| 115 args = parser.parse_args() | 109 args = parser.parse_args() |
| 116 | 110 |
| 117 ProcessGooglePlayServices(args.repository, | 111 return ProcessGooglePlayServices(args.repository, |
| 118 args.out_dir, | 112 args.out_dir, |
| 119 args.config_file, | 113 args.config_file, |
| 120 args.is_extracted_repo) | 114 args.is_extracted_repo) |
| 121 | 115 |
| 122 | 116 |
| 123 def ProcessGooglePlayServices(repo, out_dir, config_path, is_extracted_repo): | 117 def ProcessGooglePlayServices(repo, out_dir, config_path, is_extracted_repo): |
| 124 with open(config_path, 'r') as json_file: | 118 config = utils.ConfigParser(config_path) |
| 125 config = json.load(json_file) | |
| 126 | 119 |
| 127 with build_utils.TempDir() as tmp_root: | 120 tmp_root = tempfile.mkdtemp() |
| 121 try: | |
| 128 tmp_paths = _SetupTempDir(tmp_root) | 122 tmp_paths = _SetupTempDir(tmp_root) |
| 129 | 123 |
| 130 if is_extracted_repo: | 124 if is_extracted_repo: |
| 131 _ImportFromExtractedRepo(config, tmp_paths, repo) | 125 _ImportFromExtractedRepo(config, tmp_paths, repo) |
| 132 else: | 126 else: |
| 133 _ImportFromAars(config, tmp_paths, repo) | 127 _ImportFromAars(config, tmp_paths, repo) |
| 134 | 128 |
| 135 _GenerateCombinedJar(tmp_paths) | 129 _GenerateCombinedJar(tmp_paths) |
| 136 _ProcessResources(config, tmp_paths) | 130 _ProcessResources(config, tmp_paths) |
| 137 | 131 |
| 138 if is_extracted_repo: | 132 if is_extracted_repo: |
| 139 printable_repo = repo | 133 printable_repo = repo |
| 140 else: | 134 else: |
| 141 printable_repo = 'm2repository - ' + config['lib_version'] | 135 printable_repo = 'm2repository - ' + config.sdk_version |
| 142 _BuildOutput(config, tmp_paths, out_dir, printable_repo) | 136 _BuildOutput(config, tmp_paths, out_dir, printable_repo) |
| 137 finally: | |
| 138 shutil.rmtree(tmp_root) | |
| 139 | |
| 140 return 0 | |
| 143 | 141 |
| 144 | 142 |
| 145 def _SetupTempDir(tmp_root): | 143 def _SetupTempDir(tmp_root): |
| 146 tmp_paths = { | 144 tmp_paths = { |
| 147 'root': tmp_root, | 145 'root': tmp_root, |
| 148 'imported_clients': os.path.join(tmp_root, 'imported_clients'), | 146 'imported_clients': os.path.join(tmp_root, 'imported_clients'), |
| 149 'extracted_jars': os.path.join(tmp_root, 'jar'), | 147 'extracted_jars': os.path.join(tmp_root, 'jar'), |
| 150 'combined_jar': os.path.join(tmp_root, 'google-play-services.jar'), | 148 'combined_jar': os.path.join(tmp_root, 'google-play-services.jar'), |
| 151 } | 149 } |
| 152 os.mkdir(tmp_paths['imported_clients']) | 150 os.mkdir(tmp_paths['imported_clients']) |
| 153 os.mkdir(tmp_paths['extracted_jars']) | 151 os.mkdir(tmp_paths['extracted_jars']) |
| 154 | 152 |
| 155 return tmp_paths | 153 return tmp_paths |
| 156 | 154 |
| 157 | 155 |
| 158 def _SetupOutputDir(out_dir): | 156 def _SetupOutputDir(out_dir): |
| 159 out_paths = { | 157 out_paths = { |
| 160 'root': out_dir, | 158 'root': out_dir, |
| 161 'res': os.path.join(out_dir, 'res'), | 159 'res': os.path.join(out_dir, 'res'), |
| 162 'jar': os.path.join(out_dir, 'google-play-services.jar'), | 160 'jar': os.path.join(out_dir, 'google-play-services.jar'), |
| 163 'stub': os.path.join(out_dir, 'stub'), | 161 'stub': os.path.join(out_dir, 'stub'), |
| 164 } | 162 } |
| 165 | 163 |
| 166 shutil.rmtree(out_paths['jar'], ignore_errors=True) | 164 shutil.rmtree(out_paths['jar'], ignore_errors=True) |
| 167 shutil.rmtree(out_paths['res'], ignore_errors=True) | 165 shutil.rmtree(out_paths['res'], ignore_errors=True) |
| 168 shutil.rmtree(out_paths['stub'], ignore_errors=True) | 166 shutil.rmtree(out_paths['stub'], ignore_errors=True) |
| 169 | 167 |
| 170 return out_paths | 168 return out_paths |
| 171 | 169 |
| 172 | 170 |
| 173 def _MakeWritable(dir_path): | 171 def _MakeWritable(dir_path): |
| 174 for root, dirs, files in os.walk(dir_path): | 172 for root, dirs, files in os.walk(dir_path): |
| 175 for path in itertools.chain(dirs, files): | 173 for path in itertools.chain(dirs, files): |
| 176 st = os.stat(os.path.join(root, path)) | 174 st = os.stat(os.path.join(root, path)) |
| 177 os.chmod(os.path.join(root, path), st.st_mode | stat.S_IWUSR) | 175 os.chmod(os.path.join(root, path), st.st_mode | stat.S_IWUSR) |
| 178 | 176 |
| 179 | 177 |
| 180 def _ImportFromAars(config, tmp_paths, repo): | 178 def _ImportFromAars(config, tmp_paths, repo): |
| 181 for client in config['clients']: | 179 for client in config.clients: |
| 182 aar_name = '%s-%s.aar' % (client, config['lib_version']) | 180 aar_name = '%s-%s.aar' % (client, config.sdk_version) |
| 183 aar_path = os.path.join(repo, M2_PKG_PATH, client, | 181 aar_path = os.path.join(repo, M2_PKG_PATH, client, |
| 184 config['lib_version'], aar_name) | 182 config.sdk_version, aar_name) |
| 185 aar_out_path = os.path.join(tmp_paths['imported_clients'], client) | 183 aar_out_path = os.path.join(tmp_paths['imported_clients'], client) |
| 186 build_utils.ExtractAll(aar_path, aar_out_path) | 184 _ExtractAll(aar_path, aar_out_path) |
| 187 | 185 |
| 188 client_jar_path = os.path.join(aar_out_path, 'classes.jar') | 186 client_jar_path = os.path.join(aar_out_path, 'classes.jar') |
| 189 build_utils.ExtractAll(client_jar_path, tmp_paths['extracted_jars'], | 187 _ExtractAll(client_jar_path, tmp_paths['extracted_jars']) |
| 190 no_clobber=False) | |
| 191 | 188 |
| 192 | 189 |
| 193 def _ImportFromExtractedRepo(config, tmp_paths, repo): | 190 def _ImportFromExtractedRepo(config, tmp_paths, repo): |
| 194 # Import the clients | 191 # Import the clients |
| 195 try: | 192 try: |
| 196 for client in config['clients']: | 193 for client in config.clients: |
| 197 client_out_dir = os.path.join(tmp_paths['imported_clients'], client) | 194 client_out_dir = os.path.join(tmp_paths['imported_clients'], client) |
| 198 shutil.copytree(os.path.join(repo, client), client_out_dir) | 195 shutil.copytree(os.path.join(repo, client), client_out_dir) |
| 199 | 196 |
| 200 client_jar_path = os.path.join(client_out_dir, 'classes.jar') | 197 client_jar_path = os.path.join(client_out_dir, 'classes.jar') |
| 201 build_utils.ExtractAll(client_jar_path, tmp_paths['extracted_jars'], | 198 _ExtractAll(client_jar_path, tmp_paths['extracted_jars']) |
| 202 no_clobber=False) | |
| 203 finally: | 199 finally: |
| 204 _MakeWritable(tmp_paths['imported_clients']) | 200 _MakeWritable(tmp_paths['imported_clients']) |
| 205 | 201 |
| 206 | 202 |
| 207 def _GenerateCombinedJar(tmp_paths): | 203 def _GenerateCombinedJar(tmp_paths): |
| 208 out_file_name = tmp_paths['combined_jar'] | 204 out_file_name = tmp_paths['combined_jar'] |
| 209 working_dir = tmp_paths['extracted_jars'] | 205 working_dir = tmp_paths['extracted_jars'] |
| 210 cmd_helper.Call(['jar', '-cf', out_file_name, '-C', working_dir, '.']) | 206 cmd_helper.Call(['jar', '-cf', out_file_name, '-C', working_dir, '.']) |
| 211 | 207 |
| 212 | 208 |
| 213 def _ProcessResources(config, tmp_paths): | 209 def _ProcessResources(config, tmp_paths): |
| 214 LOCALIZED_VALUES_BASE_NAME = 'values-' | 210 LOCALIZED_VALUES_BASE_NAME = 'values-' |
| 215 locale_whitelist = set(config['locale_whitelist']) | 211 locale_whitelist = set(config.locale_whitelist) |
| 216 | 212 |
| 217 glob_pattern = os.path.join(tmp_paths['imported_clients'], '*', 'res', '*') | 213 glob_pattern = os.path.join(tmp_paths['imported_clients'], '*', 'res', '*') |
| 218 for res_dir in glob.glob(glob_pattern): | 214 for res_dir in glob.glob(glob_pattern): |
| 219 dir_name = os.path.basename(res_dir) | 215 dir_name = os.path.basename(res_dir) |
| 220 | 216 |
| 221 if dir_name.startswith('drawable'): | 217 if dir_name.startswith('drawable'): |
| 222 shutil.rmtree(res_dir) | 218 shutil.rmtree(res_dir) |
| 223 continue | 219 continue |
| 224 | 220 |
| 225 if dir_name.startswith(LOCALIZED_VALUES_BASE_NAME): | 221 if dir_name.startswith(LOCALIZED_VALUES_BASE_NAME): |
| 226 dir_locale = dir_name[len(LOCALIZED_VALUES_BASE_NAME):] | 222 dir_locale = dir_name[len(LOCALIZED_VALUES_BASE_NAME):] |
| 227 if dir_locale not in locale_whitelist: | 223 if dir_locale not in locale_whitelist: |
| 228 shutil.rmtree(res_dir) | 224 shutil.rmtree(res_dir) |
| 229 | 225 |
| 230 | 226 |
| 231 def _GetVersionNumber(config, tmp_paths): | |
| 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): | 227 def _BuildOutput(config, tmp_paths, out_dir, printable_repo): |
| 250 generation_date = datetime.utcnow() | 228 generation_date = datetime.utcnow() |
| 251 play_services_full_version = _GetVersionNumber(config, tmp_paths) | 229 version_xml_path = os.path.join(tmp_paths['imported_clients'], |
| 230 config.version_xml_path) | |
| 231 play_services_full_version = utils.GetVersionNumberFromLibraryResources( | |
| 232 version_xml_path) | |
| 252 | 233 |
| 253 # Create a version text file to allow quickly checking the version | 234 # Create a version text file to allow quickly checking the version |
| 254 gen_info = { | 235 gen_info = { |
| 255 '@Description@': 'Preprocessed Google Play services clients for chrome', | 236 '@Description@': 'Preprocessed Google Play services clients for chrome', |
| 256 'Generator script': os.path.basename(__file__), | 237 'Generator script': os.path.basename(__file__), |
| 257 'Repository source': printable_repo, | 238 'Repository source': printable_repo, |
| 258 'Library version': play_services_full_version, | 239 'Library version': play_services_full_version, |
| 259 'Directory format version': OUTPUT_FORMAT_VERSION, | 240 'Directory format version': OUTPUT_FORMAT_VERSION, |
| 260 'Generation Date (UTC)': str(generation_date) | 241 'Generation Date (UTC)': str(generation_date) |
| 261 } | 242 } |
| 262 tmp_version_file_path = os.path.join(tmp_paths['root'], VERSION_FILE_NAME) | 243 tmp_version_file_path = os.path.join(tmp_paths['root'], VERSION_FILE_NAME) |
| 263 with open(tmp_version_file_path, 'w') as version_file: | 244 with open(tmp_version_file_path, 'w') as version_file: |
| 264 json.dump(gen_info, version_file, indent=2, sort_keys=True) | 245 version_file.write(utils.DumpTrimmedJson(gen_info)) |
| 265 | 246 |
| 266 out_paths = _SetupOutputDir(out_dir) | 247 out_paths = _SetupOutputDir(out_dir) |
| 267 | 248 |
| 268 # Copy the resources to the output dir | 249 # Copy the resources to the output dir |
| 269 for client in config['clients']: | 250 for client in config.clients: |
| 270 res_in_tmp_dir = os.path.join(tmp_paths['imported_clients'], client, 'res') | 251 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): | 252 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) | 253 res_in_final_dir = os.path.join(out_paths['res'], client) |
| 273 shutil.copytree(res_in_tmp_dir, res_in_final_dir) | 254 shutil.copytree(res_in_tmp_dir, res_in_final_dir) |
| 274 | 255 |
| 275 # Copy the jar | 256 # Copy the jar |
| 276 shutil.copyfile(tmp_paths['combined_jar'], out_paths['jar']) | 257 shutil.copyfile(tmp_paths['combined_jar'], out_paths['jar']) |
| 277 | 258 |
| 278 # Write the java dummy stub. Needed for gyp to create the resource jar | 259 # Write the java dummy stub. Needed for gyp to create the resource jar |
| 279 stub_location = os.path.join(out_paths['stub'], 'src', 'android') | 260 stub_location = os.path.join(out_paths['stub'], 'src', 'android') |
| 280 os.makedirs(stub_location) | 261 os.makedirs(stub_location) |
| 281 with open(os.path.join(stub_location, 'UnusedStub.java'), 'w') as stub: | 262 with open(os.path.join(stub_location, 'UnusedStub.java'), 'w') as stub: |
| 282 stub.write('package android;' | 263 stub.write('package android;' |
| 283 'public final class UnusedStub {' | 264 'public final class UnusedStub {' |
| 284 ' private UnusedStub() {}' | 265 ' private UnusedStub() {}' |
| 285 '}') | 266 '}') |
| 286 | 267 |
| 287 # Create the main res directory. It is needed by gyp | 268 # Create the main res directory. It is needed by gyp |
| 288 stub_res_location = os.path.join(out_paths['stub'], 'res') | 269 stub_res_location = os.path.join(out_paths['stub'], 'res') |
| 289 os.makedirs(stub_res_location) | 270 os.makedirs(stub_res_location) |
| 290 with open(os.path.join(stub_res_location, '.res-stamp'), 'w') as stamp: | 271 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' | 272 content_str = 'google_play_services_version: %s\nutc_date: %s\n' |
| 292 stamp.write(content_str % (play_services_full_version, generation_date)) | 273 stamp.write(content_str % (play_services_full_version, generation_date)) |
| 293 | 274 |
| 294 shutil.copyfile(tmp_version_file_path, | 275 shutil.copyfile(tmp_version_file_path, |
| 295 os.path.join(out_paths['root'], VERSION_FILE_NAME)) | 276 os.path.join(out_paths['root'], VERSION_FILE_NAME)) |
| 296 | 277 |
| 278 config.UpdateVersionNumber(play_services_full_version) | |
| 279 | |
| 280 | |
| 281 def _ExtractAll(zip_path, out_path): | |
| 282 with zipfile.ZipFile(zip_path, "r") as zip_file: | |
| 283 zip_file.extractall(out_path) | |
| 297 | 284 |
| 298 if __name__ == '__main__': | 285 if __name__ == '__main__': |
| 299 sys.exit(main()) | 286 sys.exit(main()) |
| OLD | NEW |