| OLD | NEW |
| 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 import xml.etree.ElementTree |
| 21 | 21 |
| 22 import generate_v14_compatible_resources | 22 import generate_v14_compatible_resources |
| 23 | 23 |
| 24 from util import build_utils | 24 from util import build_utils |
| 25 | 25 |
| 26 _ROOT_R_JAVA_PACKAGE_PREFIX = 'gen' |
| 27 |
| 28 |
| 26 # Import jinja2 from third_party/jinja2 | 29 # Import jinja2 from third_party/jinja2 |
| 27 sys.path.insert(1, | 30 sys.path.insert(1, |
| 28 os.path.join(os.path.dirname(__file__), '../../../third_party')) | 31 os.path.join(os.path.dirname(__file__), '../../../third_party')) |
| 29 from jinja2 import Template # pylint: disable=F0401 | 32 from jinja2 import Template # pylint: disable=F0401 |
| 30 | 33 |
| 31 | 34 |
| 32 # Represents a line from a R.txt file. | 35 # Represents a line from a R.txt file. |
| 33 TextSymbolsEntry = collections.namedtuple('RTextEntry', | 36 TextSymbolsEntry = collections.namedtuple('RTextEntry', |
| 34 ('java_type', 'resource_type', 'name', 'value')) | 37 ('java_type', 'resource_type', 'name', 'value')) |
| 35 | 38 |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 142 if options.extra_r_text_files: | 145 if options.extra_r_text_files: |
| 143 options.extra_r_text_files = ( | 146 options.extra_r_text_files = ( |
| 144 build_utils.ParseGnList(options.extra_r_text_files)) | 147 build_utils.ParseGnList(options.extra_r_text_files)) |
| 145 else: | 148 else: |
| 146 options.extra_r_text_files = [] | 149 options.extra_r_text_files = [] |
| 147 | 150 |
| 148 return options | 151 return options |
| 149 | 152 |
| 150 | 153 |
| 151 def CreateRJavaFiles(srcjar_dir, main_r_txt_file, packages, r_txt_files, | 154 def CreateRJavaFiles(srcjar_dir, main_r_txt_file, packages, r_txt_files, |
| 152 shared_resources): | 155 shared_resources, srcjar_name): |
| 153 assert len(packages) == len(r_txt_files), 'Need one R.txt file per package' | 156 assert len(packages) == len(r_txt_files), 'Need one R.txt file per package' |
| 154 | 157 |
| 155 # Map of (resource_type, name) -> Entry. | 158 # Map of (resource_type, name) -> Entry. |
| 156 # Contains the correct values for resources. | 159 # Contains the correct values for resources. |
| 157 all_resources = {} | 160 all_resources = {} |
| 161 all_resources_by_type = collections.defaultdict(list) |
| 158 for entry in _ParseTextSymbolsFile(main_r_txt_file): | 162 for entry in _ParseTextSymbolsFile(main_r_txt_file): |
| 159 all_resources[(entry.resource_type, entry.name)] = entry | 163 all_resources[(entry.resource_type, entry.name)] = entry |
| 164 all_resources_by_type[entry.resource_type].append(entry) |
| 165 |
| 166 # Write Root R.java. |
| 167 root_r_java_package = _ROOT_R_JAVA_PACKAGE_PREFIX + '.' + srcjar_name |
| 168 root_r_java_dir = os.path.join(srcjar_dir, *root_r_java_package.split('.')) |
| 169 build_utils.MakeDirectory(root_r_java_dir) |
| 170 root_r_java_path = os.path.join(root_r_java_dir, 'R.java') |
| 171 root_java_file_contents = _CreateRootRJavaFile( |
| 172 root_r_java_package, all_resources_by_type, shared_resources) |
| 173 with open(root_r_java_path, 'w') as f: |
| 174 f.write(root_java_file_contents) |
| 160 | 175 |
| 161 # Map of package_name->resource_type->entry | 176 # Map of package_name->resource_type->entry |
| 162 resources_by_package = ( | 177 resources_by_package = ( |
| 163 collections.defaultdict(lambda: collections.defaultdict(list))) | 178 collections.defaultdict(lambda: collections.defaultdict(list))) |
| 164 # Build the R.java files using each package's R.txt file, but replacing | 179 # Build the R.java files using each package's R.txt file, but replacing |
| 165 # each entry's placeholder value with correct values from all_resources. | 180 # each entry's placeholder value with correct values from all_resources. |
| 166 for package, r_txt_file in zip(packages, r_txt_files): | 181 for package, r_txt_file in zip(packages, r_txt_files): |
| 167 if package in resources_by_package: | 182 if package in resources_by_package: |
| 168 raise Exception(('Package name "%s" appeared twice. All ' | 183 raise Exception(('Package name "%s" appeared twice. All ' |
| 169 'android_resources() targets must use unique package ' | 184 'android_resources() targets must use unique package ' |
| (...skipping 20 matching lines...) Expand all Loading... |
| 190 # correctly by running Proguard on the APK: it will report missing | 205 # correctly by running Proguard on the APK: it will report missing |
| 191 # fields. | 206 # fields. |
| 192 if entry: | 207 if entry: |
| 193 resources_by_type[entry.resource_type].append(entry) | 208 resources_by_type[entry.resource_type].append(entry) |
| 194 | 209 |
| 195 for package, resources_by_type in resources_by_package.iteritems(): | 210 for package, resources_by_type in resources_by_package.iteritems(): |
| 196 package_r_java_dir = os.path.join(srcjar_dir, *package.split('.')) | 211 package_r_java_dir = os.path.join(srcjar_dir, *package.split('.')) |
| 197 build_utils.MakeDirectory(package_r_java_dir) | 212 build_utils.MakeDirectory(package_r_java_dir) |
| 198 package_r_java_path = os.path.join(package_r_java_dir, 'R.java') | 213 package_r_java_path = os.path.join(package_r_java_dir, 'R.java') |
| 199 java_file_contents = _CreateRJavaFile( | 214 java_file_contents = _CreateRJavaFile( |
| 200 package, resources_by_type, shared_resources) | 215 package, root_r_java_package, resources_by_type, shared_resources) |
| 201 with open(package_r_java_path, 'w') as f: | 216 with open(package_r_java_path, 'w') as f: |
| 202 f.write(java_file_contents) | 217 f.write(java_file_contents) |
| 203 | 218 |
| 204 | 219 |
| 205 def _ParseTextSymbolsFile(path): | 220 def _ParseTextSymbolsFile(path): |
| 206 """Given an R.txt file, returns a list of TextSymbolsEntry.""" | 221 """Given an R.txt file, returns a list of TextSymbolsEntry.""" |
| 207 ret = [] | 222 ret = [] |
| 208 with open(path) as f: | 223 with open(path) as f: |
| 209 for line in f: | 224 for line in f: |
| 210 m = re.match(r'(int(?:\[\])?) (\w+) (\w+) (.+)$', line) | 225 m = re.match(r'(int(?:\[\])?) (\w+) (\w+) (.+)$', line) |
| 211 if not m: | 226 if not m: |
| 212 raise Exception('Unexpected line in R.txt: %s' % line) | 227 raise Exception('Unexpected line in R.txt: %s' % line) |
| 213 java_type, resource_type, name, value = m.groups() | 228 java_type, resource_type, name, value = m.groups() |
| 214 ret.append(TextSymbolsEntry(java_type, resource_type, name, value)) | 229 ret.append(TextSymbolsEntry(java_type, resource_type, name, value)) |
| 215 return ret | 230 return ret |
| 216 | 231 |
| 217 | 232 |
| 218 def _CreateRJavaFile(package, resources_by_type, shared_resources): | 233 def _CreateRJavaFile( |
| 234 package, root_r_java_package, resources_by_type, shared_resources): |
| 235 # TODO fix comments |
| 236 """Generates the contents of a R.java file.""" |
| 237 template = Template("""/* AUTO-GENERATED FILE. DO NOT MODIFY. */ |
| 238 |
| 239 package {{ package }}; |
| 240 |
| 241 public final class R { |
| 242 {% for resource_type in resource_types %} |
| 243 public static final class {{ resource_type }} extends |
| 244 {{ root_package }}.R.{{ resource_type }} {} |
| 245 {% endfor %} |
| 246 {% if shared_resources %} |
| 247 public static void onResourcesLoaded(int packageId) { |
| 248 {{ root_package }}.R.onResourcesLoaded(packageId); |
| 249 } |
| 250 {% endif %} |
| 251 } |
| 252 """, trim_blocks=True, lstrip_blocks=True) |
| 253 |
| 254 return template.render(package=package, |
| 255 resources=resources_by_type, |
| 256 resource_types=sorted(resources_by_type), |
| 257 root_package=root_r_java_package, |
| 258 shared_resources=shared_resources) |
| 259 |
| 260 |
| 261 def _CreateRootRJavaFile(package, all_resources_by_type, shared_resources): |
| 262 # TODO fix comments |
| 219 """Generates the contents of a R.java file.""" | 263 """Generates the contents of a R.java file.""" |
| 220 # Keep these assignments all on one line to make diffing against regular | 264 # Keep these assignments all on one line to make diffing against regular |
| 221 # aapt-generated files easier. | 265 # aapt-generated files easier. |
| 222 create_id = ('{{ e.resource_type }}.{{ e.name }} ^= packageIdTransform;') | 266 create_id = ('{{ e.resource_type }}.{{ e.name }} ^= packageIdTransform;') |
| 223 create_id_arr = ('{{ e.resource_type }}.{{ e.name }}[i] ^=' | 267 create_id_arr = ('{{ e.resource_type }}.{{ e.name }}[i] ^=' |
| 224 ' packageIdTransform;') | 268 ' packageIdTransform;') |
| 225 # Here we diverge from what aapt does. Because we have so many | 269 # Here we diverge from what aapt does. Because we have so many |
| 226 # resources, the onResourcesLoaded method was exceeding the 64KB limit that | 270 # resources, the onResourcesLoaded method was exceeding the 64KB limit that |
| 227 # Java imposes. For this reason we split onResourcesLoaded into different | 271 # Java imposes. For this reason we split onResourcesLoaded into different |
| 228 # methods for each resource type. | 272 # methods for each resource type. |
| 229 template = Template("""/* AUTO-GENERATED FILE. DO NOT MODIFY. */ | 273 template = Template("""/* AUTO-GENERATED FILE. DO NOT MODIFY. */ |
| 230 | 274 |
| 231 package {{ package }}; | 275 package {{ package }}; |
| 232 | 276 |
| 233 public final class R { | 277 public final class R { |
| 234 private static boolean sResourcesDidLoad; | 278 private static boolean sResourcesDidLoad; |
| 235 {% for resource_type in resource_types %} | 279 {% for resource_type in resource_types %} |
| 236 public static final class {{ resource_type }} { | 280 public static class {{ resource_type }} { |
| 237 {% for e in resources[resource_type] %} | 281 {% for e in resources[resource_type] %} |
| 238 {% if shared_resources %} | 282 {% if shared_resources %} |
| 239 public static {{ e.java_type }} {{ e.name }} = {{ e.value }}; | 283 public static {{ e.java_type }} {{ e.name }} = {{ e.value }}; |
| 240 {% else %} | 284 {% else %} |
| 241 public static final {{ e.java_type }} {{ e.name }} = {{ e.value }}; | 285 public static final {{ e.java_type }} {{ e.name }} = {{ e.value }}; |
| 242 {% endif %} | 286 {% endif %} |
| 243 {% endfor %} | 287 {% endfor %} |
| 244 } | 288 } |
| 245 {% endfor %} | 289 {% endfor %} |
| 246 {% if shared_resources %} | 290 {% if shared_resources %} |
| 247 public static void onResourcesLoaded(int packageId) { | 291 public static void onResourcesLoaded(int packageId) { |
| 248 assert !sResourcesDidLoad; | 292 if (sResourcesDidLoad) { |
| 293 return; |
| 294 } |
| 249 sResourcesDidLoad = true; | 295 sResourcesDidLoad = true; |
| 250 int packageIdTransform = (packageId ^ 0x7f) << 24; | 296 int packageIdTransform = (packageId ^ 0x7f) << 24; |
| 251 {% for resource_type in resource_types %} | 297 {% for resource_type in resource_types %} |
| 252 onResourcesLoaded{{ resource_type|title }}(packageIdTransform); | 298 onResourcesLoaded{{ resource_type|title }}(packageIdTransform); |
| 253 {% for e in resources[resource_type] %} | 299 {% for e in resources[resource_type] %} |
| 254 {% if e.java_type == 'int[]' %} | 300 {% if e.java_type == 'int[]' %} |
| 255 for(int i = 0; i < {{ e.resource_type }}.{{ e.name }}.length; ++i) { | 301 for(int i = 0; i < {{ e.resource_type }}.{{ e.name }}.length; ++i) { |
| 256 """ + create_id_arr + """ | 302 """ + create_id_arr + """ |
| 257 } | 303 } |
| 258 {% endif %} | 304 {% endif %} |
| 259 {% endfor %} | 305 {% endfor %} |
| 260 {% endfor %} | 306 {% endfor %} |
| 261 } | 307 } |
| 262 {% for res_type in resource_types %} | 308 {% for res_type in resource_types %} |
| 263 private static void onResourcesLoaded{{ res_type|title }} ( | 309 private static void onResourcesLoaded{{ res_type|title }} ( |
| 264 int packageIdTransform) { | 310 int packageIdTransform) { |
| 265 {% for e in resources[res_type] %} | 311 {% for e in resources[res_type] %} |
| 266 {% if res_type != 'styleable' and e.java_type != 'int[]' %} | 312 {% if res_type != 'styleable' and e.java_type != 'int[]' %} |
| 267 """ + create_id + """ | 313 """ + create_id + """ |
| 268 {% endif %} | 314 {% endif %} |
| 269 {% endfor %} | 315 {% endfor %} |
| 270 } | 316 } |
| 271 {% endfor %} | 317 {% endfor %} |
| 272 {% endif %} | 318 {% endif %} |
| 273 } | 319 } |
| 274 """, trim_blocks=True, lstrip_blocks=True) | 320 """, trim_blocks=True, lstrip_blocks=True) |
| 275 | 321 |
| 276 return template.render(package=package, | 322 return template.render(package=package, |
| 277 resources=resources_by_type, | 323 resources=all_resources_by_type, |
| 278 resource_types=sorted(resources_by_type), | 324 resource_types=sorted(all_resources_by_type), |
| 279 shared_resources=shared_resources) | 325 shared_resources=shared_resources) |
| 280 | 326 |
| 281 | 327 |
| 282 def CrunchDirectory(aapt, input_dir, output_dir): | 328 def CrunchDirectory(aapt, input_dir, output_dir): |
| 283 """Crunches the images in input_dir and its subdirectories into output_dir. | 329 """Crunches the images in input_dir and its subdirectories into output_dir. |
| 284 | 330 |
| 285 If an image is already optimized, crunching often increases image size. In | 331 If an image is already optimized, crunching often increases image size. In |
| 286 this case, the crunched image is overwritten with the original image. | 332 this case, the crunched image is overwritten with the original image. |
| 287 """ | 333 """ |
| 288 aapt_cmd = [aapt, | 334 aapt_cmd = [aapt, |
| (...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 472 # - there was already a dependent android_resources() with the same | 518 # - there was already a dependent android_resources() with the same |
| 473 # package (occurs mostly when an apk target and resources target share | 519 # package (occurs mostly when an apk target and resources target share |
| 474 # an AndroidManifest.xml) | 520 # an AndroidManifest.xml) |
| 475 if cur_package != 'dummy.package' and cur_package not in packages: | 521 if cur_package != 'dummy.package' and cur_package not in packages: |
| 476 packages.append(cur_package) | 522 packages.append(cur_package) |
| 477 r_txt_files.append(r_txt_path) | 523 r_txt_files.append(r_txt_path) |
| 478 | 524 |
| 479 if packages: | 525 if packages: |
| 480 shared_resources = options.shared_resources or options.app_as_shared_lib | 526 shared_resources = options.shared_resources or options.app_as_shared_lib |
| 481 CreateRJavaFiles(srcjar_dir, r_txt_path, packages, r_txt_files, | 527 CreateRJavaFiles(srcjar_dir, r_txt_path, packages, r_txt_files, |
| 482 shared_resources) | 528 shared_resources, os.path.basename(options.srcjar_out)) |
| 483 | 529 |
| 484 # This is the list of directories with resources to put in the final .zip | 530 # This is the list of directories with resources to put in the final .zip |
| 485 # file. The order of these is important so that crunched/v14 resources | 531 # file. The order of these is important so that crunched/v14 resources |
| 486 # override the normal ones. | 532 # override the normal ones. |
| 487 zip_resource_dirs = input_resource_dirs + [v14_dir] | 533 zip_resource_dirs = input_resource_dirs + [v14_dir] |
| 488 | 534 |
| 489 base_crunch_dir = os.path.join(temp_dir, 'crunch') | 535 base_crunch_dir = os.path.join(temp_dir, 'crunch') |
| 490 | 536 |
| 491 # Crunch image resources. This shrinks png files and is necessary for | 537 # Crunch image resources. This shrinks png files and is necessary for |
| 492 # 9-patch images to display correctly. 'aapt crunch' accepts only a single | 538 # 9-patch images to display correctly. 'aapt crunch' accepts only a single |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 560 options, | 606 options, |
| 561 input_paths=input_paths, | 607 input_paths=input_paths, |
| 562 input_strings=input_strings, | 608 input_strings=input_strings, |
| 563 output_paths=output_paths, | 609 output_paths=output_paths, |
| 564 # TODO(agrieve): Remove R_dir when it's no longer used (used only by GYP). | 610 # TODO(agrieve): Remove R_dir when it's no longer used (used only by GYP). |
| 565 force=options.R_dir) | 611 force=options.R_dir) |
| 566 | 612 |
| 567 | 613 |
| 568 if __name__ == '__main__': | 614 if __name__ == '__main__': |
| 569 main(sys.argv[1:]) | 615 main(sys.argv[1:]) |
| OLD | NEW |