blob: 7b6d0c63631df60c2f617058b1f459b513c24826 [file] [log] [blame]
# Copyright 2018 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.
import json
import logging
import os
import re
import sys
import tempfile
import zipfile
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'gyp'))
from util import build_utils
from util import md5_check
import bundletool
_ALL_ABIS = ['armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64']
def _CreateMinimalDeviceSpec(bundle_path, sdk_version):
# Could also use "bundletool dump resources", but reading directly is faster.
if not sdk_version:
with zipfile.ZipFile(bundle_path) as f:
manifest_data = f.read('base/manifest/AndroidManifest.xml')
sdk_version = int(
re.search(r'minSdkVersion.*?(\d+)', manifest_data).group(1))
# Setting sdkVersion=minSdkVersion prevents multiple per-minSdkVersion .apk
# files from being created within the .apks file.
return {
'screenDensity': 1000, # Ignored since we don't split on density.
'sdkVersion': sdk_version,
'supportedAbis': _ALL_ABIS, # Our .aab files are already split on abi.
'supportedLocales': ['en'],
}
def GenerateBundleApks(bundle_path,
bundle_apks_path,
aapt2_path,
keystore_path,
keystore_password,
keystore_alias,
universal=False,
minimal=False,
minimal_sdk_version=None,
check_for_noop=True):
"""Generate an .apks archive from a an app bundle if needed.
Args:
bundle_path: Input bundle file path.
bundle_apks_path: Output bundle .apks archive path. Name must end with
'.apks' or this operation will fail.
aapt2_path: Path to aapt2 build tool.
keystore_path: Path to keystore.
keystore_password: Keystore password, as a string.
keystore_alias: Keystore signing key alias.
universal: Whether to create a single APK that contains the contents of all
modules.
minimal: Create the minimal set of apks possible (english-only).
minimal_sdk_version: When minimal=True, use this sdkVersion.
check_for_noop: Use md5_check to short-circuit when inputs have not changed.
"""
device_spec = None
if minimal:
device_spec = _CreateMinimalDeviceSpec(bundle_path, minimal_sdk_version)
def rebuild():
logging.info('Building %s', bundle_apks_path)
with tempfile.NamedTemporaryFile(suffix='.json') as spec_file, \
build_utils.AtomicOutput(bundle_apks_path, only_if_changed=False) as f:
cmd_args = [
'build-apks',
'--aapt2=%s' % aapt2_path,
'--output=%s' % f.name,
'--bundle=%s' % bundle_path,
'--ks=%s' % keystore_path,
'--ks-pass=pass:%s' % keystore_password,
'--ks-key-alias=%s' % keystore_alias,
'--overwrite',
]
if device_spec:
json.dump(device_spec, spec_file)
spec_file.flush()
cmd_args += ['--device-spec=' + spec_file.name]
if universal:
cmd_args += ['--mode=universal']
bundletool.RunBundleTool(cmd_args)
if check_for_noop:
# NOTE: BUNDLETOOL_JAR_PATH is added to input_strings, rather than
# input_paths, to speed up MD5 computations by about 400ms (the .jar file
# contains thousands of class files which are checked independently,
# resulting in an .md5.stamp of more than 60000 lines!).
input_paths = [bundle_path, aapt2_path, keystore_path]
input_strings = [
keystore_password,
keystore_alias,
bundletool.BUNDLETOOL_JAR_PATH,
# NOTE: BUNDLETOOL_VERSION is already part of BUNDLETOOL_JAR_PATH, but
# it's simpler to assume that this may not be the case in the future.
bundletool.BUNDLETOOL_VERSION,
device_spec,
]
md5_check.CallAndRecordIfStale(
rebuild,
input_paths=input_paths,
input_strings=input_strings,
output_paths=[bundle_apks_path])
else:
rebuild()