Index: build/android/gyp/package_resources.py |
diff --git a/build/android/gyp/package_resources.py b/build/android/gyp/package_resources.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..d17d1fe2f95b98271098d1ce5174729961860257 |
--- /dev/null |
+++ b/build/android/gyp/package_resources.py |
@@ -0,0 +1,270 @@ |
+#!/usr/bin/env python |
+# |
+# Copyright 2014 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+# pylint: disable=C0301 |
+"""Package resources into an apk. |
+ |
+See https://android.googlesource.com/platform/tools/base/+/master/legacy/ant-tasks/src/main/java/com/android/ant/AaptExecTask.java |
+and |
+https://android.googlesource.com/platform/sdk/+/master/files/ant/build.xml |
+""" |
+# pylint: enable=C0301 |
+ |
+import optparse |
+import os |
+import re |
+import shutil |
+import zipfile |
+ |
+from util import build_utils |
+ |
+ |
+# List is generated from the chrome_apk.apk_intermediates.ap_ via: |
+# unzip -l $FILE_AP_ | cut -c31- | grep res/draw | cut -d'/' -f 2 | sort \ |
+# | uniq | grep -- -tvdpi- | cut -c10- |
+# and then manually sorted. |
+# Note that we can't just do a cross-product of dimentions because the filenames |
+# become too big and aapt fails to create the files. |
+# This leaves all default drawables (mdpi) in the main apk. Android gets upset |
+# though if any drawables are missing from the default drawables/ directory. |
+DENSITY_SPLITS = { |
+ 'hdpi': ( |
+ 'hdpi-v4', # Order matters for output file names. |
+ 'ldrtl-hdpi-v4', |
+ 'sw600dp-hdpi-v13', |
+ 'ldrtl-hdpi-v17', |
+ 'ldrtl-sw600dp-hdpi-v17', |
+ 'hdpi-v21', |
+ ), |
+ 'xhdpi': ( |
+ 'xhdpi-v4', |
+ 'ldrtl-xhdpi-v4', |
+ 'sw600dp-xhdpi-v13', |
+ 'ldrtl-xhdpi-v17', |
+ 'ldrtl-sw600dp-xhdpi-v17', |
+ 'xhdpi-v21', |
+ ), |
+ 'xxhdpi': ( |
+ 'xxhdpi-v4', |
+ 'ldrtl-xxhdpi-v4', |
+ 'sw600dp-xxhdpi-v13', |
+ 'ldrtl-xxhdpi-v17', |
+ 'ldrtl-sw600dp-xxhdpi-v17', |
+ 'xxhdpi-v21', |
+ ), |
+ 'xxxhdpi': ( |
+ 'xxxhdpi-v4', |
+ 'ldrtl-xxxhdpi-v4', |
+ 'sw600dp-xxxhdpi-v13', |
+ 'ldrtl-xxxhdpi-v17', |
+ 'ldrtl-sw600dp-xxxhdpi-v17', |
+ 'xxxhdpi-v21', |
+ ), |
+ 'tvdpi': ( |
+ 'tvdpi-v4', |
+ 'sw600dp-tvdpi-v13', |
+ 'ldrtl-sw600dp-tvdpi-v17', |
+ ), |
+} |
+ |
+ |
+def ParseArgs(): |
+ """Parses command line options. |
+ |
+ Returns: |
+ An options object as from optparse.OptionsParser.parse_args() |
+ """ |
+ parser = optparse.OptionParser() |
+ build_utils.AddDepfileOption(parser) |
+ parser.add_option('--android-sdk', help='path to the Android SDK folder') |
+ parser.add_option('--aapt-path', |
+ help='path to the Android aapt tool') |
+ |
+ parser.add_option('--configuration-name', |
+ help='Gyp\'s configuration name (Debug or Release).') |
+ |
+ parser.add_option('--android-manifest', help='AndroidManifest.xml path') |
+ parser.add_option('--version-code', help='Version code for apk.') |
+ parser.add_option('--version-name', help='Version name for apk.') |
+ parser.add_option( |
+ '--shared-resources', |
+ action='store_true', |
+ help='Make a resource package that can be loaded by a different' |
+ 'application at runtime to access the package\'s resources.') |
+ parser.add_option('--resource-zips', |
+ help='zip files containing resources to be packaged') |
+ parser.add_option('--asset-dir', |
+ help='directories containing assets to be packaged') |
+ parser.add_option('--no-compress', help='disables compression for the ' |
+ 'given comma separated list of extensions') |
+ parser.add_option( |
+ '--create-density-splits', |
+ action='store_true', |
+ help='Enables density splits') |
+ parser.add_option('--language-splits', |
+ help='GYP list of languages to create splits for') |
+ |
+ parser.add_option('--apk-path', |
+ help='Path to output (partial) apk.') |
+ |
+ (options, args) = parser.parse_args() |
+ |
+ if args: |
+ parser.error('No positional arguments should be given.') |
+ |
+ # Check that required options have been provided. |
+ required_options = ('android_sdk', 'aapt_path', 'configuration_name', |
+ 'android_manifest', 'version_code', 'version_name', |
+ 'apk_path') |
+ |
+ build_utils.CheckOptions(options, parser, required=required_options) |
+ |
+ return options |
+ |
+ |
+def MoveImagesToNonMdpiFolders(res_root): |
+ """Move images from drawable-*-mdpi-* folders to drawable-* folders. |
+ |
+ Why? http://crbug.com/289843 |
+ """ |
+ for src_dir_name in os.listdir(res_root): |
+ src_components = src_dir_name.split('-') |
+ if src_components[0] != 'drawable' or 'mdpi' not in src_components: |
+ continue |
+ src_dir = os.path.join(res_root, src_dir_name) |
+ if not os.path.isdir(src_dir): |
+ continue |
+ dst_components = [c for c in src_components if c != 'mdpi'] |
+ assert dst_components != src_components |
+ dst_dir_name = '-'.join(dst_components) |
+ dst_dir = os.path.join(res_root, dst_dir_name) |
+ build_utils.MakeDirectory(dst_dir) |
+ for src_file_name in os.listdir(src_dir): |
+ if not src_file_name.endswith('.png'): |
+ continue |
+ src_file = os.path.join(src_dir, src_file_name) |
+ dst_file = os.path.join(dst_dir, src_file_name) |
+ assert not os.path.lexists(dst_file) |
+ shutil.move(src_file, dst_file) |
+ |
+ |
+def PackageArgsForExtractedZip(d): |
+ """Returns the aapt args for an extracted resources zip. |
+ |
+ A resources zip either contains the resources for a single target or for |
+ multiple targets. If it is multiple targets merged into one, the actual |
+ resource directories will be contained in the subdirectories 0, 1, 2, ... |
+ """ |
+ subdirs = [os.path.join(d, s) for s in os.listdir(d)] |
+ subdirs = [s for s in subdirs if os.path.isdir(s)] |
+ is_multi = '0' in [os.path.basename(s) for s in subdirs] |
+ if is_multi: |
+ res_dirs = sorted(subdirs, key=lambda p : int(os.path.basename(p))) |
+ else: |
+ res_dirs = [d] |
+ package_command = [] |
+ for d in res_dirs: |
+ MoveImagesToNonMdpiFolders(d) |
+ package_command += ['-S', d] |
+ return package_command |
+ |
+ |
+def RenameDensitySplits(apk_path): |
+ """Renames all density splits to have shorter / predictable names.""" |
+ for density, config in DENSITY_SPLITS.iteritems(): |
+ src_path = '%s_%s' % (apk_path, '_'.join(config)) |
+ dst_path = '%s_%s' % (apk_path, density) |
+ if src_path != dst_path: |
+ if os.path.exists(dst_path): |
+ os.unlink(dst_path) |
+ os.rename(src_path, dst_path) |
+ |
+ |
+def CheckForMissedConfigs(apk_path, check_density, languages): |
+ """Raises an exception if apk_path contains any unexpected configs.""" |
+ triggers = [] |
+ if check_density: |
+ triggers.extend(re.compile('-%s' % density) for density in DENSITY_SPLITS) |
+ if languages: |
+ triggers.extend(re.compile(r'-%s\b' % lang) for lang in languages) |
+ with zipfile.ZipFile(apk_path) as main_apk_zip: |
+ for name in main_apk_zip.namelist(): |
+ for trigger in triggers: |
+ if trigger.search(name) and not 'mipmap-' in name: |
+ raise Exception(('Found config in main apk that should have been ' + |
+ 'put into a split: %s\nYou need to update ' + |
+ 'package_resources.py to include this new ' + |
+ 'config (trigger=%s)') % (name, trigger.pattern)) |
+ |
+ |
+def main(): |
+ options = ParseArgs() |
+ android_jar = os.path.join(options.android_sdk, 'android.jar') |
+ aapt = options.aapt_path |
+ |
+ with build_utils.TempDir() as temp_dir: |
+ package_command = [aapt, |
+ 'package', |
+ '--version-code', options.version_code, |
+ '--version-name', options.version_name, |
+ '-M', options.android_manifest, |
+ '--no-crunch', |
+ '-f', |
+ '--auto-add-overlay', |
+ '-I', android_jar, |
+ '-F', options.apk_path, |
+ '--ignore-assets', build_utils.AAPT_IGNORE_PATTERN, |
+ ] |
+ |
+ if options.no_compress: |
+ for ext in options.no_compress.split(','): |
+ package_command += ['-0', ext] |
+ if options.shared_resources: |
+ package_command.append('--shared-lib') |
+ |
+ if options.asset_dir and os.path.exists(options.asset_dir): |
+ package_command += ['-A', options.asset_dir] |
+ |
+ if options.resource_zips: |
+ dep_zips = build_utils.ParseGypList(options.resource_zips) |
+ for z in dep_zips: |
+ subdir = os.path.join(temp_dir, os.path.basename(z)) |
+ if os.path.exists(subdir): |
+ raise Exception('Resource zip name conflict: ' + os.path.basename(z)) |
+ build_utils.ExtractAll(z, path=subdir) |
+ package_command += PackageArgsForExtractedZip(subdir) |
+ |
+ if options.create_density_splits: |
+ for config in DENSITY_SPLITS.itervalues(): |
+ package_command.extend(('--split', ','.join(config))) |
+ |
+ language_splits = None |
+ if options.language_splits: |
+ language_splits = build_utils.ParseGypList(options.language_splits) |
+ for lang in language_splits: |
+ package_command.extend(('--split', lang)) |
+ |
+ if 'Debug' in options.configuration_name: |
+ package_command += ['--debug-mode'] |
+ |
+ build_utils.CheckOutput( |
+ package_command, print_stdout=False, print_stderr=False) |
+ |
+ if options.create_density_splits or language_splits: |
+ CheckForMissedConfigs( |
+ options.apk_path, options.create_density_splits, language_splits) |
+ |
+ if options.create_density_splits: |
+ RenameDensitySplits(options.apk_path) |
+ |
+ if options.depfile: |
+ build_utils.WriteDepfile( |
+ options.depfile, |
+ build_utils.GetPythonDependencies()) |
+ |
+ |
+if __name__ == '__main__': |
+ main() |