Home | History | Annotate | Download | only in gyp
      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 
     10 See https://android.googlesource.com/platform/tools/base/+/master/legacy/ant-tasks/src/main/java/com/android/ant/AaptExecTask.java
     11 and
     12 https://android.googlesource.com/platform/sdk/+/master/files/ant/build.xml
     13 """
     14 # pylint: enable=C0301
     15 
     16 import optparse
     17 import os
     18 import shutil
     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   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 
     65 def 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 
     91 def 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 
    112 def 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 
    158 if __name__ == '__main__':
    159   main()
    160