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
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
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
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
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:
40 +-- google-play-services.jar
49 +-- res/[.git-keep-directory]
50 +-- src/android/UnusedStub.java
52 Requires the `jar` utility in the path.
65 from pylib
import cmd_helper
66 from pylib
import constants
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')
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',
81 help='The Google Play services repository location',
84 parser
.add_argument('-o',
86 help='The output directory',
89 parser
.add_argument('-c',
91 help='Config file path',
94 parser
.add_argument('-g',
98 help='Add a .gitkeep file to the empty directories')
99 parser
.add_argument('-x',
100 '--is-extracted-repo',
103 help='The provided repository is not made of AAR files.')
105 args
= parser
.parse_args()
107 ProcessGooglePlayServices(args
.repository
,
111 args
.is_extracted_repo
)
114 def ProcessGooglePlayServices(repo
, out_dir
, config_path
, git_friendly
,
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
)
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
):
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'])
145 def _SetupOutputDir(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)
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'],
180 def _ImportFromExtractedRepo(config
, tmp_paths
, repo
):
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'],
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
)
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() {}'
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
)
234 build_utils
.Touch(os
.path
.join(stub_res_location
, '.git-keep-directory'))
237 if __name__
== '__main__':