Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(516)

Side by Side Diff: build/android/gyp/process_resources.py

Issue 2081473002: 🎂 Refactor process_resources.py to use aapt's --extra-packages (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: ensure R.txt is always created Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # 2 #
3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 3 # Copyright (c) 2012 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 """Process Android resources to generate R.java, and prepare for packaging. 7 """Process Android resources to generate R.java, and prepare for packaging.
8 8
9 This will crunch images and generate v14 compatible resources 9 This will crunch images and generate v14 compatible resources
10 (see generate_v14_compatible_resources.py). 10 (see generate_v14_compatible_resources.py).
11 """ 11 """
12 12
13 import codecs 13 import codecs
14 import collections 14 import collections
15 import optparse 15 import optparse
16 import os 16 import os
17 import re 17 import re
18 import shutil 18 import shutil
19 import sys 19 import sys
20 import xml.etree.ElementTree
20 21
21 import generate_v14_compatible_resources 22 import generate_v14_compatible_resources
22 23
23 from util import build_utils 24 from util import build_utils
24 25
25 # Import jinja2 from third_party/jinja2 26 # Import jinja2 from third_party/jinja2
26 sys.path.insert(1, 27 sys.path.insert(1,
27 os.path.join(os.path.dirname(__file__), '../../../third_party')) 28 os.path.join(os.path.dirname(__file__), '../../../third_party'))
28 from jinja2 import Template # pylint: disable=F0401 29 from jinja2 import Template # pylint: disable=F0401
29 30
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
136 137
137 if options.extra_r_text_files: 138 if options.extra_r_text_files:
138 options.extra_r_text_files = ( 139 options.extra_r_text_files = (
139 build_utils.ParseGypList(options.extra_r_text_files)) 140 build_utils.ParseGypList(options.extra_r_text_files))
140 else: 141 else:
141 options.extra_r_text_files = [] 142 options.extra_r_text_files = []
142 143
143 return options 144 return options
144 145
145 146
146 def CreateExtraRJavaFiles( 147 def CreateRJavaFiles(srcjar_dir, main_r_txt_file, packages, r_txt_files,
147 r_dir, extra_packages, extra_r_text_files, shared_resources, include_all): 148 shared_resources):
148 if include_all: 149 assert len(packages) == len(r_txt_files), 'Need one R.txt file per package'
149 java_files = build_utils.FindInDirectory(r_dir, "R.java")
150 if len(java_files) != 1:
151 return
152 r_java_file = java_files[0]
153 r_java_contents = codecs.open(r_java_file, encoding='utf-8').read()
154 150
155 for package in extra_packages: 151 # Map of (resource_type, name) -> Entry.
156 package_r_java_dir = os.path.join(r_dir, *package.split('.')) 152 # Contains the correct values for resources.
157 build_utils.MakeDirectory(package_r_java_dir) 153 all_resources = {}
158 package_r_java_path = os.path.join(package_r_java_dir, 'R.java') 154 for entry in _ParseTextSymbolsFile(main_r_txt_file):
159 new_r_java = re.sub(r'package [.\w]*;', u'package %s;' % package, 155 all_resources[(entry.resource_type, entry.name)] = entry
160 r_java_contents)
161 codecs.open(package_r_java_path, 'w', encoding='utf-8').write(new_r_java)
162 else:
163 if len(extra_packages) != len(extra_r_text_files):
164 raise Exception('Need one R.txt file per extra package')
165 156
166 r_txt_file = os.path.join(r_dir, 'R.txt') 157 # Map of package_name->resource_type->entry
167 if not os.path.exists(r_txt_file): 158 resources_by_package = (
168 return 159 collections.defaultdict(lambda: collections.defaultdict(list)))
160 # Build the R.java files using each package's R.txt file, but replacing
161 # each entry's placeholder value with correct values from all_resources.
162 for package, r_txt_file in zip(packages, r_txt_files):
163 if package in resources_by_package:
164 raise Exception(('Package name "%s" appeared twice. All '
165 'android_resources() targets must use unique package '
166 'names, or no package name at all.') % package)
167 resources_by_type = resources_by_package[package]
168 # The sub-R.txt files have the wrong values at this point. Read them to
169 # figure out which entries belong to them, but use the values from the
170 # main R.txt file.
171 for entry in _ParseTextSymbolsFile(r_txt_file):
172 entry = all_resources[(entry.resource_type, entry.name)]
173 resources_by_type[entry.resource_type].append(entry)
169 174
170 # Map of (resource_type, name) -> Entry. 175 for package, resources_by_type in resources_by_package.iteritems():
171 # Contains the correct values for resources. 176 package_r_java_dir = os.path.join(srcjar_dir, *package.split('.'))
172 all_resources = {} 177 build_utils.MakeDirectory(package_r_java_dir)
173 for entry in _ParseTextSymbolsFile(r_txt_file): 178 package_r_java_path = os.path.join(package_r_java_dir, 'R.java')
174 all_resources[(entry.resource_type, entry.name)] = entry 179 java_file_contents = _CreateExtraRJavaFile(
175 180 package, resources_by_type, shared_resources)
176 # Map of package_name->resource_type->entry 181 with open(package_r_java_path, 'w') as f:
177 resources_by_package = ( 182 f.write(java_file_contents)
178 collections.defaultdict(lambda: collections.defaultdict(list)))
179 # Build the R.java files using each package's R.txt file, but replacing
180 # each entry's placeholder value with correct values from all_resources.
181 for package, r_text_file in zip(extra_packages, extra_r_text_files):
182 if not os.path.exists(r_text_file):
183 continue
184 if package in resources_by_package:
185 raise Exception(('Package name "%s" appeared twice. All '
186 'android_resources() targets must use unique package '
187 'names, or no package name at all.') % package)
188 resources_by_type = resources_by_package[package]
189 # The sub-R.txt files have the wrong values at this point. Read them to
190 # figure out which entries belong to them, but use the values from the
191 # main R.txt file.
192 for entry in _ParseTextSymbolsFile(r_text_file):
193 entry = all_resources[(entry.resource_type, entry.name)]
194 resources_by_type[entry.resource_type].append(entry)
195
196 for package, resources_by_type in resources_by_package.iteritems():
197 package_r_java_dir = os.path.join(r_dir, *package.split('.'))
198 build_utils.MakeDirectory(package_r_java_dir)
199 package_r_java_path = os.path.join(package_r_java_dir, 'R.java')
200 java_file_contents = _CreateExtraRJavaFile(
201 package, resources_by_type, shared_resources)
202 with open(package_r_java_path, 'w') as f:
203 f.write(java_file_contents)
204 183
205 184
206 def _ParseTextSymbolsFile(path): 185 def _ParseTextSymbolsFile(path):
207 """Given an R.txt file, returns a list of TextSymbolsEntry.""" 186 """Given an R.txt file, returns a list of TextSymbolsEntry."""
208 ret = [] 187 ret = []
209 with open(path) as f: 188 with open(path) as f:
210 for line in f: 189 for line in f:
211 m = re.match(r'(int(?:\[\])?) (\w+) (\w+) (.+)$', line) 190 m = re.match(r'(int(?:\[\])?) (\w+) (\w+) (.+)$', line)
212 if not m: 191 if not m:
213 raise Exception('Unexpected line in R.txt: %s' % line) 192 raise Exception('Unexpected line in R.txt: %s' % line)
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
334 # of the form 0, 1, ..., then each subdirectory will be passed to aapt as a 313 # of the form 0, 1, ..., then each subdirectory will be passed to aapt as a
335 # resources directory. While some resources just clobber others (image files, 314 # resources directory. While some resources just clobber others (image files,
336 # etc), other resources (particularly .xml files) need to be more 315 # etc), other resources (particularly .xml files) need to be more
337 # intelligently merged. That merging is left up to aapt. 316 # intelligently merged. That merging is left up to aapt.
338 def path_transform(name, src_zip): 317 def path_transform(name, src_zip):
339 return '%d/%s' % (zip_files.index(src_zip), name) 318 return '%d/%s' % (zip_files.index(src_zip), name)
340 319
341 build_utils.MergeZips(output_path, zip_files, path_transform=path_transform) 320 build_utils.MergeZips(output_path, zip_files, path_transform=path_transform)
342 321
343 322
323 def _ExtractPackageFromManifest(manifest_path):
324 doc = xml.etree.ElementTree.parse(manifest_path)
325 return doc.getroot().get('package')
326
327
344 def _OnStaleMd5(options): 328 def _OnStaleMd5(options):
345 aapt = options.aapt_path 329 aapt = options.aapt_path
346 with build_utils.TempDir() as temp_dir: 330 with build_utils.TempDir() as temp_dir:
347 deps_dir = os.path.join(temp_dir, 'deps') 331 deps_dir = os.path.join(temp_dir, 'deps')
348 build_utils.MakeDirectory(deps_dir) 332 build_utils.MakeDirectory(deps_dir)
349 v14_dir = os.path.join(temp_dir, 'v14') 333 v14_dir = os.path.join(temp_dir, 'v14')
350 build_utils.MakeDirectory(v14_dir) 334 build_utils.MakeDirectory(v14_dir)
351 335
352 gen_dir = os.path.join(temp_dir, 'gen') 336 gen_dir = os.path.join(temp_dir, 'gen')
353 build_utils.MakeDirectory(gen_dir) 337 build_utils.MakeDirectory(gen_dir)
338 r_txt_path = os.path.join(gen_dir, 'R.txt')
339 srcjar_dir = os.path.join(temp_dir, 'java')
354 340
355 input_resource_dirs = options.resource_dirs 341 input_resource_dirs = options.resource_dirs
356 342
357 if not options.v14_skip: 343 if not options.v14_skip:
358 for resource_dir in input_resource_dirs: 344 for resource_dir in input_resource_dirs:
359 generate_v14_compatible_resources.GenerateV14Resources( 345 generate_v14_compatible_resources.GenerateV14Resources(
360 resource_dir, 346 resource_dir,
361 v14_dir) 347 v14_dir)
362 348
363 dep_zips = options.dependencies_res_zips 349 dep_zips = options.dependencies_res_zips
(...skipping 11 matching lines...) Expand all
375 # generated by merging the resources from all libraries and the main apk 361 # generated by merging the resources from all libraries and the main apk
376 # project. 362 # project.
377 package_command = [aapt, 363 package_command = [aapt,
378 'package', 364 'package',
379 '-m', 365 '-m',
380 '-M', options.android_manifest, 366 '-M', options.android_manifest,
381 '--auto-add-overlay', 367 '--auto-add-overlay',
382 '--no-version-vectors', 368 '--no-version-vectors',
383 '-I', options.android_sdk_jar, 369 '-I', options.android_sdk_jar,
384 '--output-text-symbols', gen_dir, 370 '--output-text-symbols', gen_dir,
385 '-J', gen_dir, 371 '-J', gen_dir, # Required for R.txt generation.
386 '--ignore-assets', build_utils.AAPT_IGNORE_PATTERN] 372 '--ignore-assets', build_utils.AAPT_IGNORE_PATTERN]
387 373
374 # aapt supports only the "--include-all-resources" mode, where each R.java
375 # file ends up with all symbols, rather than only those that it had at the
376 # time it was originally generated. This subtle difference makes no
377 # difference when compiling, but can lead to increased unused symbols in the
378 # resulting R.class files.
379 # TODO(agrieve): See if proguard makes this difference actually translate
380 # into a size difference. If not, we can delete all of our custom R.java
381 # template code above (and make include_all_resources the default).
382 if options.include_all_resources:
383 srcjar_dir = gen_dir
384 if options.extra_res_packages:
385 colon_separated = ':'.join(options.extra_res_packages)
386 package_command += ['--extra-packages', colon_separated]
387 if options.non_constant_id:
388 package_command.append('--non-constant-id')
389 if options.custom_package:
390 package_command += ['--custom-package', options.custom_package]
391 if options.shared_resources:
392 package_command.append('--shared-lib')
393 if options.app_as_shared_lib:
394 package_command.append('--app-as-shared-lib')
395
388 for d in input_resource_dirs: 396 for d in input_resource_dirs:
389 package_command += ['-S', d] 397 package_command += ['-S', d]
390 398
399 # Adding all dependencies as sources is necessary for @type/foo references
400 # to symbols within dependencies to resolve. However, it has the side-effect
401 # that all Java symbols from dependencies are copied into the new R.java.
402 # E.g.: It enables an arguably incorrect usage of
403 # "mypackage.R.id.lib_symbol" where "libpackage.R.id.lib_symbol" would be
404 # more correct. This is just how Android works.
391 for d in dep_subdirs: 405 for d in dep_subdirs:
392 package_command += ['-S', d] 406 package_command += ['-S', d]
393 407
394 if options.non_constant_id:
395 package_command.append('--non-constant-id')
396 if options.custom_package:
397 package_command += ['--custom-package', options.custom_package]
398 if options.proguard_file: 408 if options.proguard_file:
399 package_command += ['-G', options.proguard_file] 409 package_command += ['-G', options.proguard_file]
400 if options.shared_resources:
401 package_command.append('--shared-lib')
402 if options.app_as_shared_lib:
403 package_command.append('--app-as-shared-lib')
404 build_utils.CheckOutput(package_command, print_stderr=False) 410 build_utils.CheckOutput(package_command, print_stderr=False)
405 411
406 if options.extra_res_packages: 412 # When an empty res/ directory is passed, aapt does not write an R.txt.
407 CreateExtraRJavaFiles( 413 if not os.path.exists(r_txt_path):
408 gen_dir, 414 build_utils.Touch(r_txt_path)
409 options.extra_res_packages, 415
410 options.extra_r_text_files, 416 if not options.include_all_resources:
411 options.shared_resources or options.app_as_shared_lib, 417 packages = list(options.extra_res_packages)
412 options.include_all_resources) 418 r_txt_files = list(options.extra_r_text_files)
419
420 cur_package = options.custom_package
421 if not options.custom_package:
422 cur_package = _ExtractPackageFromManifest(options.android_manifest)
423
424 # Don't create a .java file for the current resource target when:
425 # - no package name was provided (either by manifest or build rules),
426 # - there was already a dependent android_resources() with the same
427 # package (occurs mostly when an apk target and resources target share
428 # an AndroidManifest.xml)
429 if cur_package != 'dummy.package' and cur_package not in packages:
430 packages.append(cur_package)
431 r_txt_files.append(r_txt_path)
432
433 if packages:
434 shared_resources = options.shared_resources or options.app_as_shared_lib
435 CreateRJavaFiles(srcjar_dir, r_txt_path, packages, r_txt_files,
436 shared_resources)
413 437
414 # This is the list of directories with resources to put in the final .zip 438 # This is the list of directories with resources to put in the final .zip
415 # file. The order of these is important so that crunched/v14 resources 439 # file. The order of these is important so that crunched/v14 resources
416 # override the normal ones. 440 # override the normal ones.
417 zip_resource_dirs = input_resource_dirs + [v14_dir] 441 zip_resource_dirs = input_resource_dirs + [v14_dir]
418 442
419 base_crunch_dir = os.path.join(temp_dir, 'crunch') 443 base_crunch_dir = os.path.join(temp_dir, 'crunch')
420 444
421 # Crunch image resources. This shrinks png files and is necessary for 445 # Crunch image resources. This shrinks png files and is necessary for
422 # 9-patch images to display correctly. 'aapt crunch' accepts only a single 446 # 9-patch images to display correctly. 'aapt crunch' accepts only a single
423 # directory at a time and deletes everything in the output directory. 447 # directory at a time and deletes everything in the output directory.
424 for idx, input_dir in enumerate(input_resource_dirs): 448 for idx, input_dir in enumerate(input_resource_dirs):
425 crunch_dir = os.path.join(base_crunch_dir, str(idx)) 449 crunch_dir = os.path.join(base_crunch_dir, str(idx))
426 build_utils.MakeDirectory(crunch_dir) 450 build_utils.MakeDirectory(crunch_dir)
427 zip_resource_dirs.append(crunch_dir) 451 zip_resource_dirs.append(crunch_dir)
428 CrunchDirectory(aapt, input_dir, crunch_dir) 452 CrunchDirectory(aapt, input_dir, crunch_dir)
429 453
430 ZipResources(zip_resource_dirs, options.resource_zip_out) 454 ZipResources(zip_resource_dirs, options.resource_zip_out)
431 455
432 if options.all_resources_zip_out: 456 if options.all_resources_zip_out:
433 CombineZips([options.resource_zip_out] + dep_zips, 457 CombineZips([options.resource_zip_out] + dep_zips,
434 options.all_resources_zip_out) 458 options.all_resources_zip_out)
435 459
436 if options.R_dir: 460 if options.R_dir:
437 build_utils.DeleteDirectory(options.R_dir) 461 build_utils.DeleteDirectory(options.R_dir)
438 shutil.copytree(gen_dir, options.R_dir) 462 shutil.copytree(srcjar_dir, options.R_dir)
439 else: 463 else:
440 build_utils.ZipDir(options.srcjar_out, gen_dir) 464 build_utils.ZipDir(options.srcjar_out, srcjar_dir)
441 465
442 if options.r_text_out: 466 if options.r_text_out:
443 r_text_path = os.path.join(gen_dir, 'R.txt') 467 shutil.copyfile(r_txt_path, options.r_text_out)
444 if os.path.exists(r_text_path):
445 shutil.copyfile(r_text_path, options.r_text_out)
446 else:
447 open(options.r_text_out, 'w').close()
448 468
449 469
450 def main(args): 470 def main(args):
451 args = build_utils.ExpandFileArgs(args) 471 args = build_utils.ExpandFileArgs(args)
452 options = _ParseArgs(args) 472 options = _ParseArgs(args)
453 473
454 possible_output_paths = [ 474 possible_output_paths = [
455 options.resource_zip_out, 475 options.resource_zip_out,
456 options.all_resources_zip_out, 476 options.all_resources_zip_out,
457 options.proguard_file, 477 options.proguard_file,
(...skipping 12 matching lines...) Expand all
470 options.shared_resources, 490 options.shared_resources,
471 options.v14_skip, 491 options.v14_skip,
472 ] 492 ]
473 493
474 input_paths = [ 494 input_paths = [
475 options.aapt_path, 495 options.aapt_path,
476 options.android_manifest, 496 options.android_manifest,
477 options.android_sdk_jar, 497 options.android_sdk_jar,
478 ] 498 ]
479 input_paths.extend(options.dependencies_res_zips) 499 input_paths.extend(options.dependencies_res_zips)
480 input_paths.extend(p for p in options.extra_r_text_files if os.path.exists(p)) 500 input_paths.extend(options.extra_r_text_files)
481 501
482 resource_names = [] 502 resource_names = []
483 for resource_dir in options.resource_dirs: 503 for resource_dir in options.resource_dirs:
484 for resource_file in build_utils.FindInDirectory(resource_dir, '*'): 504 for resource_file in build_utils.FindInDirectory(resource_dir, '*'):
485 input_paths.append(resource_file) 505 input_paths.append(resource_file)
486 resource_names.append(os.path.relpath(resource_file, resource_dir)) 506 resource_names.append(os.path.relpath(resource_file, resource_dir))
487 507
488 # Resource filenames matter to the output, so add them to strings as well. 508 # Resource filenames matter to the output, so add them to strings as well.
489 # This matters if a file is renamed but not changed (http://crbug.com/597126). 509 # This matters if a file is renamed but not changed (http://crbug.com/597126).
490 input_strings.extend(sorted(resource_names)) 510 input_strings.extend(sorted(resource_names))
491 511
492 build_utils.CallAndWriteDepfileIfStale( 512 build_utils.CallAndWriteDepfileIfStale(
493 lambda: _OnStaleMd5(options), 513 lambda: _OnStaleMd5(options),
494 options, 514 options,
495 input_paths=input_paths, 515 input_paths=input_paths,
496 input_strings=input_strings, 516 input_strings=input_strings,
497 output_paths=output_paths, 517 output_paths=output_paths,
498 # TODO(agrieve): Remove R_dir when it's no longer used (used only by GYP). 518 # TODO(agrieve): Remove R_dir when it's no longer used (used only by GYP).
499 force=options.R_dir) 519 force=options.R_dir)
500 520
501 521
502 if __name__ == '__main__': 522 if __name__ == '__main__':
503 main(sys.argv[1:]) 523 main(sys.argv[1:])
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698