blob: f1be4ccf1a9c305f1a8c66e74e819861ffd5e644 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright 2021 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import argparse
import logging
import os
import sys
from util import build_utils
import action_helpers # build_utils adds //build to sys.path.
def _ParseArgs(args):
"""Parses command line options.
Returns:
An options object as from argparse.ArgumentParser.parse_args()
"""
parser = argparse.ArgumentParser()
parser.add_argument('--aapt2-path',
required=True,
help='Path to the Android aapt2 tool.')
parser.add_argument(
'--short-resource-paths',
action='store_true',
help='Whether to shorten resource paths inside the apk or module.')
parser.add_argument(
'--strip-resource-names',
action='store_true',
help='Whether to strip resource names from the resource table of the apk '
'or module.')
parser.add_argument('--proto-path',
required=True,
help='Input proto format resources APK.')
parser.add_argument('--resources-config-paths',
default='[]',
help='GN list of paths to aapt2 resources config files.')
parser.add_argument('--r-text-in',
required=True,
help='Path to R.txt. Used to exclude id/ resources.')
parser.add_argument(
'--resources-path-map-out-path',
help='Path to file produced by aapt2 that maps original resource paths '
'to shortened resource paths inside the apk or module.')
parser.add_argument('--optimized-proto-path',
required=True,
help='Output for `aapt2 optimize`.')
options = parser.parse_args(args)
options.resources_config_paths = action_helpers.parse_gn_list(
options.resources_config_paths)
if options.resources_path_map_out_path and not options.short_resource_paths:
parser.error(
'--resources-path-map-out-path requires --short-resource-paths')
return options
def _CombineResourceConfigs(resources_config_paths, out_config_path):
with open(out_config_path, 'w') as out_config:
for config_path in resources_config_paths:
with open(config_path) as config:
out_config.write(config.read())
out_config.write('\n')
def _ExtractNonCollapsableResources(rtxt_path):
"""Extract resources that should not be collapsed from the R.txt file
Resources of type ID are references to UI elements/views. They are used by
UI automation testing frameworks. They are kept in so that they don't break
tests, even though they may not actually be used during runtime. See
https://crbug.com/900993
App icons (aka mipmaps) are sometimes referenced by other apps by name so must
be keps as well. See https://b/161564466
Args:
rtxt_path: Path to R.txt file with all the resources
Returns:
List of resources in the form of <resource_type>/<resource_name>
"""
resources = []
_NO_COLLAPSE_TYPES = ['id', 'mipmap']
with open(rtxt_path) as rtxt:
for line in rtxt:
for resource_type in _NO_COLLAPSE_TYPES:
if ' {} '.format(resource_type) in line:
resource_name = line.split()[2]
resources.append('{}/{}'.format(resource_type, resource_name))
return resources
def _OptimizeApk(output, options, temp_dir, unoptimized_path, r_txt_path):
"""Optimize intermediate .ap_ file with aapt2.
Args:
output: Path to write to.
options: The command-line options.
temp_dir: A temporary directory.
unoptimized_path: path of the apk to optimize.
r_txt_path: path to the R.txt file of the unoptimized apk.
"""
optimize_command = [
options.aapt2_path,
'optimize',
unoptimized_path,
'-o',
output,
]
# Optimize the resources.pb file by obfuscating resource names and only
# allow usage via R.java constant.
if options.strip_resource_names:
no_collapse_resources = _ExtractNonCollapsableResources(r_txt_path)
gen_config_path = os.path.join(temp_dir, 'aapt2.config')
if options.resources_config_paths:
_CombineResourceConfigs(options.resources_config_paths, gen_config_path)
with open(gen_config_path, 'a') as config:
for resource in no_collapse_resources:
config.write('{}#no_collapse\n'.format(resource))
optimize_command += [
'--collapse-resource-names',
'--resources-config-path',
gen_config_path,
]
if options.short_resource_paths:
optimize_command += ['--shorten-resource-paths']
if options.resources_path_map_out_path:
optimize_command += [
'--resource-path-shortening-map', options.resources_path_map_out_path
]
logging.debug('Running aapt2 optimize')
build_utils.CheckOutput(optimize_command,
print_stdout=False,
print_stderr=False)
def main(args):
options = _ParseArgs(args)
with build_utils.TempDir() as temp_dir:
_OptimizeApk(options.optimized_proto_path, options, temp_dir,
options.proto_path, options.r_text_in)
if __name__ == '__main__':
main(sys.argv[1:])