1#!/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 19 20from util import build_utils 21 22def 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 build_utils.AddDepfileOption(parser) 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 34 parser.add_option('--configuration-name', 35 help='Gyp\'s configuration name (Debug or Release).') 36 37 parser.add_option('--android-manifest', help='AndroidManifest.xml path') 38 parser.add_option('--version-code', help='Version code for apk.') 39 parser.add_option('--version-name', help='Version name for apk.') 40 parser.add_option('--resource-zips', 41 help='zip files containing resources to be packaged') 42 parser.add_option('--asset-dir', 43 help='directories containing assets to be packaged') 44 parser.add_option('--no-compress', help='disables compression for the ' 45 'given comma separated list of extensions') 46 47 parser.add_option('--apk-path', 48 help='Path to output (partial) apk.') 49 50 (options, args) = parser.parse_args() 51 52 if args: 53 parser.error('No positional arguments should be given.') 54 55 # Check that required options have been provided. 56 required_options = ('android_sdk', 'android_sdk_tools', 'configuration_name', 57 'android_manifest', 'version_code', 'version_name', 58 'resource_zips', 'asset_dir', 'apk_path') 59 60 build_utils.CheckOptions(options, parser, required=required_options) 61 62 return options 63 64 65def MoveImagesToNonMdpiFolders(res_root): 66 """Move images from drawable-*-mdpi-* folders to drawable-* folders. 67 68 Why? http://crbug.com/289843 69 """ 70 for src_dir_name in os.listdir(res_root): 71 src_components = src_dir_name.split('-') 72 if src_components[0] != 'drawable' or 'mdpi' not in src_components: 73 continue 74 src_dir = os.path.join(res_root, src_dir_name) 75 if not os.path.isdir(src_dir): 76 continue 77 dst_components = [c for c in src_components if c != 'mdpi'] 78 assert dst_components != src_components 79 dst_dir_name = '-'.join(dst_components) 80 dst_dir = os.path.join(res_root, dst_dir_name) 81 build_utils.MakeDirectory(dst_dir) 82 for src_file_name in os.listdir(src_dir): 83 if not src_file_name.endswith('.png'): 84 continue 85 src_file = os.path.join(src_dir, src_file_name) 86 dst_file = os.path.join(dst_dir, src_file_name) 87 assert not os.path.lexists(dst_file) 88 shutil.move(src_file, dst_file) 89 90 91def PackageArgsForExtractedZip(d): 92 """Returns the aapt args for an extracted resources zip. 93 94 A resources zip either contains the resources for a single target or for 95 multiple targets. If it is multiple targets merged into one, the actual 96 resource directories will be contained in the subdirectories 0, 1, 2, ... 97 """ 98 res_dirs = [] 99 subdirs = [os.path.join(d, s) for s in os.listdir(d)] 100 subdirs = sorted([s for s in subdirs if os.path.isdir(s)]) 101 if subdirs and os.path.basename(subdirs[0]) == '0': 102 res_dirs = subdirs 103 else: 104 res_dirs = [d] 105 package_command = [] 106 for d in res_dirs: 107 MoveImagesToNonMdpiFolders(d) 108 package_command += ['-S', d] 109 return package_command 110 111 112def main(): 113 options = ParseArgs() 114 android_jar = os.path.join(options.android_sdk, 'android.jar') 115 aapt = os.path.join(options.android_sdk_tools, 'aapt') 116 117 with build_utils.TempDir() as temp_dir: 118 package_command = [aapt, 119 'package', 120 '--version-code', options.version_code, 121 '--version-name', options.version_name, 122 '-M', options.android_manifest, 123 '--no-crunch', 124 '-f', 125 '--auto-add-overlay', 126 127 '-I', android_jar, 128 '-F', options.apk_path, 129 ] 130 131 if options.no_compress: 132 for ext in options.no_compress.split(','): 133 package_command += ['-0', ext] 134 135 if os.path.exists(options.asset_dir): 136 package_command += ['-A', options.asset_dir] 137 138 dep_zips = build_utils.ParseGypList(options.resource_zips) 139 for z in dep_zips: 140 subdir = os.path.join(temp_dir, os.path.basename(z)) 141 if os.path.exists(subdir): 142 raise Exception('Resource zip name conflict: ' + os.path.basename(z)) 143 build_utils.ExtractAll(z, path=subdir) 144 package_command += PackageArgsForExtractedZip(subdir) 145 146 if 'Debug' in options.configuration_name: 147 package_command += ['--debug-mode'] 148 149 build_utils.CheckOutput( 150 package_command, print_stdout=False, print_stderr=False) 151 152 if options.depfile: 153 build_utils.WriteDepfile( 154 options.depfile, 155 build_utils.GetPythonDependencies()) 156 157 158if __name__ == '__main__': 159 main() 160