| 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 optparse | 15 import optparse |
| 15 import os | 16 import os |
| 16 import re | 17 import re |
| 17 import shutil | 18 import shutil |
| 18 import sys | 19 import sys |
| 19 | 20 |
| 20 import generate_v14_compatible_resources | 21 import generate_v14_compatible_resources |
| 21 | 22 |
| 22 from util import build_utils | 23 from util import build_utils |
| 23 | 24 |
| 24 # Import jinja2 from third_party/jinja2 | 25 # Import jinja2 from third_party/jinja2 |
| 25 sys.path.insert(1, | 26 sys.path.insert(1, |
| 26 os.path.join(os.path.dirname(__file__), '../../../third_party')) | 27 os.path.join(os.path.dirname(__file__), '../../../third_party')) |
| 27 from jinja2 import Template # pylint: disable=F0401 | 28 from jinja2 import Template # pylint: disable=F0401 |
| 28 | 29 |
| 29 | 30 |
| 31 # Represents a line from a R.txt file. |
| 32 TextSymbolsEntry = collections.namedtuple('RTextEntry', |
| 33 ('java_type', 'resource_type', 'name', 'value')) |
| 34 |
| 35 |
| 30 def ParseArgs(args): | 36 def ParseArgs(args): |
| 31 """Parses command line options. | 37 """Parses command line options. |
| 32 | 38 |
| 33 Returns: | 39 Returns: |
| 34 An options object as from optparse.OptionsParser.parse_args() | 40 An options object as from optparse.OptionsParser.parse_args() |
| 35 """ | 41 """ |
| 36 parser = optparse.OptionParser() | 42 parser = optparse.OptionParser() |
| 37 build_utils.AddDepfileOption(parser) | 43 build_utils.AddDepfileOption(parser) |
| 38 | 44 |
| 39 parser.add_option('--android-sdk', help='path to the Android SDK folder') | 45 parser.add_option('--android-sdk', help='path to the Android SDK folder') |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 128 package_r_java_dir = os.path.join(r_dir, *package.split('.')) | 134 package_r_java_dir = os.path.join(r_dir, *package.split('.')) |
| 129 build_utils.MakeDirectory(package_r_java_dir) | 135 build_utils.MakeDirectory(package_r_java_dir) |
| 130 package_r_java_path = os.path.join(package_r_java_dir, 'R.java') | 136 package_r_java_path = os.path.join(package_r_java_dir, 'R.java') |
| 131 new_r_java = re.sub(r'package [.\w]*;', u'package %s;' % package, | 137 new_r_java = re.sub(r'package [.\w]*;', u'package %s;' % package, |
| 132 r_java_contents) | 138 r_java_contents) |
| 133 codecs.open(package_r_java_path, 'w', encoding='utf-8').write(new_r_java) | 139 codecs.open(package_r_java_path, 'w', encoding='utf-8').write(new_r_java) |
| 134 else: | 140 else: |
| 135 if len(extra_packages) != len(extra_r_text_files): | 141 if len(extra_packages) != len(extra_r_text_files): |
| 136 raise Exception('Need one R.txt file per extra package') | 142 raise Exception('Need one R.txt file per extra package') |
| 137 | 143 |
| 138 all_resources = {} | |
| 139 r_txt_file = os.path.join(r_dir, 'R.txt') | 144 r_txt_file = os.path.join(r_dir, 'R.txt') |
| 140 if not os.path.exists(r_txt_file): | 145 if not os.path.exists(r_txt_file): |
| 141 return | 146 return |
| 142 with open(r_txt_file) as f: | |
| 143 for line in f: | |
| 144 m = re.match(r'(int(?:\[\])?) (\w+) (\w+) (.+)$', line) | |
| 145 if not m: | |
| 146 raise Exception('Unexpected line in R.txt: %s' % line) | |
| 147 java_type, resource_type, name, value = m.groups() | |
| 148 all_resources[(resource_type, name)] = (java_type, value) | |
| 149 | 147 |
| 148 # Map of (resource_type, name) -> Entry. |
| 149 # Contains the correct values for resources. |
| 150 all_resources = {} |
| 151 for entry in _ParseTextSymbolsFile(r_txt_file): |
| 152 all_resources[(entry.resource_type, entry.name)] = entry |
| 153 |
| 154 # Map of package_name->resource_type->entry |
| 155 resources_by_package = ( |
| 156 collections.defaultdict(lambda: collections.defaultdict(list))) |
| 157 # Build the R.java files using each package's R.txt file, but replacing |
| 158 # each entry's placeholder value with correct values from all_resources. |
| 150 for package, r_text_file in zip(extra_packages, extra_r_text_files): | 159 for package, r_text_file in zip(extra_packages, extra_r_text_files): |
| 151 if os.path.exists(r_text_file): | 160 if not os.path.exists(r_text_file): |
| 152 package_r_java_dir = os.path.join(r_dir, *package.split('.')) | 161 continue |
| 153 build_utils.MakeDirectory(package_r_java_dir) | 162 if package in resources_by_package: |
| 154 package_r_java_path = os.path.join(package_r_java_dir, 'R.java') | 163 raise Exception(('Package name "%s" appeared twice. All ' |
| 155 CreateExtraRJavaFile( | 164 'android_resources() targets must use unique package ' |
| 156 package, package_r_java_path, r_text_file, all_resources, | 165 'names, or no package name at all.') % package) |
| 157 shared_resources) | 166 resources_by_type = resources_by_package[package] |
| 167 # The sub-R.txt files have the wrong values at this point. Read them to |
| 168 # figure out which entries belong to them, but use the values from the |
| 169 # main R.txt file. |
| 170 for entry in _ParseTextSymbolsFile(r_text_file): |
| 171 entry = all_resources[(entry.resource_type, entry.name)] |
| 172 resources_by_type[entry.resource_type].append(entry) |
| 173 |
| 174 for package, resources_by_type in resources_by_package.iteritems(): |
| 175 package_r_java_dir = os.path.join(r_dir, *package.split('.')) |
| 176 build_utils.MakeDirectory(package_r_java_dir) |
| 177 package_r_java_path = os.path.join(package_r_java_dir, 'R.java') |
| 178 java_file_contents = _CreateExtraRJavaFile( |
| 179 package, resources_by_type, shared_resources) |
| 180 with open(package_r_java_path, 'w') as f: |
| 181 f.write(java_file_contents) |
| 158 | 182 |
| 159 | 183 |
| 160 def CreateExtraRJavaFile( | 184 def _ParseTextSymbolsFile(path): |
| 161 package, r_java_path, r_text_file, all_resources, shared_resources): | 185 """Given an R.txt file, returns a list of TextSymbolsEntry.""" |
| 162 resources = {} | 186 ret = [] |
| 163 with open(r_text_file) as f: | 187 with open(path) as f: |
| 164 for line in f: | 188 for line in f: |
| 165 m = re.match(r'int(?:\[\])? (\w+) (\w+) ', line) | 189 m = re.match(r'(int(?:\[\])?) (\w+) (\w+) (.+)$', line) |
| 166 if not m: | 190 if not m: |
| 167 raise Exception('Unexpected line in R.txt: %s' % line) | 191 raise Exception('Unexpected line in R.txt: %s' % line) |
| 168 resource_type, name = m.groups() | 192 java_type, resource_type, name, value = m.groups() |
| 169 java_type, value = all_resources[(resource_type, name)] | 193 ret.append(TextSymbolsEntry(java_type, resource_type, name, value)) |
| 170 if resource_type not in resources: | 194 return ret |
| 171 resources[resource_type] = [] | |
| 172 resources[resource_type].append((name, java_type, value)) | |
| 173 | 195 |
| 196 |
| 197 def _CreateExtraRJavaFile(package, resources_by_type, shared_resources): |
| 198 """Generates the contents of a R.java file.""" |
| 174 template = Template("""/* AUTO-GENERATED FILE. DO NOT MODIFY. */ | 199 template = Template("""/* AUTO-GENERATED FILE. DO NOT MODIFY. */ |
| 175 | 200 |
| 176 package {{ package }}; | 201 package {{ package }}; |
| 177 | 202 |
| 178 public final class R { | 203 public final class R { |
| 179 {% for resource_type in resources %} | 204 {% for resource_type in resources %} |
| 180 public static final class {{ resource_type }} { | 205 public static final class {{ resource_type }} { |
| 181 {% for name, java_type, value in resources[resource_type] %} | 206 {% for e in resources[resource_type] %} |
| 182 {% if shared_resources %} | 207 {% if shared_resources %} |
| 183 public static {{ java_type }} {{ name }} = {{ value }}; | 208 public static {{ e.java_type }} {{ e.name }} = {{ e.value }}; |
| 184 {% else %} | 209 {% else %} |
| 185 public static final {{ java_type }} {{ name }} = {{ value }}; | 210 public static final {{ e.java_type }} {{ e.name }} = {{ e.value }}; |
| 186 {% endif %} | 211 {% endif %} |
| 187 {% endfor %} | 212 {% endfor %} |
| 188 } | 213 } |
| 189 {% endfor %} | 214 {% endfor %} |
| 190 {% if shared_resources %} | 215 {% if shared_resources %} |
| 191 public static void onResourcesLoaded(int packageId) { | 216 public static void onResourcesLoaded(int packageId) { |
| 192 {% for resource_type in resources %} | 217 {% for resource_type in resources %} |
| 193 {% for name, java_type, value in resources[resource_type] %} | 218 {% for e in resources[resource_type] %} |
| 194 {% if java_type == 'int[]' %} | 219 {% if e.java_type == 'int[]' %} |
| 195 for(int i = 0; i < {{ resource_type }}.{{ name }}.length; ++i) { | 220 for(int i = 0; i < {{ e.resource_type }}.{{ e.name }}.length; ++i) { |
| 196 {{ resource_type }}.{{ name }}[i] = | 221 {{ e.resource_type }}.{{ e.name }}[i] = |
| 197 ({{ resource_type }}.{{ name }}[i] & 0x00ffffff) | 222 ({{ e.resource_type }}.{{ e.name }}[i] & 0x00ffffff) |
| 198 | (packageId << 24); | 223 | (packageId << 24); |
| 199 } | 224 } |
| 200 {% else %} | 225 {% else %} |
| 201 {{ resource_type }}.{{ name }} = | 226 {{ e.resource_type }}.{{ e.name }} = |
| 202 ({{ resource_type }}.{{ name }} & 0x00ffffff) | 227 ({{ e.resource_type }}.{{ e.name }} & 0x00ffffff) |
| 203 | (packageId << 24); | 228 | (packageId << 24); |
| 204 {% endif %} | 229 {% endif %} |
| 205 {% endfor %} | 230 {% endfor %} |
| 206 {% endfor %} | 231 {% endfor %} |
| 207 } | 232 } |
| 208 {% endif %} | 233 {% endif %} |
| 209 } | 234 } |
| 210 """, trim_blocks=True, lstrip_blocks=True) | 235 """, trim_blocks=True, lstrip_blocks=True) |
| 211 | 236 |
| 212 output = template.render(package=package, resources=resources, | 237 return template.render(package=package, resources=resources_by_type, |
| 213 shared_resources=shared_resources) | 238 shared_resources=shared_resources) |
| 214 with open(r_java_path, 'w') as f: | |
| 215 f.write(output) | |
| 216 | 239 |
| 217 | 240 |
| 218 def CrunchDirectory(aapt, input_dir, output_dir): | 241 def CrunchDirectory(aapt, input_dir, output_dir): |
| 219 """Crunches the images in input_dir and its subdirectories into output_dir. | 242 """Crunches the images in input_dir and its subdirectories into output_dir. |
| 220 | 243 |
| 221 If an image is already optimized, crunching often increases image size. In | 244 If an image is already optimized, crunching often increases image size. In |
| 222 this case, the crunched image is overwritten with the original image. | 245 this case, the crunched image is overwritten with the original image. |
| 223 """ | 246 """ |
| 224 aapt_cmd = [aapt, | 247 aapt_cmd = [aapt, |
| 225 'crunch', | 248 'crunch', |
| (...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 409 if options.depfile: | 432 if options.depfile: |
| 410 input_files += build_utils.GetPythonDependencies() | 433 input_files += build_utils.GetPythonDependencies() |
| 411 build_utils.WriteDepfile(options.depfile, input_files) | 434 build_utils.WriteDepfile(options.depfile, input_files) |
| 412 | 435 |
| 413 if options.stamp: | 436 if options.stamp: |
| 414 build_utils.Touch(options.stamp) | 437 build_utils.Touch(options.stamp) |
| 415 | 438 |
| 416 | 439 |
| 417 if __name__ == '__main__': | 440 if __name__ == '__main__': |
| 418 main() | 441 main() |
| OLD | NEW |