| 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 | 
|---|