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 |