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).
18 import generate_v14_compatible_resources
20 from util import build_utils
23 """Parses command line options.
26 An options object as from optparse.OptionsParser.parse_args()
28 parser = optparse.OptionParser()
30 parser.add_option('--android-sdk', help='path to the Android SDK folder')
31 parser.add_option('--android-sdk-tools',
32 help='path to the Android SDK build tools folder')
33 parser.add_option('--non-constant-id', action='store_true')
35 parser.add_option('--android-manifest', help='AndroidManifest.xml path')
36 parser.add_option('--custom-package', help='Java package for R.java')
38 parser.add_option('--resource-dirs',
39 help='Directories containing resources of this target.')
40 parser.add_option('--dependencies-res-zips',
41 help='Resources from dependents.')
43 parser.add_option('--R-dir', help='directory to hold generated R.java')
44 parser.add_option('--resource-zip-out',
45 help='Path for output zipped resources.')
47 parser.add_option('--proguard-file',
48 help='Path to proguard.txt generated file')
53 help='Do not generate v14 resources. Instead, just verify that the '
54 'resources are already compatible with v14, i.e. they don\'t use '
55 'attributes that cause crashes on certain devices.')
58 '--extra-res-packages',
59 help='Additional package names to generate R.java files for')
61 '--extra-r-text-files',
62 help='For each additional package, the R.txt file should contain a '
63 'list of resources to be included in the R.java file in the format '
66 parser.add_option('--stamp', help='File to touch on success')
68 (options, args) = parser.parse_args()
71 parser.error('No positional arguments should be given.')
73 # Check that required options have been provided.
78 'dependencies_res_zips',
83 build_utils.CheckOptions(options, parser, required=required_options)
88 def CreateExtraRJavaFiles(
89 r_dir, extra_packages, extra_r_text_files):
90 if len(extra_packages) != len(extra_r_text_files):
91 raise Exception('--extra-res-packages and --extra-r-text-files'
92 'should have the same length')
94 java_files = build_utils.FindInDirectory(r_dir, "R.java")
95 if len(java_files) != 1:
97 r_java_file = java_files[0]
98 r_java_contents = open(r_java_file).read()
100 for package in extra_packages:
101 package_r_java_dir = os.path.join(r_dir, *package.split('.'))
102 build_utils.MakeDirectory(package_r_java_dir)
103 package_r_java_path = os.path.join(package_r_java_dir, 'R.java')
104 open(package_r_java_path, 'w').write(
105 re.sub(r'package [.\w]*;', 'package %s;' % package, r_java_contents))
106 # TODO(cjhopman): These extra package's R.java files should be filtered to
107 # only contain the resources listed in their R.txt files. At this point, we
108 # have already compiled those other libraries, so doing this would only
109 # affect how the code in this .apk target could refer to the resources.
116 def DidCrunchFail(returncode, stderr):
117 """Determines whether aapt crunch failed from its return code and output.
119 Because aapt's return code cannot be trusted, any output to stderr is
120 an indication that aapt has failed (http://crbug.com/314885), except
121 lines that contain "libpng warning", which is a known non-error condition
122 (http://crbug.com/364355).
126 for line in stderr.splitlines():
127 if line and not 'libpng warning' in line:
133 options = ParseArgs()
134 android_jar = os.path.join(options.android_sdk, 'android.jar')
135 aapt = os.path.join(options.android_sdk_tools, 'aapt')
137 build_utils.DeleteDirectory(options.R_dir)
138 build_utils.MakeDirectory(options.R_dir)
140 with build_utils.TempDir() as temp_dir:
141 deps_dir = os.path.join(temp_dir, 'deps')
142 build_utils.MakeDirectory(deps_dir)
143 v14_dir = os.path.join(temp_dir, 'v14')
144 build_utils.MakeDirectory(v14_dir)
146 input_resource_dirs = build_utils.ParseGypList(options.resource_dirs)
148 for resource_dir in input_resource_dirs:
149 generate_v14_compatible_resources.GenerateV14Resources(
152 options.v14_verify_only)
154 # Generate R.java. This R.java contains non-final constants and is used only
155 # while compiling the library jar (e.g. chromium_content.jar). When building
156 # an apk, a new R.java file with the correct resource -> ID mappings will be
157 # generated by merging the resources from all libraries and the main apk
159 package_command = [aapt,
162 '-M', options.android_manifest,
163 '--auto-add-overlay',
165 '--output-text-symbols', options.R_dir,
168 for d in input_resource_dirs:
169 package_command += ['-S', d]
171 dep_zips = build_utils.ParseGypList(options.dependencies_res_zips)
173 subdir = os.path.join(deps_dir, os.path.basename(z))
174 if os.path.exists(subdir):
175 raise Exception('Resource zip name conflict: ' + os.path.basename(z))
176 build_utils.ExtractAll(z, path=subdir)
177 package_command += ['-S', subdir]
179 if options.non_constant_id:
180 package_command.append('--non-constant-id')
181 if options.custom_package:
182 package_command += ['--custom-package', options.custom_package]
183 if options.proguard_file:
184 package_command += ['-G', options.proguard_file]
185 build_utils.CheckOutput(package_command, print_stderr=False)
187 if options.extra_res_packages:
188 CreateExtraRJavaFiles(
190 build_utils.ParseGypList(options.extra_res_packages),
191 build_utils.ParseGypList(options.extra_r_text_files))
193 # This is the list of directories with resources to put in the final .zip
194 # file. The order of these is important so that crunched/v14 resources
195 # override the normal ones.
196 zip_resource_dirs = input_resource_dirs + [v14_dir]
198 base_crunch_dir = os.path.join(temp_dir, 'crunch')
200 # Crunch image resources. This shrinks png files and is necessary for
201 # 9-patch images to display correctly. 'aapt crunch' accepts only a single
202 # directory at a time and deletes everything in the output directory.
203 for idx, d in enumerate(input_resource_dirs):
204 crunch_dir = os.path.join(base_crunch_dir, str(idx))
205 build_utils.MakeDirectory(crunch_dir)
206 zip_resource_dirs.append(crunch_dir)
211 build_utils.CheckOutput(aapt_cmd, fail_func=DidCrunchFail)
213 # Python zipfile does not provide a way to replace a file (it just writes
214 # another file with the same name). So, first collect all the files to put
215 # in the zip (with proper overriding), and then zip them.
216 files_to_zip = dict()
217 for d in zip_resource_dirs:
218 for root, _, files in os.walk(d):
220 archive_path = os.path.join(os.path.relpath(root, d), f)
221 path = os.path.join(root, f)
222 files_to_zip[archive_path] = path
223 with zipfile.ZipFile(options.resource_zip_out, 'w') as outzip:
224 for archive_path, path in files_to_zip.iteritems():
225 outzip.write(path, archive_path)
228 build_utils.Touch(options.stamp)
231 if __name__ == '__main__':