Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / build / android / preprocess_google_play_services.py
blob85d239ad30200ef5a8aef5d4abe722b433e7f383
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 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.
25 The json config (see the -c argument) file should provide the following fields:
27 - lib_version: String. Used when building from the maven repository. It should
28 be the package's version (e.g. "7.3.0")
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.
34 - client_filter: String array. Pattern of files to prune from the clients once
35 extracted. Metacharacters are allowed. (e.g. "res/drawable*")
37 The output is a directory with the following structure:
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
52 Requires the `jar` utility in the path.
54 '''
56 import argparse
57 import glob
58 import itertools
59 import json
60 import os
61 import shutil
62 import stat
63 import sys
65 from pylib import cmd_helper
66 from pylib import constants
68 sys.path.append(
69 os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'android', 'gyp'))
70 from util import build_utils
73 M2_PKG_PATH = os.path.join('com', 'google', 'android', 'gms')
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.')
105 args = parser.parse_args()
107 ProcessGooglePlayServices(args.repository,
108 args.out_dir,
109 args.config_file,
110 args.git_friendly,
111 args.is_extracted_repo)
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)
119 with build_utils.TempDir() as tmp_root:
120 tmp_paths = _SetupTempDir(tmp_root)
122 if is_extracted_repo:
123 _ImportFromExtractedRepo(config, tmp_paths, repo)
124 else:
125 _ImportFromAars(config, tmp_paths, repo)
127 _GenerateCombinedJar(tmp_paths)
128 _ProcessResources(config, tmp_paths)
129 _BuildOutput(config, tmp_paths, out_dir, git_friendly)
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'),
139 os.mkdir(tmp_paths['imported_clients'])
140 os.mkdir(tmp_paths['extracted_jars'])
142 return tmp_paths
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'),
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)
157 return out_paths
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)
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)
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)
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)
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'])
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, '.'])
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)
208 def _BuildOutput(config, tmp_paths, out_dir, git_friendly):
209 out_paths = _SetupOutputDir(out_dir)
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)
218 # Copy the jar
219 shutil.copyfile(tmp_paths['combined_jar'], out_paths['jar'])
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 '}')
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'))
237 if __name__ == '__main__':
238 sys.exit(main())