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).
20 import generate_v14_compatible_resources
22 from util
import build_utils
25 """Parses command line options.
28 An options object as from optparse.OptionsParser.parse_args()
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')
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.')
65 '--extra-res-packages',
66 help='Additional package names to generate R.java files for')
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 '
74 '--all-resources-zip-out',
75 help='Path for output of all resources. This includes resources in '
78 parser
.add_option('--stamp', help='File to touch on success')
80 (options
, args
) = parser
.parse_args(args
)
83 parser
.error('No positional arguments should be given.')
85 # Check that required options have been provided.
90 'dependencies_res_zips',
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.')
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:
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).
136 for line
in stderr
.splitlines():
137 if line
and not 'libpng warning' in line
:
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
):
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
)
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')
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(
182 options
.v14_verify_only
)
184 dep_zips
= build_utils
.ParseGypList(options
.dependencies_res_zips
)
185 input_files
+= 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
199 package_command
= [aapt
,
202 '-M', options
.android_manifest
,
203 '--auto-add-overlay',
205 '--output-text-symbols', 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(
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
)
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
:
252 zip_resource_dirs
+ dep_subdirs
, options
.all_resources_zip_out
)
255 build_utils
.DeleteDirectory(options
.R_dir
)
256 shutil
.copytree(gen_dir
, options
.R_dir
)
258 build_utils
.ZipDir(options
.srcjar_out
, gen_dir
)
261 input_files
+= build_utils
.GetPythonDependencies()
262 build_utils
.WriteDepfile(options
.depfile
, input_files
)
265 build_utils
.Touch(options
.stamp
)
268 if __name__
== '__main__':