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 seen_entries_by_package = collections.defaultdict(set) | |
newt (away)
2015/09/15 17:07:53
Are there legitimate cases where extra_packages co
newt (away)
2015/09/15 17:14:01
Ah, now I see. You're reusing org.chromium.chrome
| |
158 # Build the R.java files using each package's R.txt file, but replacing | |
159 # 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): | 160 for package, r_text_file in zip(extra_packages, extra_r_text_files): |
151 if os.path.exists(r_text_file): | 161 if not os.path.exists(r_text_file): |
152 package_r_java_dir = os.path.join(r_dir, *package.split('.')) | 162 continue |
153 build_utils.MakeDirectory(package_r_java_dir) | 163 resources_by_type = resources_by_package[package] |
154 package_r_java_path = os.path.join(package_r_java_dir, 'R.java') | 164 seen_entries = seen_entries_by_package[package] |
155 CreateExtraRJavaFile( | 165 # The sub-R.txt files have the wrong values at this point. Read them to |
156 package, package_r_java_path, r_text_file, all_resources, | 166 # figure out which entries belong to them, but use the values from the |
157 shared_resources) | 167 # main R.txt file. |
168 for entry in _ParseTextSymbolsFile(r_text_file): | |
169 entry_key = (entry.resource_type, entry.name) | |
170 if not entry_key in seen_entries: | |
171 seen_entries.add(entry_key) | |
172 entry = all_resources[entry_key] | |
173 resources_by_type[entry.resource_type].append(entry) | |
174 | |
175 for package, resources_by_type in resources_by_package.iteritems(): | |
176 package_r_java_dir = os.path.join(r_dir, *package.split('.')) | |
177 build_utils.MakeDirectory(package_r_java_dir) | |
178 package_r_java_path = os.path.join(package_r_java_dir, 'R.java') | |
179 java_file_contents = _CreateExtraRJavaFile( | |
180 package, resources_by_type, shared_resources) | |
181 with open(package_r_java_path, 'w') as f: | |
182 f.write(java_file_contents) | |
158 | 183 |
159 | 184 |
160 def CreateExtraRJavaFile( | 185 def _ParseTextSymbolsFile(path): |
161 package, r_java_path, r_text_file, all_resources, shared_resources): | 186 """Given an R.txt file, returns a list of TextSymbolsEntry.""" |
162 resources = {} | 187 ret = [] |
163 with open(r_text_file) as f: | 188 with open(path) as f: |
164 for line in f: | 189 for line in f: |
165 m = re.match(r'int(?:\[\])? (\w+) (\w+) ', line) | 190 m = re.match(r'(int(?:\[\])?) (\w+) (\w+) (.+)$', line) |
166 if not m: | 191 if not m: |
167 raise Exception('Unexpected line in R.txt: %s' % line) | 192 raise Exception('Unexpected line in R.txt: %s' % line) |
168 resource_type, name = m.groups() | 193 java_type, resource_type, name, value = m.groups() |
169 java_type, value = all_resources[(resource_type, name)] | 194 ret.append(TextSymbolsEntry(java_type, resource_type, name, value)) |
170 if resource_type not in resources: | 195 return ret |
171 resources[resource_type] = [] | |
172 resources[resource_type].append((name, java_type, value)) | |
173 | 196 |
197 | |
198 def _CreateExtraRJavaFile(package, resources_by_type, shared_resources): | |
199 """Generates the contents of a R.java file.""" | |
174 template = Template("""/* AUTO-GENERATED FILE. DO NOT MODIFY. */ | 200 template = Template("""/* AUTO-GENERATED FILE. DO NOT MODIFY. */ |
175 | 201 |
176 package {{ package }}; | 202 package {{ package }}; |
177 | 203 |
178 public final class R { | 204 public final class R { |
179 {% for resource_type in resources %} | 205 {% for resource_type in resources %} |
180 public static final class {{ resource_type }} { | 206 public static final class {{ resource_type }} { |
181 {% for name, java_type, value in resources[resource_type] %} | 207 {% for entry in resources[resource_type] %} |
182 {% if shared_resources %} | 208 {% if shared_resources %} |
183 public static {{ java_type }} {{ name }} = {{ value }}; | 209 public static {{ entry.java_type }} {{ entry.name }} = {{ entry.value }} ; |
184 {% else %} | 210 {% else %} |
185 public static final {{ java_type }} {{ name }} = {{ value }}; | 211 public static final {{ entry.java_type }} {{ entry.name }} = {{ entry.va lue }}; |
186 {% endif %} | 212 {% endif %} |
187 {% endfor %} | 213 {% endfor %} |
188 } | 214 } |
189 {% endfor %} | 215 {% endfor %} |
190 {% if shared_resources %} | 216 {% if shared_resources %} |
191 public static void onResourcesLoaded(int packageId) { | 217 public static void onResourcesLoaded(int packageId) { |
192 {% for resource_type in resources %} | 218 {% for resource_type in resources %} |
193 {% for name, java_type, value in resources[resource_type] %} | 219 {% for entry in resources[resource_type] %} |
194 {% if java_type == 'int[]' %} | 220 {% if entry.java_type == 'int[]' %} |
195 for(int i = 0; i < {{ resource_type }}.{{ name }}.length; ++i) { | 221 for(int i = 0; i < {{ entry.resource_type }}.{{ entry.name }}.length; ++ i) { |
196 {{ resource_type }}.{{ name }}[i] = | 222 {{ entry.resource_type }}.{{ entry.name }}[i] = |
197 ({{ resource_type }}.{{ name }}[i] & 0x00ffffff) | 223 ({{ entry.resource_type }}.{{ entry.name }}[i] & 0x00ffffff) |
198 | (packageId << 24); | 224 | (packageId << 24); |
199 } | 225 } |
200 {% else %} | 226 {% else %} |
201 {{ resource_type }}.{{ name }} = | 227 {{ entry.resource_type }}.{{ entry.name }} = |
202 ({{ resource_type }}.{{ name }} & 0x00ffffff) | 228 ({{ entry.resource_type }}.{{ entry.name }} & 0x00ffffff) |
203 | (packageId << 24); | 229 | (packageId << 24); |
204 {% endif %} | 230 {% endif %} |
205 {% endfor %} | 231 {% endfor %} |
206 {% endfor %} | 232 {% endfor %} |
207 } | 233 } |
208 {% endif %} | 234 {% endif %} |
209 } | 235 } |
210 """, trim_blocks=True, lstrip_blocks=True) | 236 """, trim_blocks=True, lstrip_blocks=True) |
211 | 237 |
212 output = template.render(package=package, resources=resources, | 238 return template.render(package=package, resources=resources_by_type, |
213 shared_resources=shared_resources) | 239 shared_resources=shared_resources) |
214 with open(r_java_path, 'w') as f: | |
215 f.write(output) | |
216 | 240 |
217 | 241 |
218 def CrunchDirectory(aapt, input_dir, output_dir): | 242 def CrunchDirectory(aapt, input_dir, output_dir): |
219 """Crunches the images in input_dir and its subdirectories into output_dir. | 243 """Crunches the images in input_dir and its subdirectories into output_dir. |
220 | 244 |
221 If an image is already optimized, crunching often increases image size. In | 245 If an image is already optimized, crunching often increases image size. In |
222 this case, the crunched image is overwritten with the original image. | 246 this case, the crunched image is overwritten with the original image. |
223 """ | 247 """ |
224 aapt_cmd = [aapt, | 248 aapt_cmd = [aapt, |
225 'crunch', | 249 'crunch', |
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
409 if options.depfile: | 433 if options.depfile: |
410 input_files += build_utils.GetPythonDependencies() | 434 input_files += build_utils.GetPythonDependencies() |
411 build_utils.WriteDepfile(options.depfile, input_files) | 435 build_utils.WriteDepfile(options.depfile, input_files) |
412 | 436 |
413 if options.stamp: | 437 if options.stamp: |
414 build_utils.Touch(options.stamp) | 438 build_utils.Touch(options.stamp) |
415 | 439 |
416 | 440 |
417 if __name__ == '__main__': | 441 if __name__ == '__main__': |
418 main() | 442 main() |
OLD | NEW |