OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # | 2 # |
3 # Copyright 2014 The Chromium Authors. All rights reserved. | 3 # Copyright 2014 The Chromium Authors. All rights reserved. |
4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 # pylint: disable=C0301 | 7 # pylint: disable=C0301 |
8 """Package resources into an apk. | 8 """Package resources into an apk. |
9 | 9 |
10 See https://android.googlesource.com/platform/tools/base/+/master/legacy/ant-tas ks/src/main/java/com/android/ant/AaptExecTask.java | 10 See https://android.googlesource.com/platform/tools/base/+/master/legacy/ant-tas ks/src/main/java/com/android/ant/AaptExecTask.java |
11 and | 11 and |
12 https://android.googlesource.com/platform/sdk/+/master/files/ant/build.xml | 12 https://android.googlesource.com/platform/sdk/+/master/files/ant/build.xml |
13 """ | 13 """ |
14 # pylint: enable=C0301 | 14 # pylint: enable=C0301 |
15 | 15 |
16 import optparse | 16 import optparse |
17 import os | 17 import os |
18 import re | 18 import re |
19 import shutil | 19 import shutil |
20 import sys | |
20 import zipfile | 21 import zipfile |
21 | 22 |
22 from util import build_utils | 23 from util import build_utils |
23 | 24 |
24 | 25 |
25 # List is generated from the chrome_apk.apk_intermediates.ap_ via: | 26 # List is generated from the chrome_apk.apk_intermediates.ap_ via: |
26 # unzip -l $FILE_AP_ | cut -c31- | grep res/draw | cut -d'/' -f 2 | sort \ | 27 # unzip -l $FILE_AP_ | cut -c31- | grep res/draw | cut -d'/' -f 2 | sort \ |
27 # | uniq | grep -- -tvdpi- | cut -c10- | 28 # | uniq | grep -- -tvdpi- | cut -c10- |
28 # and then manually sorted. | 29 # and then manually sorted. |
29 # Note that we can't just do a cross-product of dimentions because the filenames | 30 # Note that we can't just do a cross-product of dimentions because the filenames |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
64 'xxxhdpi-v21', | 65 'xxxhdpi-v21', |
65 ), | 66 ), |
66 'tvdpi': ( | 67 'tvdpi': ( |
67 'tvdpi-v4', | 68 'tvdpi-v4', |
68 'sw600dp-tvdpi-v13', | 69 'sw600dp-tvdpi-v13', |
69 'ldrtl-sw600dp-tvdpi-v17', | 70 'ldrtl-sw600dp-tvdpi-v17', |
70 ), | 71 ), |
71 } | 72 } |
72 | 73 |
73 | 74 |
74 def ParseArgs(): | 75 def _ParseArgs(args): |
75 """Parses command line options. | 76 """Parses command line options. |
76 | 77 |
77 Returns: | 78 Returns: |
78 An options object as from optparse.OptionsParser.parse_args() | 79 An options object as from optparse.OptionsParser.parse_args() |
79 """ | 80 """ |
80 parser = optparse.OptionParser() | 81 parser = optparse.OptionParser() |
81 build_utils.AddDepfileOption(parser) | 82 build_utils.AddDepfileOption(parser) |
82 parser.add_option('--android-sdk', help='path to the Android SDK folder') | 83 parser.add_option('--android-sdk', help='path to the Android SDK folder') |
83 parser.add_option('--aapt-path', | 84 parser.add_option('--aapt-path', |
84 help='path to the Android aapt tool') | 85 help='path to the Android aapt tool') |
85 | 86 |
86 parser.add_option('--configuration-name', | 87 parser.add_option('--configuration-name', |
87 help='Gyp\'s configuration name (Debug or Release).') | 88 help='Gyp\'s configuration name (Debug or Release).') |
88 | 89 |
89 parser.add_option('--android-manifest', help='AndroidManifest.xml path') | 90 parser.add_option('--android-manifest', help='AndroidManifest.xml path') |
90 parser.add_option('--version-code', help='Version code for apk.') | 91 parser.add_option('--version-code', help='Version code for apk.') |
91 parser.add_option('--version-name', help='Version name for apk.') | 92 parser.add_option('--version-name', help='Version name for apk.') |
92 parser.add_option( | 93 parser.add_option( |
93 '--shared-resources', | 94 '--shared-resources', |
94 action='store_true', | 95 action='store_true', |
95 help='Make a resource package that can be loaded by a different' | 96 help='Make a resource package that can be loaded by a different' |
96 'application at runtime to access the package\'s resources.') | 97 'application at runtime to access the package\'s resources.') |
97 parser.add_option( | 98 parser.add_option( |
98 '--app-as-shared-lib', | 99 '--app-as-shared-lib', |
99 action='store_true', | 100 action='store_true', |
100 help='Make a resource package that can be loaded as shared library') | 101 help='Make a resource package that can be loaded as shared library') |
101 parser.add_option('--resource-zips', | 102 parser.add_option('--resource-zips', |
103 default='[]', | |
102 help='zip files containing resources to be packaged') | 104 help='zip files containing resources to be packaged') |
103 parser.add_option('--asset-dir', | 105 parser.add_option('--asset-dir', |
104 help='directories containing assets to be packaged') | 106 help='directories containing assets to be packaged') |
105 parser.add_option('--no-compress', help='disables compression for the ' | 107 parser.add_option('--no-compress', help='disables compression for the ' |
106 'given comma separated list of extensions') | 108 'given comma separated list of extensions') |
107 parser.add_option( | 109 parser.add_option( |
108 '--create-density-splits', | 110 '--create-density-splits', |
109 action='store_true', | 111 action='store_true', |
110 help='Enables density splits') | 112 help='Enables density splits') |
111 parser.add_option('--language-splits', | 113 parser.add_option('--language-splits', |
114 default='[]', | |
112 help='GYP list of languages to create splits for') | 115 help='GYP list of languages to create splits for') |
113 | 116 |
114 parser.add_option('--apk-path', | 117 parser.add_option('--apk-path', |
115 help='Path to output (partial) apk.') | 118 help='Path to output (partial) apk.') |
116 | 119 |
117 (options, args) = parser.parse_args() | 120 options, positional_args = parser.parse_args(args) |
118 | 121 |
119 if args: | 122 if positional_args: |
120 parser.error('No positional arguments should be given.') | 123 parser.error('No positional arguments should be given.') |
121 | 124 |
122 # Check that required options have been provided. | 125 # Check that required options have been provided. |
123 required_options = ('android_sdk', 'aapt_path', 'configuration_name', | 126 required_options = ('android_sdk', 'aapt_path', 'configuration_name', |
124 'android_manifest', 'version_code', 'version_name', | 127 'android_manifest', 'version_code', 'version_name', |
125 'apk_path') | 128 'apk_path') |
126 | 129 |
127 build_utils.CheckOptions(options, parser, required=required_options) | 130 build_utils.CheckOptions(options, parser, required=required_options) |
128 | 131 |
132 options.resource_zips = build_utils.ParseGypList(options.resource_zips) | |
133 options.language_splits = build_utils.ParseGypList(options.language_splits) | |
129 return options | 134 return options |
130 | 135 |
131 | 136 |
132 def MoveImagesToNonMdpiFolders(res_root): | 137 def MoveImagesToNonMdpiFolders(res_root): |
133 """Move images from drawable-*-mdpi-* folders to drawable-* folders. | 138 """Move images from drawable-*-mdpi-* folders to drawable-* folders. |
134 | 139 |
135 Why? http://crbug.com/289843 | 140 Why? http://crbug.com/289843 |
136 """ | 141 """ |
137 for src_dir_name in os.listdir(res_root): | 142 for src_dir_name in os.listdir(res_root): |
138 src_components = src_dir_name.split('-') | 143 src_components = src_dir_name.split('-') |
(...skipping 30 matching lines...) Expand all Loading... | |
169 res_dirs = sorted(subdirs, key=lambda p : int(os.path.basename(p))) | 174 res_dirs = sorted(subdirs, key=lambda p : int(os.path.basename(p))) |
170 else: | 175 else: |
171 res_dirs = [d] | 176 res_dirs = [d] |
172 package_command = [] | 177 package_command = [] |
173 for d in res_dirs: | 178 for d in res_dirs: |
174 MoveImagesToNonMdpiFolders(d) | 179 MoveImagesToNonMdpiFolders(d) |
175 package_command += ['-S', d] | 180 package_command += ['-S', d] |
176 return package_command | 181 return package_command |
177 | 182 |
178 | 183 |
184 def _GenerateDensitySplitPaths(apk_path): | |
185 for density, config in DENSITY_SPLITS.iteritems(): | |
186 src_path = '%s_%s' % (apk_path, '_'.join(config)) | |
187 dst_path = '%s_%s' % (apk_path, density) | |
188 yield src_path, dst_path | |
189 | |
190 | |
191 def _GenerateLanguageSplitOutputPaths(apk_path, languages): | |
192 for lang in languages: | |
193 yield '%s_%s' % (apk_path, lang) | |
194 | |
195 | |
179 def RenameDensitySplits(apk_path): | 196 def RenameDensitySplits(apk_path): |
180 """Renames all density splits to have shorter / predictable names.""" | 197 """Renames all density splits to have shorter / predictable names.""" |
181 for density, config in DENSITY_SPLITS.iteritems(): | 198 for src_path, dst_path in _GenerateDensitySplitPaths(apk_path): |
182 src_path = '%s_%s' % (apk_path, '_'.join(config)) | 199 shutil.move(src_path, dst_path) |
183 dst_path = '%s_%s' % (apk_path, density) | |
184 if src_path != dst_path: | |
185 if os.path.exists(dst_path): | |
186 os.unlink(dst_path) | |
187 os.rename(src_path, dst_path) | |
188 | 200 |
189 | 201 |
190 def CheckForMissedConfigs(apk_path, check_density, languages): | 202 def CheckForMissedConfigs(apk_path, check_density, languages): |
191 """Raises an exception if apk_path contains any unexpected configs.""" | 203 """Raises an exception if apk_path contains any unexpected configs.""" |
192 triggers = [] | 204 triggers = [] |
193 if check_density: | 205 if check_density: |
194 triggers.extend(re.compile('-%s' % density) for density in DENSITY_SPLITS) | 206 triggers.extend(re.compile('-%s' % density) for density in DENSITY_SPLITS) |
195 if languages: | 207 if languages: |
196 triggers.extend(re.compile(r'-%s\b' % lang) for lang in languages) | 208 triggers.extend(re.compile(r'-%s\b' % lang) for lang in languages) |
197 with zipfile.ZipFile(apk_path) as main_apk_zip: | 209 with zipfile.ZipFile(apk_path) as main_apk_zip: |
198 for name in main_apk_zip.namelist(): | 210 for name in main_apk_zip.namelist(): |
199 for trigger in triggers: | 211 for trigger in triggers: |
200 if trigger.search(name) and not 'mipmap-' in name: | 212 if trigger.search(name) and not 'mipmap-' in name: |
201 raise Exception(('Found config in main apk that should have been ' + | 213 raise Exception(('Found config in main apk that should have been ' + |
202 'put into a split: %s\nYou need to update ' + | 214 'put into a split: %s\nYou need to update ' + |
203 'package_resources.py to include this new ' + | 215 'package_resources.py to include this new ' + |
204 'config (trigger=%s)') % (name, trigger.pattern)) | 216 'config (trigger=%s)') % (name, trigger.pattern)) |
205 | 217 |
206 | 218 |
207 def main(): | 219 def _ConstructMostAaptArgs(options): |
208 options = ParseArgs() | 220 package_command = [ |
209 android_jar = os.path.join(options.android_sdk, 'android.jar') | 221 options.aapt_path, |
210 aapt = options.aapt_path | 222 'package', |
223 '--version-code', options.version_code, | |
224 '--version-name', options.version_name, | |
225 '-M', options.android_manifest, | |
226 '--no-crunch', | |
227 '-f', | |
228 '--auto-add-overlay', | |
229 '-I', os.path.join(options.android_sdk, 'android.jar'), | |
230 '-F', options.apk_path, | |
231 '--ignore-assets', build_utils.AAPT_IGNORE_PATTERN, | |
232 ] | |
pkotwicz
2015/09/24 19:21:36
Nit: I think that ']' should have the same indent
agrieve
2015/09/25 02:14:35
Done.
| |
233 if options.no_compress: | |
pkotwicz
2015/09/24 19:21:36
Nit: A blank line between lines 232 and 233?
agrieve
2015/09/25 02:14:35
Done.
| |
234 for ext in options.no_compress.split(','): | |
235 package_command += ['-0', ext] | |
211 | 236 |
237 if options.shared_resources: | |
238 package_command.append('--shared-lib') | |
239 | |
240 if options.app_as_shared_lib: | |
241 package_command.append('--app-as-shared-lib') | |
242 | |
243 if options.asset_dir and os.path.exists(options.asset_dir): | |
244 package_command += ['-A', options.asset_dir] | |
245 | |
246 if options.create_density_splits: | |
247 for config in DENSITY_SPLITS.itervalues(): | |
248 package_command.extend(('--split', ','.join(config))) | |
249 | |
250 if options.language_splits: | |
251 for lang in options.language_splits: | |
252 package_command.extend(('--split', lang)) | |
253 | |
254 if 'Debug' in options.configuration_name: | |
255 package_command += ['--debug-mode'] | |
256 | |
257 return package_command | |
258 | |
pkotwicz
2015/09/24 19:21:36
Nit: Remove one of the blank line?
agrieve
2015/09/25 02:14:36
Style guide says 2 lines between top-level definit
| |
259 | |
260 def _OnStaleMd5(package_command, options): | |
212 with build_utils.TempDir() as temp_dir: | 261 with build_utils.TempDir() as temp_dir: |
213 package_command = [aapt, | |
214 'package', | |
215 '--version-code', options.version_code, | |
216 '--version-name', options.version_name, | |
217 '-M', options.android_manifest, | |
218 '--no-crunch', | |
219 '-f', | |
220 '--auto-add-overlay', | |
221 '-I', android_jar, | |
222 '-F', options.apk_path, | |
223 '--ignore-assets', build_utils.AAPT_IGNORE_PATTERN, | |
224 ] | |
225 | |
226 if options.no_compress: | |
227 for ext in options.no_compress.split(','): | |
228 package_command += ['-0', ext] | |
229 if options.shared_resources: | |
230 package_command.append('--shared-lib') | |
231 if options.app_as_shared_lib: | |
232 package_command.append('--app-as-shared-lib') | |
233 | |
234 if options.asset_dir and os.path.exists(options.asset_dir): | |
235 package_command += ['-A', options.asset_dir] | |
236 | |
237 if options.resource_zips: | 262 if options.resource_zips: |
238 dep_zips = build_utils.ParseGypList(options.resource_zips) | 263 dep_zips = options.resource_zips |
239 for z in dep_zips: | 264 for z in dep_zips: |
240 subdir = os.path.join(temp_dir, os.path.basename(z)) | 265 subdir = os.path.join(temp_dir, os.path.basename(z)) |
241 if os.path.exists(subdir): | 266 if os.path.exists(subdir): |
242 raise Exception('Resource zip name conflict: ' + os.path.basename(z)) | 267 raise Exception('Resource zip name conflict: ' + os.path.basename(z)) |
243 build_utils.ExtractAll(z, path=subdir) | 268 build_utils.ExtractAll(z, path=subdir) |
244 package_command += PackageArgsForExtractedZip(subdir) | 269 package_command += PackageArgsForExtractedZip(subdir) |
245 | 270 |
246 if options.create_density_splits: | |
247 for config in DENSITY_SPLITS.itervalues(): | |
248 package_command.extend(('--split', ','.join(config))) | |
249 | |
250 language_splits = None | |
251 if options.language_splits: | |
252 language_splits = build_utils.ParseGypList(options.language_splits) | |
253 for lang in language_splits: | |
254 package_command.extend(('--split', lang)) | |
255 | |
256 if 'Debug' in options.configuration_name: | |
257 package_command += ['--debug-mode'] | |
258 | |
259 build_utils.CheckOutput( | 271 build_utils.CheckOutput( |
260 package_command, print_stdout=False, print_stderr=False) | 272 package_command, print_stdout=False, print_stderr=False) |
261 | 273 |
262 if options.create_density_splits or language_splits: | 274 if options.create_density_splits or options.language_splits: |
263 CheckForMissedConfigs( | 275 CheckForMissedConfigs(options.apk_path, options.create_density_splits, |
264 options.apk_path, options.create_density_splits, language_splits) | 276 options.language_splits) |
265 | 277 |
266 if options.create_density_splits: | 278 if options.create_density_splits: |
267 RenameDensitySplits(options.apk_path) | 279 RenameDensitySplits(options.apk_path) |
268 | 280 |
269 if options.depfile: | 281 |
270 build_utils.WriteDepfile( | 282 def main(args): |
271 options.depfile, | 283 args = build_utils.ExpandFileArgs(args) |
272 build_utils.GetPythonDependencies()) | 284 options = _ParseArgs(args) |
285 | |
286 package_command = _ConstructMostAaptArgs(options) | |
287 | |
288 output_paths = [ options.apk_path ] | |
289 | |
290 if options.create_density_splits: | |
291 for _, dst_path in _GenerateDensitySplitPaths(options.apk_path): | |
292 output_paths.append(dst_path) | |
293 output_paths.extend( | |
294 _GenerateLanguageSplitOutputPaths(options.apk_path, | |
295 options.language_splits)) | |
296 | |
297 input_paths = [ options.android_manifest ] + options.resource_zips | |
298 if options.asset_dir and os.path.exists(options.asset_dir): | |
299 for root, _, filenames in os.walk(options.asset_dir): | |
300 input_paths.extend(os.path.join(root, f) for f in filenames) | |
301 | |
302 build_utils.CallAndWriteDepfileIfStale( | |
303 lambda: _OnStaleMd5(package_command, options), | |
304 options, | |
305 input_paths=input_paths, | |
306 input_strings=package_command, | |
307 output_paths=output_paths) | |
273 | 308 |
274 | 309 |
275 if __name__ == '__main__': | 310 if __name__ == '__main__': |
276 main() | 311 main(sys.argv[1:]) |
OLD | NEW |