blob: f2c2a6ed9ee6329bc3a44a6b487c87aef41dd997 [file] [log] [blame]
James Robinson646469d2014-10-03 15:33:28 -07001#!/usr/bin/env python
2#
3# Copyright 2014 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# pylint: disable=C0301
8"""Package resources into an apk.
9
10See https://android.googlesource.com/platform/tools/base/+/master/legacy/ant-tasks/src/main/java/com/android/ant/AaptExecTask.java
11and
12https://android.googlesource.com/platform/sdk/+/master/files/ant/build.xml
13"""
14# pylint: enable=C0301
15
16import optparse
17import os
18import shutil
Viet-Trung Luu235cf3d2015-06-11 10:01:25 -070019import zipfile
James Robinson646469d2014-10-03 15:33:28 -070020
21from util import build_utils
22
Viet-Trung Luu235cf3d2015-06-11 10:01:25 -070023
24# List is generated from the chrome_apk.apk_intermediates.ap_ via:
25# unzip -l $FILE_AP_ | cut -c31- | grep res/draw | cut -d'/' -f 2 | sort \
26# | uniq | grep -- -tvdpi- | cut -c10-
27# and then manually sorted.
28# Note that we can't just do a cross-product of dimentions because the filenames
29# become too big and aapt fails to create the files.
30# This leaves all default drawables (mdpi) in the main apk. Android gets upset
31# though if any drawables are missing from the default drawables/ directory.
32DENSITY_SPLITS = {
33 'hdpi': (
34 'hdpi-v4', # Order matters for output file names.
35 'ldrtl-hdpi-v4',
36 'sw600dp-hdpi-v13',
37 'ldrtl-hdpi-v17',
38 'ldrtl-sw600dp-hdpi-v17',
39 'hdpi-v21',
40 ),
41 'xhdpi': (
42 'xhdpi-v4',
43 'ldrtl-xhdpi-v4',
44 'sw600dp-xhdpi-v13',
45 'ldrtl-xhdpi-v17',
46 'ldrtl-sw600dp-xhdpi-v17',
47 'xhdpi-v21',
48 ),
49 'xxhdpi': (
50 'xxhdpi-v4',
51 'ldrtl-xxhdpi-v4',
52 'sw600dp-xxhdpi-v13',
53 'ldrtl-xxhdpi-v17',
54 'ldrtl-sw600dp-xxhdpi-v17',
55 'xxhdpi-v21',
56 ),
57 'tvdpi': (
58 'tvdpi-v4',
59 'sw600dp-tvdpi-v13',
60 'ldrtl-sw600dp-tvdpi-v17',
61 ),
62}
63
64
James Robinson646469d2014-10-03 15:33:28 -070065def ParseArgs():
66 """Parses command line options.
67
68 Returns:
69 An options object as from optparse.OptionsParser.parse_args()
70 """
71 parser = optparse.OptionParser()
72 build_utils.AddDepfileOption(parser)
73 parser.add_option('--android-sdk', help='path to the Android SDK folder')
Viet-Trung Luu235cf3d2015-06-11 10:01:25 -070074 parser.add_option('--aapt-path',
75 help='path to the Android aapt tool')
James Robinson646469d2014-10-03 15:33:28 -070076
77 parser.add_option('--configuration-name',
78 help='Gyp\'s configuration name (Debug or Release).')
79
80 parser.add_option('--android-manifest', help='AndroidManifest.xml path')
81 parser.add_option('--version-code', help='Version code for apk.')
82 parser.add_option('--version-name', help='Version name for apk.')
James Robinson6e9a1c92014-11-13 17:05:42 -080083 parser.add_option(
84 '--shared-resources',
85 action='store_true',
86 help='Make a resource package that can be loaded by a different'
87 'application at runtime to access the package\'s resources.')
James Robinson646469d2014-10-03 15:33:28 -070088 parser.add_option('--resource-zips',
89 help='zip files containing resources to be packaged')
90 parser.add_option('--asset-dir',
91 help='directories containing assets to be packaged')
92 parser.add_option('--no-compress', help='disables compression for the '
93 'given comma separated list of extensions')
Viet-Trung Luu235cf3d2015-06-11 10:01:25 -070094 parser.add_option(
95 '--create-density-splits',
96 action='store_true',
97 help='Enables density splits')
James Robinson646469d2014-10-03 15:33:28 -070098
99 parser.add_option('--apk-path',
100 help='Path to output (partial) apk.')
101
102 (options, args) = parser.parse_args()
103
104 if args:
105 parser.error('No positional arguments should be given.')
106
107 # Check that required options have been provided.
Viet-Trung Luu235cf3d2015-06-11 10:01:25 -0700108 required_options = ('android_sdk', 'aapt_path', 'configuration_name',
James Robinson646469d2014-10-03 15:33:28 -0700109 'android_manifest', 'version_code', 'version_name',
Viet-Trung Luu235cf3d2015-06-11 10:01:25 -0700110 'apk_path')
James Robinson646469d2014-10-03 15:33:28 -0700111
112 build_utils.CheckOptions(options, parser, required=required_options)
113
114 return options
115
116
117def MoveImagesToNonMdpiFolders(res_root):
118 """Move images from drawable-*-mdpi-* folders to drawable-* folders.
119
120 Why? http://crbug.com/289843
121 """
122 for src_dir_name in os.listdir(res_root):
123 src_components = src_dir_name.split('-')
124 if src_components[0] != 'drawable' or 'mdpi' not in src_components:
125 continue
126 src_dir = os.path.join(res_root, src_dir_name)
127 if not os.path.isdir(src_dir):
128 continue
129 dst_components = [c for c in src_components if c != 'mdpi']
130 assert dst_components != src_components
131 dst_dir_name = '-'.join(dst_components)
132 dst_dir = os.path.join(res_root, dst_dir_name)
133 build_utils.MakeDirectory(dst_dir)
134 for src_file_name in os.listdir(src_dir):
135 if not src_file_name.endswith('.png'):
136 continue
137 src_file = os.path.join(src_dir, src_file_name)
138 dst_file = os.path.join(dst_dir, src_file_name)
139 assert not os.path.lexists(dst_file)
140 shutil.move(src_file, dst_file)
141
142
143def PackageArgsForExtractedZip(d):
144 """Returns the aapt args for an extracted resources zip.
145
146 A resources zip either contains the resources for a single target or for
147 multiple targets. If it is multiple targets merged into one, the actual
148 resource directories will be contained in the subdirectories 0, 1, 2, ...
149 """
James Robinson646469d2014-10-03 15:33:28 -0700150 subdirs = [os.path.join(d, s) for s in os.listdir(d)]
Dave Moore0ae79f42015-03-17 12:56:46 -0700151 subdirs = [s for s in subdirs if os.path.isdir(s)]
152 is_multi = '0' in [os.path.basename(s) for s in subdirs]
153 if is_multi:
154 res_dirs = sorted(subdirs, key=lambda p : int(os.path.basename(p)))
James Robinson646469d2014-10-03 15:33:28 -0700155 else:
156 res_dirs = [d]
157 package_command = []
158 for d in res_dirs:
159 MoveImagesToNonMdpiFolders(d)
160 package_command += ['-S', d]
161 return package_command
162
163
Viet-Trung Luu235cf3d2015-06-11 10:01:25 -0700164def RenameDensitySplits(apk_path):
165 """Renames all density splits to have shorter / predictable names."""
166 for density, config in DENSITY_SPLITS.iteritems():
167 src_path = '%s_%s' % (apk_path, '_'.join(config))
168 dst_path = '%s-%s' % (apk_path, density)
169 if os.path.exists(dst_path):
170 os.unlink(dst_path)
171 os.rename(src_path, dst_path)
172
173
174def CheckDensityMissedConfigs(apk_path):
175 """Raises an exception if apk_path contains any density-specifc files."""
176 triggers = ['-%s' % density for density in DENSITY_SPLITS]
177 with zipfile.ZipFile(apk_path) as main_apk_zip:
178 for name in main_apk_zip.namelist():
179 for trigger in triggers:
180 if trigger in name and not 'mipmap-' in name:
181 raise Exception(('Found density in main apk that should have been ' +
182 'put into a split: %s\nYou need to update ' +
183 'package_resources.py to include this new ' +
184 'config.') % name)
185
186
James Robinson646469d2014-10-03 15:33:28 -0700187def main():
188 options = ParseArgs()
189 android_jar = os.path.join(options.android_sdk, 'android.jar')
Viet-Trung Luu235cf3d2015-06-11 10:01:25 -0700190 aapt = options.aapt_path
James Robinson646469d2014-10-03 15:33:28 -0700191
192 with build_utils.TempDir() as temp_dir:
193 package_command = [aapt,
194 'package',
195 '--version-code', options.version_code,
196 '--version-name', options.version_name,
197 '-M', options.android_manifest,
198 '--no-crunch',
199 '-f',
200 '--auto-add-overlay',
James Robinson646469d2014-10-03 15:33:28 -0700201 '-I', android_jar,
202 '-F', options.apk_path,
James Robinson30d547e2014-10-23 18:20:06 -0700203 '--ignore-assets', build_utils.AAPT_IGNORE_PATTERN,
James Robinson646469d2014-10-03 15:33:28 -0700204 ]
205
206 if options.no_compress:
207 for ext in options.no_compress.split(','):
208 package_command += ['-0', ext]
James Robinson6e9a1c92014-11-13 17:05:42 -0800209 if options.shared_resources:
210 package_command.append('--shared-lib')
James Robinson646469d2014-10-03 15:33:28 -0700211
Viet-Trung Luu235cf3d2015-06-11 10:01:25 -0700212 if options.asset_dir and os.path.exists(options.asset_dir):
James Robinson646469d2014-10-03 15:33:28 -0700213 package_command += ['-A', options.asset_dir]
214
Viet-Trung Luu235cf3d2015-06-11 10:01:25 -0700215 if options.resource_zips:
216 dep_zips = build_utils.ParseGypList(options.resource_zips)
217 for z in dep_zips:
218 subdir = os.path.join(temp_dir, os.path.basename(z))
219 if os.path.exists(subdir):
220 raise Exception('Resource zip name conflict: ' + os.path.basename(z))
221 build_utils.ExtractAll(z, path=subdir)
222 package_command += PackageArgsForExtractedZip(subdir)
223
224 if options.create_density_splits:
225 for config in DENSITY_SPLITS.itervalues():
226 package_command.extend(('--split', ','.join(config)))
James Robinson646469d2014-10-03 15:33:28 -0700227
228 if 'Debug' in options.configuration_name:
229 package_command += ['--debug-mode']
230
231 build_utils.CheckOutput(
232 package_command, print_stdout=False, print_stderr=False)
233
Viet-Trung Luu235cf3d2015-06-11 10:01:25 -0700234 if options.create_density_splits:
235 CheckDensityMissedConfigs(options.apk_path)
236 RenameDensitySplits(options.apk_path)
237
James Robinson646469d2014-10-03 15:33:28 -0700238 if options.depfile:
239 build_utils.WriteDepfile(
240 options.depfile,
241 build_utils.GetPythonDependencies())
242
243
244if __name__ == '__main__':
245 main()