Instrumental test for BookmarksBridge. It currently tests these functionalities:...
[chromium-blink-merge.git] / build / android / gyp / process_resources.py
blobfd3206437d8584d768e876e49c2f88ad5027f145
1 #!/usr/bin/env python
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.
7 """Process Android resources to generate R.java, and prepare for packaging.
9 This will crunch images and generate v14 compatible resources
10 (see generate_v14_compatible_resources.py).
11 """
13 import optparse
14 import os
15 import re
16 import shutil
17 import sys
18 import zipfile
20 import generate_v14_compatible_resources
22 from util import build_utils
24 def ParseArgs(args):
25 """Parses command line options.
27 Returns:
28 An options object as from optparse.OptionsParser.parse_args()
29 """
30 parser = optparse.OptionParser()
31 build_utils.AddDepfileOption(parser)
33 parser.add_option('--android-sdk', help='path to the Android SDK folder')
34 parser.add_option('--android-sdk-tools',
35 help='path to the Android SDK build tools folder')
36 parser.add_option('--non-constant-id', action='store_true')
38 parser.add_option('--android-manifest', help='AndroidManifest.xml path')
39 parser.add_option('--custom-package', help='Java package for R.java')
41 parser.add_option('--resource-dirs',
42 help='Directories containing resources of this target.')
43 parser.add_option('--dependencies-res-zips',
44 help='Resources from dependents.')
46 parser.add_option('--resource-zip-out',
47 help='Path for output zipped resources.')
49 parser.add_option('--R-dir',
50 help='directory to hold generated R.java.')
51 parser.add_option('--srcjar-out',
52 help='Path to srcjar to contain generated R.java.')
54 parser.add_option('--proguard-file',
55 help='Path to proguard.txt generated file')
57 parser.add_option(
58 '--v14-verify-only',
59 action='store_true',
60 help='Do not generate v14 resources. Instead, just verify that the '
61 'resources are already compatible with v14, i.e. they don\'t use '
62 'attributes that cause crashes on certain devices.')
64 parser.add_option(
65 '--extra-res-packages',
66 help='Additional package names to generate R.java files for')
67 # TODO(cjhopman): Actually use --extra-r-text-files. We currently include all
68 # the resources in all R.java files for a particular apk.
69 parser.add_option(
70 '--extra-r-text-files',
71 help='For each additional package, the R.txt file should contain a '
72 'list of resources to be included in the R.java file in the format '
73 'generated by aapt')
75 parser.add_option(
76 '--all-resources-zip-out',
77 help='Path for output of all resources. This includes resources in '
78 'dependencies.')
80 parser.add_option('--stamp', help='File to touch on success')
82 (options, args) = parser.parse_args(args)
84 if args:
85 parser.error('No positional arguments should be given.')
87 # Check that required options have been provided.
88 required_options = (
89 'android_sdk',
90 'android_sdk_tools',
91 'android_manifest',
92 'dependencies_res_zips',
93 'resource_dirs',
94 'resource_zip_out',
96 build_utils.CheckOptions(options, parser, required=required_options)
98 if (options.R_dir is None) == (options.srcjar_out is None):
99 raise Exception('Exactly one of --R-dir or --srcjar-out must be specified.')
101 return options
104 def CreateExtraRJavaFiles(r_dir, extra_packages):
105 java_files = build_utils.FindInDirectory(r_dir, "R.java")
106 if len(java_files) != 1:
107 return
108 r_java_file = java_files[0]
109 r_java_contents = open(r_java_file).read()
111 for package in extra_packages:
112 package_r_java_dir = os.path.join(r_dir, *package.split('.'))
113 build_utils.MakeDirectory(package_r_java_dir)
114 package_r_java_path = os.path.join(package_r_java_dir, 'R.java')
115 open(package_r_java_path, 'w').write(
116 re.sub(r'package [.\w]*;', 'package %s;' % package, r_java_contents))
117 # TODO(cjhopman): These extra package's R.java files should be filtered to
118 # only contain the resources listed in their R.txt files. At this point, we
119 # have already compiled those other libraries, so doing this would only
120 # affect how the code in this .apk target could refer to the resources.
123 def DidCrunchFail(returncode, stderr):
124 """Determines whether aapt crunch failed from its return code and output.
126 Because aapt's return code cannot be trusted, any output to stderr is
127 an indication that aapt has failed (http://crbug.com/314885), except
128 lines that contain "libpng warning", which is a known non-error condition
129 (http://crbug.com/364355).
131 if returncode != 0:
132 return True
133 for line in stderr.splitlines():
134 if line and not 'libpng warning' in line:
135 return True
136 return False
139 def ZipResources(resource_dirs, zip_path):
140 # Python zipfile does not provide a way to replace a file (it just writes
141 # another file with the same name). So, first collect all the files to put
142 # in the zip (with proper overriding), and then zip them.
143 files_to_zip = dict()
144 for d in resource_dirs:
145 for root, _, files in os.walk(d):
146 for f in files:
147 archive_path = os.path.join(os.path.relpath(root, d), f)
148 path = os.path.join(root, f)
149 files_to_zip[archive_path] = path
150 with zipfile.ZipFile(zip_path, 'w') as outzip:
151 for archive_path, path in files_to_zip.iteritems():
152 outzip.write(path, archive_path)
155 def CombineZips(zip_files, output_path):
156 # When packaging resources, if the top-level directories in the zip file are
157 # of the form 0, 1, ..., then each subdirectory will be passed to aapt as a
158 # resources directory. While some resources just clobber others (image files,
159 # etc), other resources (particularly .xml files) need to be more
160 # intelligently merged. That merging is left up to aapt.
161 with zipfile.ZipFile(output_path, 'w') as outzip:
162 for i, z in enumerate(zip_files):
163 with zipfile.ZipFile(z, 'r') as inzip:
164 for name in inzip.namelist():
165 new_name = '%d/%s' % (i, name)
166 outzip.writestr(new_name, inzip.read(name))
169 def main():
170 args = build_utils.ExpandFileArgs(sys.argv[1:])
172 options = ParseArgs(args)
173 android_jar = os.path.join(options.android_sdk, 'android.jar')
174 aapt = os.path.join(options.android_sdk_tools, 'aapt')
176 input_files = []
178 with build_utils.TempDir() as temp_dir:
179 deps_dir = os.path.join(temp_dir, 'deps')
180 build_utils.MakeDirectory(deps_dir)
181 v14_dir = os.path.join(temp_dir, 'v14')
182 build_utils.MakeDirectory(v14_dir)
184 gen_dir = os.path.join(temp_dir, 'gen')
185 build_utils.MakeDirectory(gen_dir)
187 input_resource_dirs = build_utils.ParseGypList(options.resource_dirs)
189 for resource_dir in input_resource_dirs:
190 generate_v14_compatible_resources.GenerateV14Resources(
191 resource_dir,
192 v14_dir,
193 options.v14_verify_only)
195 dep_zips = build_utils.ParseGypList(options.dependencies_res_zips)
196 input_files += dep_zips
197 dep_subdirs = []
198 for z in dep_zips:
199 subdir = os.path.join(deps_dir, os.path.basename(z))
200 if os.path.exists(subdir):
201 raise Exception('Resource zip name conflict: ' + os.path.basename(z))
202 build_utils.ExtractAll(z, path=subdir)
203 dep_subdirs.append(subdir)
205 # Generate R.java. This R.java contains non-final constants and is used only
206 # while compiling the library jar (e.g. chromium_content.jar). When building
207 # an apk, a new R.java file with the correct resource -> ID mappings will be
208 # generated by merging the resources from all libraries and the main apk
209 # project.
210 package_command = [aapt,
211 'package',
212 '-m',
213 '-M', options.android_manifest,
214 '--auto-add-overlay',
215 '-I', android_jar,
216 '--output-text-symbols', gen_dir,
217 '-J', gen_dir]
219 for d in input_resource_dirs:
220 package_command += ['-S', d]
222 for d in dep_subdirs:
223 package_command += ['-S', d]
225 if options.non_constant_id:
226 package_command.append('--non-constant-id')
227 if options.custom_package:
228 package_command += ['--custom-package', options.custom_package]
229 if options.proguard_file:
230 package_command += ['-G', options.proguard_file]
231 build_utils.CheckOutput(package_command, print_stderr=False)
233 if options.extra_res_packages:
234 CreateExtraRJavaFiles(
235 gen_dir,
236 build_utils.ParseGypList(options.extra_res_packages))
238 # This is the list of directories with resources to put in the final .zip
239 # file. The order of these is important so that crunched/v14 resources
240 # override the normal ones.
241 zip_resource_dirs = input_resource_dirs + [v14_dir]
243 base_crunch_dir = os.path.join(temp_dir, 'crunch')
245 # Crunch image resources. This shrinks png files and is necessary for
246 # 9-patch images to display correctly. 'aapt crunch' accepts only a single
247 # directory at a time and deletes everything in the output directory.
248 for idx, d in enumerate(input_resource_dirs):
249 crunch_dir = os.path.join(base_crunch_dir, str(idx))
250 build_utils.MakeDirectory(crunch_dir)
251 zip_resource_dirs.append(crunch_dir)
252 aapt_cmd = [aapt,
253 'crunch',
254 '-C', crunch_dir,
255 '-S', d]
256 build_utils.CheckOutput(aapt_cmd, fail_func=DidCrunchFail)
258 ZipResources(zip_resource_dirs, options.resource_zip_out)
260 if options.all_resources_zip_out:
261 CombineZips([options.resource_zip_out] + dep_zips,
262 options.all_resources_zip_out)
264 if options.R_dir:
265 build_utils.DeleteDirectory(options.R_dir)
266 shutil.copytree(gen_dir, options.R_dir)
267 else:
268 build_utils.ZipDir(options.srcjar_out, gen_dir)
270 if options.depfile:
271 input_files += build_utils.GetPythonDependencies()
272 build_utils.WriteDepfile(options.depfile, input_files)
274 if options.stamp:
275 build_utils.Touch(options.stamp)
278 if __name__ == '__main__':
279 main()