Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / build / android / gyp / process_resources.py
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 optparse
14 import os
15 import re
16 import zipfile
17
18 import generate_v14_compatible_resources
19
20 from util import build_utils
21
22 def ParseArgs():
23   """Parses command line options.
24
25   Returns:
26     An options object as from optparse.OptionsParser.parse_args()
27   """
28   parser = optparse.OptionParser()
29
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')
34
35   parser.add_option('--android-manifest', help='AndroidManifest.xml path')
36   parser.add_option('--custom-package', help='Java package for R.java')
37
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.')
42
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.')
46
47   parser.add_option('--proguard-file',
48                     help='Path to proguard.txt generated file')
49
50   parser.add_option(
51       '--v14-verify-only',
52       action='store_true',
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.')
56
57   parser.add_option(
58       '--extra-res-packages',
59       help='Additional package names to generate R.java files for')
60   parser.add_option(
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 '
64       'generated by aapt')
65
66   parser.add_option('--stamp', help='File to touch on success')
67
68   (options, args) = parser.parse_args()
69
70   if args:
71     parser.error('No positional arguments should be given.')
72
73   # Check that required options have been provided.
74   required_options = (
75       'android_sdk',
76       'android_sdk_tools',
77       'android_manifest',
78       'dependencies_res_zips',
79       'resource_dirs',
80       'resource_zip_out',
81       'R_dir',
82       )
83   build_utils.CheckOptions(options, parser, required=required_options)
84
85   return options
86
87
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')
93
94   java_files = build_utils.FindInDirectory(r_dir, "R.java")
95   if len(java_files) != 1:
96     return
97   r_java_file = java_files[0]
98   r_java_contents = open(r_java_file).read()
99
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.
110
111
112
113
114
115
116 def DidCrunchFail(returncode, stderr):
117   """Determines whether aapt crunch failed from its return code and output.
118
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).
123   """
124   if returncode != 0:
125     return True
126   for line in stderr.splitlines():
127     if line and not 'libpng warning' in line:
128       return True
129   return False
130
131
132 def main():
133   options = ParseArgs()
134   android_jar = os.path.join(options.android_sdk, 'android.jar')
135   aapt = os.path.join(options.android_sdk_tools, 'aapt')
136
137   build_utils.DeleteDirectory(options.R_dir)
138   build_utils.MakeDirectory(options.R_dir)
139
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)
145
146     input_resource_dirs = build_utils.ParseGypList(options.resource_dirs)
147
148     for resource_dir in input_resource_dirs:
149       generate_v14_compatible_resources.GenerateV14Resources(
150           resource_dir,
151           v14_dir,
152           options.v14_verify_only)
153
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
158     # project.
159     package_command = [aapt,
160                        'package',
161                        '-m',
162                        '-M', options.android_manifest,
163                        '--auto-add-overlay',
164                        '-I', android_jar,
165                        '--output-text-symbols', options.R_dir,
166                        '-J', options.R_dir]
167
168     for d in input_resource_dirs:
169       package_command += ['-S', d]
170
171     dep_zips = build_utils.ParseGypList(options.dependencies_res_zips)
172     for z in dep_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]
178
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)
186
187     if options.extra_res_packages:
188       CreateExtraRJavaFiles(
189           options.R_dir,
190           build_utils.ParseGypList(options.extra_res_packages),
191           build_utils.ParseGypList(options.extra_r_text_files))
192
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]
197
198     base_crunch_dir = os.path.join(temp_dir, 'crunch')
199
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)
207       aapt_cmd = [aapt,
208                   'crunch',
209                   '-C', crunch_dir,
210                   '-S', d]
211       build_utils.CheckOutput(aapt_cmd, fail_func=DidCrunchFail)
212
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):
219         for f in files:
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)
226
227     if options.stamp:
228       build_utils.Touch(options.stamp)
229
230
231 if __name__ == '__main__':
232   main()