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 |