OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # |
| 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 |
| 5 # found in the LICENSE file. |
| 6 |
| 7 '''Prepares the Google Play services split client libraries before usage by |
| 8 Chrome's build system. |
| 9 |
| 10 We need to preprocess Google Play services before using it in Chrome |
| 11 builds for 2 main reasons: |
| 12 |
| 13 - Getting rid of unused resources: unsupported languages, unused |
| 14 drawables, etc. |
| 15 |
| 16 - Merging the differents jars so that it can be proguarded more |
| 17 easily. This is necessary since debug and test apks get very close |
| 18 to the dex limit. |
| 19 |
| 20 The script is supposed to be used with the maven repository that can be obtained |
| 21 by downloading the "extra-google-m2repository" from the Android SDK Manager. It |
| 22 also supports importing from already extracted AAR files using the |
| 23 --is-extracted-repo flag. |
| 24 |
| 25 The json config (see the -c argument) file should provide the following fields: |
| 26 |
| 27 - lib_version: String. Used when building from the maven repository. It should |
| 28 be the package's version (e.g. "7.3.0") |
| 29 |
| 30 - clients: String array. List of clients to pick. For example, when building |
| 31 from the maven repository, it's the artifactId (e.g. "play-services-base") of |
| 32 each client. |
| 33 |
| 34 - client_filter: String array. Pattern of files to prune from the clients once |
| 35 extracted. Metacharacters are allowed. (e.g. "res/drawable*") |
| 36 |
| 37 The output is a directory with the following structure: |
| 38 |
| 39 OUT_DIR |
| 40 +-- google-play-services.jar |
| 41 +-- res |
| 42 | +-- CLIENT_1 |
| 43 | | +-- color |
| 44 | | +-- values |
| 45 | | +-- etc. |
| 46 | +-- CLIENT_2 |
| 47 | +-- ... |
| 48 +-- stub |
| 49 +-- res/[.git-keep-directory] |
| 50 +-- src/android/UnusedStub.java |
| 51 |
| 52 Requires the `jar` utility in the path. |
| 53 |
| 54 ''' |
| 55 |
| 56 import argparse |
| 57 import glob |
| 58 import itertools |
| 59 import json |
| 60 import os |
| 61 import shutil |
| 62 import stat |
| 63 import sys |
| 64 |
| 65 from pylib import cmd_helper |
| 66 from pylib import constants |
| 67 |
| 68 sys.path.append( |
| 69 os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'android', 'gyp')) |
| 70 from util import build_utils |
| 71 |
| 72 |
| 73 M2_PKG_PATH = os.path.join('com', 'google', 'android', 'gms') |
| 74 |
| 75 |
| 76 def main(): |
| 77 parser = argparse.ArgumentParser(description=("Prepares the Google Play " |
| 78 "services split client libraries before usage by Chrome's build system")) |
| 79 parser.add_argument('-r', |
| 80 '--repository', |
| 81 help='The Google Play services repository location', |
| 82 required=True, |
| 83 metavar='FILE') |
| 84 parser.add_argument('-o', |
| 85 '--out-dir', |
| 86 help='The output directory', |
| 87 required=True, |
| 88 metavar='FILE') |
| 89 parser.add_argument('-c', |
| 90 '--config-file', |
| 91 help='Config file path', |
| 92 required=True, |
| 93 metavar='FILE') |
| 94 parser.add_argument('-g', |
| 95 '--git-friendly', |
| 96 action='store_true', |
| 97 default=False, |
| 98 help='Add a .gitkeep file to the empty directories') |
| 99 parser.add_argument('-x', |
| 100 '--is-extracted-repo', |
| 101 action='store_true', |
| 102 default=False, |
| 103 help='The provided repository is not made of AAR files.') |
| 104 |
| 105 args = parser.parse_args() |
| 106 |
| 107 ProcessGooglePlayServices(args.repository, |
| 108 args.out_dir, |
| 109 args.config_file, |
| 110 args.git_friendly, |
| 111 args.is_extracted_repo) |
| 112 |
| 113 |
| 114 def ProcessGooglePlayServices(repo, out_dir, config_path, git_friendly, |
| 115 is_extracted_repo): |
| 116 with open(config_path, 'r') as json_file: |
| 117 config = json.load(json_file) |
| 118 |
| 119 with build_utils.TempDir() as tmp_root: |
| 120 tmp_paths = _SetupTempDir(tmp_root) |
| 121 |
| 122 if is_extracted_repo: |
| 123 _ImportFromExtractedRepo(config, tmp_paths, repo) |
| 124 else: |
| 125 _ImportFromAars(config, tmp_paths, repo) |
| 126 |
| 127 _GenerateCombinedJar(tmp_paths) |
| 128 _ProcessResources(config, tmp_paths) |
| 129 _BuildOutput(config, tmp_paths, out_dir, git_friendly) |
| 130 |
| 131 |
| 132 def _SetupTempDir(tmp_root): |
| 133 tmp_paths = { |
| 134 'root': tmp_root, |
| 135 'imported_clients': os.path.join(tmp_root, 'imported_clients'), |
| 136 'extracted_jars': os.path.join(tmp_root, 'jar'), |
| 137 'combined_jar': os.path.join(tmp_root, 'google-play-services.jar'), |
| 138 } |
| 139 os.mkdir(tmp_paths['imported_clients']) |
| 140 os.mkdir(tmp_paths['extracted_jars']) |
| 141 |
| 142 return tmp_paths |
| 143 |
| 144 |
| 145 def _SetupOutputDir(out_dir): |
| 146 out_paths = { |
| 147 'root': out_dir, |
| 148 'res': os.path.join(out_dir, 'res'), |
| 149 'jar': os.path.join(out_dir, 'google-play-services.jar'), |
| 150 'stub': os.path.join(out_dir, 'stub'), |
| 151 } |
| 152 |
| 153 shutil.rmtree(out_paths['jar'], ignore_errors=True) |
| 154 shutil.rmtree(out_paths['res'], ignore_errors=True) |
| 155 shutil.rmtree(out_paths['stub'], ignore_errors=True) |
| 156 |
| 157 return out_paths |
| 158 |
| 159 |
| 160 def _MakeWritable(dir_path): |
| 161 for root, dirs, files in os.walk(dir_path): |
| 162 for path in itertools.chain(dirs, files): |
| 163 st = os.stat(os.path.join(root, path)) |
| 164 os.chmod(os.path.join(root, path), st.st_mode | stat.S_IWUSR) |
| 165 |
| 166 |
| 167 def _ImportFromAars(config, tmp_paths, repo): |
| 168 for client in config['clients']: |
| 169 aar_name = '%s-%s.aar' % (client, config['lib_version']) |
| 170 aar_path = os.path.join(repo, M2_PKG_PATH, client, |
| 171 config['lib_version'], aar_name) |
| 172 aar_out_path = os.path.join(tmp_paths['imported_clients'], client) |
| 173 build_utils.ExtractAll(aar_path, aar_out_path) |
| 174 |
| 175 client_jar_path = os.path.join(aar_out_path, 'classes.jar') |
| 176 build_utils.ExtractAll(client_jar_path, tmp_paths['extracted_jars'], |
| 177 no_clobber=False) |
| 178 |
| 179 |
| 180 def _ImportFromExtractedRepo(config, tmp_paths, repo): |
| 181 # Import the clients |
| 182 try: |
| 183 for client in config['clients']: |
| 184 client_out_dir = os.path.join(tmp_paths['imported_clients'], client) |
| 185 shutil.copytree(os.path.join(repo, client), client_out_dir) |
| 186 |
| 187 client_jar_path = os.path.join(client_out_dir, 'classes.jar') |
| 188 build_utils.ExtractAll(client_jar_path, tmp_paths['extracted_jars'], |
| 189 no_clobber=False) |
| 190 finally: |
| 191 _MakeWritable(tmp_paths['imported_clients']) |
| 192 |
| 193 |
| 194 def _GenerateCombinedJar(tmp_paths): |
| 195 out_file_name = tmp_paths['combined_jar'] |
| 196 working_dir = tmp_paths['extracted_jars'] |
| 197 cmd_helper.Call(['jar', '-cf', out_file_name, '-C', working_dir, '.']) |
| 198 |
| 199 |
| 200 def _ProcessResources(config, tmp_paths): |
| 201 # Prune unused resources |
| 202 for res_filter in config['client_filter']: |
| 203 glob_pattern = os.path.join(tmp_paths['imported_clients'], '*', res_filter) |
| 204 for prune_target in glob.glob(glob_pattern): |
| 205 shutil.rmtree(prune_target) |
| 206 |
| 207 |
| 208 def _BuildOutput(config, tmp_paths, out_dir, git_friendly): |
| 209 out_paths = _SetupOutputDir(out_dir) |
| 210 |
| 211 # Copy the resources to the output dir |
| 212 for client in config['clients']: |
| 213 res_in_tmp_dir = os.path.join(tmp_paths['imported_clients'], client, 'res') |
| 214 if os.path.isdir(res_in_tmp_dir) and os.listdir(res_in_tmp_dir): |
| 215 res_in_final_dir = os.path.join(out_paths['res'], client) |
| 216 shutil.copytree(res_in_tmp_dir, res_in_final_dir) |
| 217 |
| 218 # Copy the jar |
| 219 shutil.copyfile(tmp_paths['combined_jar'], out_paths['jar']) |
| 220 |
| 221 # Write the java dummy stub. Needed for gyp to create the resource jar |
| 222 stub_location = os.path.join(out_paths['stub'], 'src', 'android') |
| 223 os.makedirs(stub_location) |
| 224 with open(os.path.join(stub_location, 'UnusedStub.java'), 'w') as stub: |
| 225 stub.write('package android;' |
| 226 'public final class UnusedStub {' |
| 227 ' private UnusedStub() {}' |
| 228 '}') |
| 229 |
| 230 # Create the main res directory. Will be empty but is needed by gyp |
| 231 stub_res_location = os.path.join(out_paths['stub'], 'res') |
| 232 os.makedirs(stub_res_location) |
| 233 if git_friendly: |
| 234 build_utils.Touch(os.path.join(stub_res_location, '.git-keep-directory')) |
| 235 |
| 236 |
| 237 if __name__ == '__main__': |
| 238 sys.exit(main()) |
OLD | NEW |