Add beginnings of android_apk and unittest_apk templates
[chromium-blink-merge.git] / build / android / gyp / process_resources.py
blob0fe02dfa84877a41a354526da0eaf635ecf3774b
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 parser.add_option(
68 '--extra-r-text-files',
69 help='For each additional package, the R.txt file should contain a '
70 'list of resources to be included in the R.java file in the format '
71 'generated by aapt')
73 parser.add_option(
74 '--all-resources-zip-out',
75 help='Path for output of all resources. This includes resources in '
76 'dependencies.')
78 parser.add_option('--stamp', help='File to touch on success')
80 (options, args) = parser.parse_args(args)
82 if args:
83 parser.error('No positional arguments should be given.')
85 # Check that required options have been provided.
86 required_options = (
87 'android_sdk',
88 'android_sdk_tools',
89 'android_manifest',
90 'dependencies_res_zips',
91 'resource_dirs',
92 'resource_zip_out',
94 build_utils.CheckOptions(options, parser, required=required_options)
96 if (options.R_dir is None) == (options.srcjar_out is None):
97 raise Exception('Exactly one of --R-dir or --srcjar-out must be specified.')
99 return options
102 def CreateExtraRJavaFiles(
103 r_dir, extra_packages, extra_r_text_files):
104 if len(extra_packages) != len(extra_r_text_files):
105 raise Exception('--extra-res-packages and --extra-r-text-files'
106 'should have the same length')
108 java_files = build_utils.FindInDirectory(r_dir, "R.java")
109 if len(java_files) != 1:
110 return
111 r_java_file = java_files[0]
112 r_java_contents = open(r_java_file).read()
114 for package in extra_packages:
115 package_r_java_dir = os.path.join(r_dir, *package.split('.'))
116 build_utils.MakeDirectory(package_r_java_dir)
117 package_r_java_path = os.path.join(package_r_java_dir, 'R.java')
118 open(package_r_java_path, 'w').write(
119 re.sub(r'package [.\w]*;', 'package %s;' % package, r_java_contents))
120 # TODO(cjhopman): These extra package's R.java files should be filtered to
121 # only contain the resources listed in their R.txt files. At this point, we
122 # have already compiled those other libraries, so doing this would only
123 # affect how the code in this .apk target could refer to the resources.
126 def DidCrunchFail(returncode, stderr):
127 """Determines whether aapt crunch failed from its return code and output.
129 Because aapt's return code cannot be trusted, any output to stderr is
130 an indication that aapt has failed (http://crbug.com/314885), except
131 lines that contain "libpng warning", which is a known non-error condition
132 (http://crbug.com/364355).
134 if returncode != 0:
135 return True
136 for line in stderr.splitlines():
137 if line and not 'libpng warning' in line:
138 return True
139 return False
142 def ZipResources(resource_dirs, zip_path):
143 # Python zipfile does not provide a way to replace a file (it just writes
144 # another file with the same name). So, first collect all the files to put
145 # in the zip (with proper overriding), and then zip them.
146 files_to_zip = dict()
147 for d in resource_dirs:
148 for root, _, files in os.walk(d):
149 for f in files:
150 archive_path = os.path.join(os.path.relpath(root, d), f)
151 path = os.path.join(root, f)
152 files_to_zip[archive_path] = path
153 with zipfile.ZipFile(zip_path, 'w') as outzip:
154 for archive_path, path in files_to_zip.iteritems():
155 outzip.write(path, archive_path)
158 def main():
159 args = build_utils.ExpandFileArgs(sys.argv[1:])
161 options = ParseArgs(args)
162 android_jar = os.path.join(options.android_sdk, 'android.jar')
163 aapt = os.path.join(options.android_sdk_tools, 'aapt')
165 input_files = []
167 with build_utils.TempDir() as temp_dir:
168 deps_dir = os.path.join(temp_dir, 'deps')
169 build_utils.MakeDirectory(deps_dir)
170 v14_dir = os.path.join(temp_dir, 'v14')
171 build_utils.MakeDirectory(v14_dir)
173 gen_dir = os.path.join(temp_dir, 'gen')
174 build_utils.MakeDirectory(gen_dir)
176 input_resource_dirs = build_utils.ParseGypList(options.resource_dirs)
178 for resource_dir in input_resource_dirs:
179 generate_v14_compatible_resources.GenerateV14Resources(
180 resource_dir,
181 v14_dir,
182 options.v14_verify_only)
184 dep_zips = build_utils.ParseGypList(options.dependencies_res_zips)
185 input_files += dep_zips
186 dep_subdirs = []
187 for z in dep_zips:
188 subdir = os.path.join(deps_dir, os.path.basename(z))
189 if os.path.exists(subdir):
190 raise Exception('Resource zip name conflict: ' + os.path.basename(z))
191 build_utils.ExtractAll(z, path=subdir)
192 dep_subdirs.append(subdir)
194 # Generate R.java. This R.java contains non-final constants and is used only
195 # while compiling the library jar (e.g. chromium_content.jar). When building
196 # an apk, a new R.java file with the correct resource -> ID mappings will be
197 # generated by merging the resources from all libraries and the main apk
198 # project.
199 package_command = [aapt,
200 'package',
201 '-m',
202 '-M', options.android_manifest,
203 '--auto-add-overlay',
204 '-I', android_jar,
205 '--output-text-symbols', gen_dir,
206 '-J', gen_dir]
208 for d in input_resource_dirs:
209 package_command += ['-S', d]
211 for d in dep_subdirs:
212 package_command += ['-S', d]
214 if options.non_constant_id:
215 package_command.append('--non-constant-id')
216 if options.custom_package:
217 package_command += ['--custom-package', options.custom_package]
218 if options.proguard_file:
219 package_command += ['-G', options.proguard_file]
220 build_utils.CheckOutput(package_command, print_stderr=False)
222 if options.extra_res_packages:
223 CreateExtraRJavaFiles(
224 gen_dir,
225 build_utils.ParseGypList(options.extra_res_packages),
226 build_utils.ParseGypList(options.extra_r_text_files))
228 # This is the list of directories with resources to put in the final .zip
229 # file. The order of these is important so that crunched/v14 resources
230 # override the normal ones.
231 zip_resource_dirs = input_resource_dirs + [v14_dir]
233 base_crunch_dir = os.path.join(temp_dir, 'crunch')
235 # Crunch image resources. This shrinks png files and is necessary for
236 # 9-patch images to display correctly. 'aapt crunch' accepts only a single
237 # directory at a time and deletes everything in the output directory.
238 for idx, d in enumerate(input_resource_dirs):
239 crunch_dir = os.path.join(base_crunch_dir, str(idx))
240 build_utils.MakeDirectory(crunch_dir)
241 zip_resource_dirs.append(crunch_dir)
242 aapt_cmd = [aapt,
243 'crunch',
244 '-C', crunch_dir,
245 '-S', d]
246 build_utils.CheckOutput(aapt_cmd, fail_func=DidCrunchFail)
248 ZipResources(zip_resource_dirs, options.resource_zip_out)
250 if options.all_resources_zip_out:
251 ZipResources(
252 zip_resource_dirs + dep_subdirs, options.all_resources_zip_out)
254 if options.R_dir:
255 build_utils.DeleteDirectory(options.R_dir)
256 shutil.copytree(gen_dir, options.R_dir)
257 else:
258 build_utils.ZipDir(options.srcjar_out, gen_dir)
260 if options.depfile:
261 input_files += build_utils.GetPythonDependencies()
262 build_utils.WriteDepfile(options.depfile, input_files)
264 if options.stamp:
265 build_utils.Touch(options.stamp)
268 if __name__ == '__main__':
269 main()