Add ICU message format support
[chromium-blink-merge.git] / build / android / preprocess_google_play_services.py
blob717a021f6d1b8d46bfa58df6107c65f6363704e4
1 #!/usr/bin/env python
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.
7 '''Prepares the Google Play services split client libraries before usage by
8 Chrome's build system.
10 We need to preprocess Google Play services before using it in Chrome
11 builds for 2 main reasons:
13 - Getting rid of unused resources: unsupported languages, unused
14 drawables, etc.
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.
20 The script is supposed to be used with the maven repository that can be
21 obtained by downloading the "extra-google-m2repository" from the Android SDK
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
24 look like:
26 REPOSITORY_DIR
27 +-- CLIENT_1
28 | +-- <content of the first AAR file>
29 +-- CLIENT_2
30 +-- etc.
32 The json config (see the -c argument) file should provide the following fields:
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.
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.
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").
46 The output is a directory with the following structure:
48 OUT_DIR
49 +-- google-play-services.jar
50 +-- res
51 | +-- CLIENT_1
52 | | +-- color
53 | | +-- values
54 | | +-- etc.
55 | +-- CLIENT_2
56 | +-- ...
57 +-- stub
58 +-- res/[.git-keep-directory]
59 +-- src/android/UnusedStub.java
61 Requires the `jar` utility in the path.
63 '''
65 import argparse
66 import glob
67 import itertools
68 import json
69 import os
70 import shutil
71 import stat
72 import sys
74 from pylib import cmd_helper
75 from pylib import constants
77 sys.path.append(
78 os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'android', 'gyp'))
79 from util import build_utils
82 M2_PKG_PATH = os.path.join('com', 'google', 'android', 'gms')
85 def main():
86 parser = argparse.ArgumentParser(description=("Prepares the Google Play "
87 "services split client libraries before usage by Chrome's build system. "
88 "See the script's documentation for more a detailed help."))
89 parser.add_argument('-r',
90 '--repository',
91 help='The Google Play services repository location',
92 required=True,
93 metavar='FILE')
94 parser.add_argument('-o',
95 '--out-dir',
96 help='The output directory',
97 required=True,
98 metavar='FILE')
99 parser.add_argument('-c',
100 '--config-file',
101 help='Config file path',
102 required=True,
103 metavar='FILE')
104 parser.add_argument('-g',
105 '--git-friendly',
106 action='store_true',
107 default=False,
108 help='Add a .gitkeep file to the empty directories')
109 parser.add_argument('-x',
110 '--is-extracted-repo',
111 action='store_true',
112 default=False,
113 help='The provided repository is not made of AAR files.')
115 args = parser.parse_args()
117 ProcessGooglePlayServices(args.repository,
118 args.out_dir,
119 args.config_file,
120 args.git_friendly,
121 args.is_extracted_repo)
124 def ProcessGooglePlayServices(repo, out_dir, config_path, git_friendly,
125 is_extracted_repo):
126 with open(config_path, 'r') as json_file:
127 config = json.load(json_file)
129 with build_utils.TempDir() as tmp_root:
130 tmp_paths = _SetupTempDir(tmp_root)
132 if is_extracted_repo:
133 _ImportFromExtractedRepo(config, tmp_paths, repo)
134 else:
135 _ImportFromAars(config, tmp_paths, repo)
137 _GenerateCombinedJar(tmp_paths)
138 _ProcessResources(config, tmp_paths)
139 _BuildOutput(config, tmp_paths, out_dir, git_friendly)
142 def _SetupTempDir(tmp_root):
143 tmp_paths = {
144 'root': tmp_root,
145 'imported_clients': os.path.join(tmp_root, 'imported_clients'),
146 'extracted_jars': os.path.join(tmp_root, 'jar'),
147 'combined_jar': os.path.join(tmp_root, 'google-play-services.jar'),
149 os.mkdir(tmp_paths['imported_clients'])
150 os.mkdir(tmp_paths['extracted_jars'])
152 return tmp_paths
155 def _SetupOutputDir(out_dir):
156 out_paths = {
157 'root': out_dir,
158 'res': os.path.join(out_dir, 'res'),
159 'jar': os.path.join(out_dir, 'google-play-services.jar'),
160 'stub': os.path.join(out_dir, 'stub'),
163 shutil.rmtree(out_paths['jar'], ignore_errors=True)
164 shutil.rmtree(out_paths['res'], ignore_errors=True)
165 shutil.rmtree(out_paths['stub'], ignore_errors=True)
167 return out_paths
170 def _MakeWritable(dir_path):
171 for root, dirs, files in os.walk(dir_path):
172 for path in itertools.chain(dirs, files):
173 st = os.stat(os.path.join(root, path))
174 os.chmod(os.path.join(root, path), st.st_mode | stat.S_IWUSR)
177 def _ImportFromAars(config, tmp_paths, repo):
178 for client in config['clients']:
179 aar_name = '%s-%s.aar' % (client, config['lib_version'])
180 aar_path = os.path.join(repo, M2_PKG_PATH, client,
181 config['lib_version'], aar_name)
182 aar_out_path = os.path.join(tmp_paths['imported_clients'], client)
183 build_utils.ExtractAll(aar_path, aar_out_path)
185 client_jar_path = os.path.join(aar_out_path, 'classes.jar')
186 build_utils.ExtractAll(client_jar_path, tmp_paths['extracted_jars'],
187 no_clobber=False)
190 def _ImportFromExtractedRepo(config, tmp_paths, repo):
191 # Import the clients
192 try:
193 for client in config['clients']:
194 client_out_dir = os.path.join(tmp_paths['imported_clients'], client)
195 shutil.copytree(os.path.join(repo, client), client_out_dir)
197 client_jar_path = os.path.join(client_out_dir, 'classes.jar')
198 build_utils.ExtractAll(client_jar_path, tmp_paths['extracted_jars'],
199 no_clobber=False)
200 finally:
201 _MakeWritable(tmp_paths['imported_clients'])
204 def _GenerateCombinedJar(tmp_paths):
205 out_file_name = tmp_paths['combined_jar']
206 working_dir = tmp_paths['extracted_jars']
207 cmd_helper.Call(['jar', '-cf', out_file_name, '-C', working_dir, '.'])
210 def _ProcessResources(config, tmp_paths):
211 LOCALIZED_VALUES_BASE_NAME = 'values-'
212 locale_whitelist = set(config['locale_whitelist'])
214 glob_pattern = os.path.join(tmp_paths['imported_clients'], '*', 'res', '*')
215 for res_dir in glob.glob(glob_pattern):
216 dir_name = os.path.basename(res_dir)
218 if dir_name.startswith('drawable'):
219 shutil.rmtree(res_dir)
220 continue
222 if dir_name.startswith(LOCALIZED_VALUES_BASE_NAME):
223 dir_locale = dir_name[len(LOCALIZED_VALUES_BASE_NAME):]
224 if dir_locale not in locale_whitelist:
225 shutil.rmtree(res_dir)
228 def _BuildOutput(config, tmp_paths, out_dir, git_friendly):
229 out_paths = _SetupOutputDir(out_dir)
231 # Copy the resources to the output dir
232 for client in config['clients']:
233 res_in_tmp_dir = os.path.join(tmp_paths['imported_clients'], client, 'res')
234 if os.path.isdir(res_in_tmp_dir) and os.listdir(res_in_tmp_dir):
235 res_in_final_dir = os.path.join(out_paths['res'], client)
236 shutil.copytree(res_in_tmp_dir, res_in_final_dir)
238 # Copy the jar
239 shutil.copyfile(tmp_paths['combined_jar'], out_paths['jar'])
241 # Write the java dummy stub. Needed for gyp to create the resource jar
242 stub_location = os.path.join(out_paths['stub'], 'src', 'android')
243 os.makedirs(stub_location)
244 with open(os.path.join(stub_location, 'UnusedStub.java'), 'w') as stub:
245 stub.write('package android;'
246 'public final class UnusedStub {'
247 ' private UnusedStub() {}'
248 '}')
250 # Create the main res directory. Will be empty but is needed by gyp
251 stub_res_location = os.path.join(out_paths['stub'], 'res')
252 os.makedirs(stub_res_location)
253 if git_friendly:
254 build_utils.Touch(os.path.join(stub_res_location, '.git-keep-directory'))
257 if __name__ == '__main__':
258 sys.exit(main())