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

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

Issue 2392643003: Removes files from //build that we don't need (Closed)
Patch Set: Created 4 years, 2 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 | « build/android/gyp/package_resources.py ('k') | build/android/gyp/proguard.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 #
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
5 # found in the LICENSE file.
6
7 """Process Android resources to generate R.java, and prepare for packaging.
8
9 This will crunch images and generate v14 compatible resources
10 (see generate_v14_compatible_resources.py).
11 """
12
13 import codecs
14 import optparse
15 import os
16 import re
17 import shutil
18 import sys
19 import zipfile
20
21 import generate_v14_compatible_resources
22
23 from util import build_utils
24
25 # Import jinja2 from third_party/jinja2
26 sys.path.insert(1,
27 os.path.join(os.path.dirname(__file__), '../../../third_party'))
28 from jinja2 import Template # pylint: disable=F0401
29
30
31 def ParseArgs(args):
32 """Parses command line options.
33
34 Returns:
35 An options object as from optparse.OptionsParser.parse_args()
36 """
37 parser = optparse.OptionParser()
38 build_utils.AddDepfileOption(parser)
39
40 parser.add_option('--android-sdk', help='path to the Android SDK folder')
41 parser.add_option('--aapt-path',
42 help='path to the Android aapt tool')
43 parser.add_option('--non-constant-id', action='store_true')
44
45 parser.add_option('--android-manifest', help='AndroidManifest.xml path')
46 parser.add_option('--custom-package', help='Java package for R.java')
47 parser.add_option(
48 '--shared-resources',
49 action='store_true',
50 help='Make a resource package that can be loaded by a different'
51 'application at runtime to access the package\'s resources.')
52
53 parser.add_option('--resource-dirs',
54 help='Directories containing resources of this target.')
55 parser.add_option('--dependencies-res-zips',
56 help='Resources from dependents.')
57
58 parser.add_option('--resource-zip-out',
59 help='Path for output zipped resources.')
60
61 parser.add_option('--R-dir',
62 help='directory to hold generated R.java.')
63 parser.add_option('--srcjar-out',
64 help='Path to srcjar to contain generated R.java.')
65 parser.add_option('--r-text-out',
66 help='Path to store the R.txt file generated by appt.')
67
68 parser.add_option('--proguard-file',
69 help='Path to proguard.txt generated file')
70
71 parser.add_option(
72 '--v14-skip',
73 action="store_true",
74 help='Do not generate nor verify v14 resources')
75
76 parser.add_option(
77 '--extra-res-packages',
78 help='Additional package names to generate R.java files for')
79 parser.add_option(
80 '--extra-r-text-files',
81 help='For each additional package, the R.txt file should contain a '
82 'list of resources to be included in the R.java file in the format '
83 'generated by aapt')
84 parser.add_option(
85 '--include-all-resources',
86 action='store_true',
87 help='Include every resource ID in every generated R.java file '
88 '(ignoring R.txt).')
89
90 parser.add_option(
91 '--all-resources-zip-out',
92 help='Path for output of all resources. This includes resources in '
93 'dependencies.')
94
95 parser.add_option('--stamp', help='File to touch on success')
96
97 (options, args) = parser.parse_args(args)
98
99 if args:
100 parser.error('No positional arguments should be given.')
101
102 # Check that required options have been provided.
103 required_options = (
104 'android_sdk',
105 'aapt_path',
106 'android_manifest',
107 'dependencies_res_zips',
108 'resource_dirs',
109 'resource_zip_out',
110 )
111 build_utils.CheckOptions(options, parser, required=required_options)
112
113 if (options.R_dir is None) == (options.srcjar_out is None):
114 raise Exception('Exactly one of --R-dir or --srcjar-out must be specified.')
115
116 return options
117
118
119 def CreateExtraRJavaFiles(
120 r_dir, extra_packages, extra_r_text_files, shared_resources, include_all):
121 if include_all:
122 java_files = build_utils.FindInDirectory(r_dir, "R.java")
123 if len(java_files) != 1:
124 return
125 r_java_file = java_files[0]
126 r_java_contents = codecs.open(r_java_file, encoding='utf-8').read()
127
128 for package in extra_packages:
129 package_r_java_dir = os.path.join(r_dir, *package.split('.'))
130 build_utils.MakeDirectory(package_r_java_dir)
131 package_r_java_path = os.path.join(package_r_java_dir, 'R.java')
132 new_r_java = re.sub(r'package [.\w]*;', u'package %s;' % package,
133 r_java_contents)
134 codecs.open(package_r_java_path, 'w', encoding='utf-8').write(new_r_java)
135 else:
136 if len(extra_packages) != len(extra_r_text_files):
137 raise Exception('Need one R.txt file per extra package')
138
139 all_resources = {}
140 r_txt_file = os.path.join(r_dir, 'R.txt')
141 if not os.path.exists(r_txt_file):
142 return
143 with open(r_txt_file) as f:
144 for line in f:
145 m = re.match(r'(int(?:\[\])?) (\w+) (\w+) (.+)$', line)
146 if not m:
147 raise Exception('Unexpected line in R.txt: %s' % line)
148 java_type, resource_type, name, value = m.groups()
149 all_resources[(resource_type, name)] = (java_type, value)
150
151 for package, r_text_file in zip(extra_packages, extra_r_text_files):
152 if os.path.exists(r_text_file):
153 package_r_java_dir = os.path.join(r_dir, *package.split('.'))
154 build_utils.MakeDirectory(package_r_java_dir)
155 package_r_java_path = os.path.join(package_r_java_dir, 'R.java')
156 CreateExtraRJavaFile(
157 package, package_r_java_path, r_text_file, all_resources,
158 shared_resources)
159
160
161 def CreateExtraRJavaFile(
162 package, r_java_path, r_text_file, all_resources, shared_resources):
163 resources = {}
164 with open(r_text_file) as f:
165 for line in f:
166 m = re.match(r'int(?:\[\])? (\w+) (\w+) ', line)
167 if not m:
168 raise Exception('Unexpected line in R.txt: %s' % line)
169 resource_type, name = m.groups()
170 java_type, value = all_resources[(resource_type, name)]
171 if resource_type not in resources:
172 resources[resource_type] = []
173 resources[resource_type].append((name, java_type, value))
174
175 template = Template("""/* AUTO-GENERATED FILE. DO NOT MODIFY. */
176
177 package {{ package }};
178
179 public final class R {
180 {% for resource_type in resources %}
181 public static final class {{ resource_type }} {
182 {% for name, java_type, value in resources[resource_type] %}
183 {% if shared_resources %}
184 public static {{ java_type }} {{ name }} = {{ value }};
185 {% else %}
186 public static final {{ java_type }} {{ name }} = {{ value }};
187 {% endif %}
188 {% endfor %}
189 }
190 {% endfor %}
191 {% if shared_resources %}
192 public static void onResourcesLoaded(int packageId) {
193 {% for resource_type in resources %}
194 {% for name, java_type, value in resources[resource_type] %}
195 {% if java_type == 'int[]' %}
196 for(int i = 0; i < {{ resource_type }}.{{ name }}.length; ++i) {
197 {{ resource_type }}.{{ name }}[i] =
198 ({{ resource_type }}.{{ name }}[i] & 0x00ffffff)
199 | (packageId << 24);
200 }
201 {% else %}
202 {{ resource_type }}.{{ name }} =
203 ({{ resource_type }}.{{ name }} & 0x00ffffff)
204 | (packageId << 24);
205 {% endif %}
206 {% endfor %}
207 {% endfor %}
208 }
209 {% endif %}
210 }
211 """, trim_blocks=True, lstrip_blocks=True)
212
213 output = template.render(package=package, resources=resources,
214 shared_resources=shared_resources)
215 with open(r_java_path, 'w') as f:
216 f.write(output)
217
218
219 def CrunchDirectory(aapt, input_dir, output_dir):
220 """Crunches the images in input_dir and its subdirectories into output_dir.
221
222 If an image is already optimized, crunching often increases image size. In
223 this case, the crunched image is overwritten with the original image.
224 """
225 aapt_cmd = [aapt,
226 'crunch',
227 '-C', output_dir,
228 '-S', input_dir,
229 '--ignore-assets', build_utils.AAPT_IGNORE_PATTERN]
230 build_utils.CheckOutput(aapt_cmd, stderr_filter=FilterCrunchStderr,
231 fail_func=DidCrunchFail)
232
233 # Check for images whose size increased during crunching and replace them
234 # with their originals (except for 9-patches, which must be crunched).
235 for dir_, _, files in os.walk(output_dir):
236 for crunched in files:
237 if crunched.endswith('.9.png'):
238 continue
239 if not crunched.endswith('.png'):
240 raise Exception('Unexpected file in crunched dir: ' + crunched)
241 crunched = os.path.join(dir_, crunched)
242 original = os.path.join(input_dir, os.path.relpath(crunched, output_dir))
243 original_size = os.path.getsize(original)
244 crunched_size = os.path.getsize(crunched)
245 if original_size < crunched_size:
246 shutil.copyfile(original, crunched)
247
248
249 def FilterCrunchStderr(stderr):
250 """Filters out lines from aapt crunch's stderr that can safely be ignored."""
251 filtered_lines = []
252 for line in stderr.splitlines(True):
253 # Ignore this libpng warning, which is a known non-error condition.
254 # http://crbug.com/364355
255 if ('libpng warning: iCCP: Not recognizing known sRGB profile that has '
256 + 'been edited' in line):
257 continue
258 filtered_lines.append(line)
259 return ''.join(filtered_lines)
260
261
262 def DidCrunchFail(returncode, stderr):
263 """Determines whether aapt crunch failed from its return code and output.
264
265 Because aapt's return code cannot be trusted, any output to stderr is
266 an indication that aapt has failed (http://crbug.com/314885).
267 """
268 return returncode != 0 or stderr
269
270
271 def ZipResources(resource_dirs, zip_path):
272 # Python zipfile does not provide a way to replace a file (it just writes
273 # another file with the same name). So, first collect all the files to put
274 # in the zip (with proper overriding), and then zip them.
275 files_to_zip = dict()
276 for d in resource_dirs:
277 for root, _, files in os.walk(d):
278 for f in files:
279 archive_path = os.path.join(os.path.relpath(root, d), f)
280 path = os.path.join(root, f)
281 files_to_zip[archive_path] = path
282 with zipfile.ZipFile(zip_path, 'w') as outzip:
283 for archive_path, path in files_to_zip.iteritems():
284 outzip.write(path, archive_path)
285
286
287 def CombineZips(zip_files, output_path):
288 # When packaging resources, if the top-level directories in the zip file are
289 # of the form 0, 1, ..., then each subdirectory will be passed to aapt as a
290 # resources directory. While some resources just clobber others (image files,
291 # etc), other resources (particularly .xml files) need to be more
292 # intelligently merged. That merging is left up to aapt.
293 with zipfile.ZipFile(output_path, 'w') as outzip:
294 for i, z in enumerate(zip_files):
295 with zipfile.ZipFile(z, 'r') as inzip:
296 for name in inzip.namelist():
297 new_name = '%d/%s' % (i, name)
298 outzip.writestr(new_name, inzip.read(name))
299
300
301 def main():
302 args = build_utils.ExpandFileArgs(sys.argv[1:])
303
304 options = ParseArgs(args)
305 android_jar = os.path.join(options.android_sdk, 'android.jar')
306 aapt = options.aapt_path
307
308 input_files = []
309
310 with build_utils.TempDir() as temp_dir:
311 deps_dir = os.path.join(temp_dir, 'deps')
312 build_utils.MakeDirectory(deps_dir)
313 v14_dir = os.path.join(temp_dir, 'v14')
314 build_utils.MakeDirectory(v14_dir)
315
316 gen_dir = os.path.join(temp_dir, 'gen')
317 build_utils.MakeDirectory(gen_dir)
318
319 input_resource_dirs = build_utils.ParseGypList(options.resource_dirs)
320
321 if not options.v14_skip:
322 for resource_dir in input_resource_dirs:
323 generate_v14_compatible_resources.GenerateV14Resources(
324 resource_dir,
325 v14_dir)
326
327 dep_zips = build_utils.ParseGypList(options.dependencies_res_zips)
328 input_files += dep_zips
329 dep_subdirs = []
330 for z in dep_zips:
331 subdir = os.path.join(deps_dir, os.path.basename(z))
332 if os.path.exists(subdir):
333 raise Exception('Resource zip name conflict: ' + os.path.basename(z))
334 build_utils.ExtractAll(z, path=subdir)
335 dep_subdirs.append(subdir)
336
337 # Generate R.java. This R.java contains non-final constants and is used only
338 # while compiling the library jar (e.g. chromium_content.jar). When building
339 # an apk, a new R.java file with the correct resource -> ID mappings will be
340 # generated by merging the resources from all libraries and the main apk
341 # project.
342 package_command = [aapt,
343 'package',
344 '-m',
345 '-M', options.android_manifest,
346 '--auto-add-overlay',
347 '-I', android_jar,
348 '--output-text-symbols', gen_dir,
349 '-J', gen_dir,
350 '--ignore-assets', build_utils.AAPT_IGNORE_PATTERN]
351
352 for d in input_resource_dirs:
353 package_command += ['-S', d]
354
355 for d in dep_subdirs:
356 package_command += ['-S', d]
357
358 if options.non_constant_id:
359 package_command.append('--non-constant-id')
360 if options.custom_package:
361 package_command += ['--custom-package', options.custom_package]
362 if options.proguard_file:
363 package_command += ['-G', options.proguard_file]
364 if options.shared_resources:
365 package_command.append('--shared-lib')
366 build_utils.CheckOutput(package_command, print_stderr=False)
367
368 if options.extra_res_packages:
369 CreateExtraRJavaFiles(
370 gen_dir,
371 build_utils.ParseGypList(options.extra_res_packages),
372 build_utils.ParseGypList(options.extra_r_text_files),
373 options.shared_resources,
374 options.include_all_resources)
375
376 # This is the list of directories with resources to put in the final .zip
377 # file. The order of these is important so that crunched/v14 resources
378 # override the normal ones.
379 zip_resource_dirs = input_resource_dirs + [v14_dir]
380
381 base_crunch_dir = os.path.join(temp_dir, 'crunch')
382
383 # Crunch image resources. This shrinks png files and is necessary for
384 # 9-patch images to display correctly. 'aapt crunch' accepts only a single
385 # directory at a time and deletes everything in the output directory.
386 for idx, input_dir in enumerate(input_resource_dirs):
387 crunch_dir = os.path.join(base_crunch_dir, str(idx))
388 build_utils.MakeDirectory(crunch_dir)
389 zip_resource_dirs.append(crunch_dir)
390 CrunchDirectory(aapt, input_dir, crunch_dir)
391
392 ZipResources(zip_resource_dirs, options.resource_zip_out)
393
394 if options.all_resources_zip_out:
395 CombineZips([options.resource_zip_out] + dep_zips,
396 options.all_resources_zip_out)
397
398 if options.R_dir:
399 build_utils.DeleteDirectory(options.R_dir)
400 shutil.copytree(gen_dir, options.R_dir)
401 else:
402 build_utils.ZipDir(options.srcjar_out, gen_dir)
403
404 if options.r_text_out:
405 r_text_path = os.path.join(gen_dir, 'R.txt')
406 if os.path.exists(r_text_path):
407 shutil.copyfile(r_text_path, options.r_text_out)
408 else:
409 open(options.r_text_out, 'w').close()
410
411 if options.depfile:
412 input_files += build_utils.GetPythonDependencies()
413 build_utils.WriteDepfile(options.depfile, input_files)
414
415 if options.stamp:
416 build_utils.Touch(options.stamp)
417
418
419 if __name__ == '__main__':
420 main()
OLDNEW
« no previous file with comments | « build/android/gyp/package_resources.py ('k') | build/android/gyp/proguard.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698