0

Revert "[Android] Enable unused resources removal on android bundle targets"

This reverts commit 3f66b9ee6f.

Reason for revert: breaks android-builder-perf, see crbug/1216318

Original change's description:
> [Android] Enable unused resources removal on android bundle targets
>
> Bug: 636448
> Change-Id: I4bb156503f568b4e81924e7f62b2d6748ce34048
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2907078
> Auto-Submit: Mohamed Heikal <mheikal@chromium.org>
> Commit-Queue: Mohamed Heikal <mheikal@chromium.org>
> Reviewed-by: Andrew Grieve <agrieve@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#888944}

Bug: 636448
Bug: 1216318
Change-Id: Ia96b494e9872a37371e0cfaf8ff97fe6c4601c84
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2940245
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Owners-Override: Peter Conn <peconn@chromium.org>
Commit-Queue: Eric Seckler <eseckler@chromium.org>
Cr-Commit-Position: refs/heads/master@{#889185}
This commit is contained in:
Eric Seckler
2021-06-04 08:38:34 +00:00
committed by Chromium LUCI CQ
parent 1da162b5b5
commit 0f10f39d0a
16 changed files with 362 additions and 463 deletions

@ -1142,8 +1142,8 @@ _GENERIC_PYDEPS_FILES = [
'build/android/gyp/prepare_resources.pydeps',
'build/android/gyp/process_native_prebuilt.pydeps',
'build/android/gyp/proguard.pydeps',
'build/android/gyp/resources_shrinker/shrinker.pydeps',
'build/android/gyp/turbine.pydeps',
'build/android/gyp/unused_resources.pydeps',
'build/android/gyp/validate_static_library_dex_references.pydeps',
'build/android/gyp/write_build_config.pydeps',
'build/android/gyp/write_native_libraries_java.pydeps',

@ -177,10 +177,29 @@ def _ParseArgs(args):
'--no-xml-namespaces',
action='store_true',
help='Whether to strip xml namespaces from processed xml resources.')
input_opts.add_argument(
'--short-resource-paths',
action='store_true',
help='Whether to shorten resource paths inside the apk or module.')
input_opts.add_argument(
'--strip-resource-names',
action='store_true',
help='Whether to strip resource names from the resource table of the apk '
'or module.')
output_opts.add_argument('--arsc-path', help='Apk output for arsc format.')
output_opts.add_argument('--proto-path', help='Apk output for proto format.')
group = input_opts.add_mutually_exclusive_group()
group.add_argument(
'--optimized-arsc-path',
help='Output for `aapt2 optimize` for arsc format (enables the step).')
group.add_argument(
'--optimized-proto-path',
help='Output for `aapt2 optimize` for proto format (enables the step).')
input_opts.add_argument(
'--resources-config-paths',
default='[]',
help='GN list of paths to aapt2 resources config files.')
output_opts.add_argument(
'--info-path', help='Path to output info file for the partial apk.')
@ -203,6 +222,11 @@ def _ParseArgs(args):
output_opts.add_argument(
'--emit-ids-out', help='Path to file produced by aapt2 --emit-ids.')
output_opts.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.')
input_opts.add_argument(
'--is-bundle-module',
action='store_true',
@ -233,10 +257,20 @@ def _ParseArgs(args):
options.values_filter_rules)
options.extra_main_r_text_files = build_utils.ParseGnList(
options.extra_main_r_text_files)
options.resources_config_paths = build_utils.ParseGnList(
options.resources_config_paths)
if options.optimized_proto_path and not options.proto_path:
# We could write to a temp file, but it's simpler to require it.
parser.error('--optimized-proto-path requires --proto-path')
if not options.arsc_path and not options.proto_path:
parser.error('One of --arsc-path or --proto-path is required.')
if options.resources_path_map_out_path and not options.short_resource_paths:
parser.error(
'--resources-path-map-out-path requires --short-resource-paths')
if options.package_id and options.shared_resources:
parser.error('--package-id and --shared-resources are mutually exclusive')
@ -812,6 +846,9 @@ def _PackageApk(options, build):
# We always create a binary arsc file first, then convert to proto, so flags
# such as --shared-lib can be supported.
arsc_path = build.arsc_path
if arsc_path is None:
_, arsc_path = tempfile.mkstmp()
link_command += ['-o', build.arsc_path]
logging.debug('Starting: aapt2 link')
@ -859,9 +896,100 @@ def _PackageApk(options, build):
build.arsc_path, build.proto_path
])
if build.arsc_path is None:
os.remove(arsc_path)
if options.optimized_proto_path:
_OptimizeApk(build.optimized_proto_path, options, build.temp_dir,
build.proto_path, build.r_txt_path)
elif options.optimized_arsc_path:
_OptimizeApk(build.optimized_arsc_path, options, build.temp_dir,
build.arsc_path, build.r_txt_path)
return desired_manifest_package_name
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 _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.arsc 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 _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
@contextlib.contextmanager
def _CreateStableIdsFile(in_path, out_path, package_name):
"""Transforms a file generated by --emit-ids from another package.
@ -890,6 +1018,8 @@ def _WriteOutputs(options, build):
(options.r_text_out, build.r_txt_path),
(options.arsc_path, build.arsc_path),
(options.proto_path, build.proto_path),
(options.optimized_arsc_path, build.optimized_arsc_path),
(options.optimized_proto_path, build.optimized_proto_path),
(options.proguard_file, build.proguard_path),
(options.proguard_file_main_dex, build.proguard_main_dex_path),
(options.emit_ids_out, build.emit_ids_path),

@ -1,151 +0,0 @@
#!/usr/bin/env python3
#
# Copyright 2021 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 argparse
import logging
import os
import sys
from util import build_utils
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 = build_utils.ParseGnList(
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:])

@ -1,12 +1,8 @@
# Copyright 2021 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("//build/config/android/rules.gni")
java_binary("unused_resources") {
sources = [ "//build/android/unused_resources/UnusedResources.java" ]
main_class = "build.android.unused_resources.UnusedResources"
java_binary("resources_shrinker") {
sources = [ "//build/android/gyp/resources_shrinker/Shrinker.java" ]
main_class = "build.android.gyp.resources_shrinker.Shrinker"
deps = [
"//third_party/android_deps:com_android_tools_common_java",
"//third_party/android_deps:com_android_tools_layoutlib_layoutlib_api_java",
@ -15,5 +11,5 @@ java_binary("unused_resources") {
"//third_party/android_deps:org_jetbrains_kotlin_kotlin_stdlib_java",
"//third_party/r8:r8_java",
]
wrapper_script_name = "helper/unused_resources"
wrapper_script_name = "helper/resources_shrinker"
}

@ -19,7 +19,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package build.android.unused_resources;
package build.android.gyp.resources_shrinker;
import static com.android.ide.common.symbols.SymbolIo.readFromAapt;
import static com.android.utils.SdkUtils.endsWithIgnoreCase;
@ -44,7 +44,6 @@ import com.google.common.base.Joiner;
import com.google.common.collect.Maps;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closeables;
import com.google.common.io.Files;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
@ -55,6 +54,7 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
@ -77,7 +77,7 @@ import javax.xml.parsers.ParserConfigurationException;
- Reduce dependencies unless absolutely required.
*/
public class UnusedResources {
public class Shrinker {
private static final String ANDROID_RES = "android_res/";
private static final String DOT_DEX = ".dex";
private static final String DOT_CLASS = ".class";
@ -97,6 +97,9 @@ public class UnusedResources {
private final StringWriter mDebugOutput;
private final PrintWriter mDebugPrinter;
/** Easy way to invoke more verbose output for debugging */
private boolean mDebug = false;
/** The computed set of unused resources */
private List<Resource> mUnused;
@ -133,8 +136,8 @@ public class UnusedResources {
}
}
public UnusedResources(Iterable<File> rTxtFiles, Iterable<File> classes,
Iterable<File> manifests, File mapping, Iterable<File> resources, File reportFile) {
public Shrinker(Iterable<File> rTxtFiles, Iterable<File> classes, Iterable<File> manifests,
File mapping, Iterable<File> resources, File reportFile) {
mRTxtFiles = rTxtFiles;
mProguardMapping = mapping;
mClasses = classes;
@ -210,11 +213,13 @@ public class UnusedResources {
throws IOException, SAXException, ParserConfigurationException {
for (File resDir : resources) {
File[] resourceFolders = resDir.listFiles();
assert resourceFolders != null : "Invalid resource directory " + resDir;
for (File folder : resourceFolders) {
ResourceFolderType folderType = ResourceFolderType.getFolderType(folder.getName());
if (folderType != null) {
recordResources(folderType, folder);
if (resourceFolders != null) {
for (File folder : resourceFolders) {
ResourceFolderType folderType =
ResourceFolderType.getFolderType(folder.getName());
if (folderType != null) {
recordResources(folderType, folder);
}
}
}
}
@ -387,7 +392,7 @@ public class UnusedResources {
@Override
public void referencedInt(int value) {
UnusedResources.this.referencedInt("dex", value, file, name);
Shrinker.this.referencedInt("dex", value, file, name);
}
@Override
@ -497,7 +502,8 @@ public class UnusedResources {
private void referencedInt(String context, int value, File file, String currentClass) {
Resource resource = mModel.getResource(value);
if (ResourceUsageModel.markReachable(resource) && mDebugPrinter != null) {
if (ResourceUsageModel.markReachable(resource) && mDebug) {
assert mDebugPrinter != null : "mDebug is true, but mDebugPrinter is null.";
mDebugPrinter.println("Marking " + resource + " reachable: referenced from " + context
+ " in " + file + ":" + currentClass);
}
@ -557,7 +563,7 @@ public class UnusedResources {
.map(s -> new File(s))
.collect(Collectors.toList());
break;
case "--dexes":
case "--dex":
classes = Arrays.stream(args[i + 1].split(":"))
.map(s -> new File(s))
.collect(Collectors.toList());
@ -585,10 +591,9 @@ public class UnusedResources {
throw new IllegalArgumentException(args[i] + " is not a valid arg.");
}
}
UnusedResources unusedResources =
new UnusedResources(rTxtFiles, classes, manifests, mapping, resources, log);
unusedResources.analyze();
unusedResources.close();
unusedResources.emitConfig(configPath);
Shrinker shrinker = new Shrinker(rTxtFiles, classes, manifests, mapping, resources, log);
shrinker.analyze();
shrinker.close();
shrinker.emitConfig(configPath);
}
}

@ -24,21 +24,16 @@ def main(args):
parser.add_argument(
'--dependencies-res-zips',
required=True,
action='append',
help='Resources zip archives to investigate for unused resources.')
parser.add_argument('--dexes',
action='append',
parser.add_argument('--dex',
required=True,
help='Path to dex file, or zip with dex files.')
parser.add_argument(
'--proguard-mapping',
required=True,
help='Path to proguard mapping file for the optimized dex.')
parser.add_argument('--r-texts',
action='append',
required=True,
help='Path to R.txt')
parser.add_argument('--android-manifests',
action='append',
parser.add_argument('--r-text', required=True, help='Path to R.txt')
parser.add_argument('--android-manifest',
required=True,
help='Path to AndroidManifest')
parser.add_argument('--output-config',
@ -59,31 +54,20 @@ def main(args):
for dependency_res_zip in options.dependencies_res_zips:
dep_subdirs += resource_utils.ExtractDeps([dependency_res_zip], temp_dir)
cmd = [
options.script,
'--rtxts',
':'.join(options.r_texts),
'--manifests',
':'.join(options.android_manifests),
'--resourceDirs',
':'.join(dep_subdirs),
'--dexes',
':'.join(options.dexes),
'--outputConfig',
options.output_config,
]
if options.proguard_mapping:
cmd += [
'--mapping',
options.proguard_mapping,
]
build_utils.CheckOutput(cmd)
build_utils.CheckOutput([
options.script, '--rtxts', options.r_text, '--manifests',
options.android_manifest, '--resourceDirs', ':'.join(dep_subdirs),
'--dex', options.dex, '--mapping', options.proguard_mapping,
'--outputConfig', options.output_config
])
if options.depfile:
depfile_deps = (options.dependencies_res_zips + options.r_texts +
options.android_manifests + options.dexes)
if options.proguard_mapping:
depfile_deps.append(options.proguard_mapping)
depfile_deps = options.dependencies_res_zips + [
options.r_text,
options.android_manifest,
options.dex,
options.proguard_mapping,
]
build_utils.WriteDepfile(options.depfile, options.output_config,
depfile_deps)

@ -0,0 +1,30 @@
# Generated by running:
# build/print_python_deps.py --root build/android/gyp/resources_shrinker --output build/android/gyp/resources_shrinker/shrinker.pydeps build/android/gyp/resources_shrinker/shrinker.py
../../../../third_party/jinja2/__init__.py
../../../../third_party/jinja2/_compat.py
../../../../third_party/jinja2/asyncfilters.py
../../../../third_party/jinja2/asyncsupport.py
../../../../third_party/jinja2/bccache.py
../../../../third_party/jinja2/compiler.py
../../../../third_party/jinja2/defaults.py
../../../../third_party/jinja2/environment.py
../../../../third_party/jinja2/exceptions.py
../../../../third_party/jinja2/filters.py
../../../../third_party/jinja2/idtracking.py
../../../../third_party/jinja2/lexer.py
../../../../third_party/jinja2/loaders.py
../../../../third_party/jinja2/nodes.py
../../../../third_party/jinja2/optimizer.py
../../../../third_party/jinja2/parser.py
../../../../third_party/jinja2/runtime.py
../../../../third_party/jinja2/tests.py
../../../../third_party/jinja2/utils.py
../../../../third_party/jinja2/visitor.py
../../../../third_party/markupsafe/__init__.py
../../../../third_party/markupsafe/_compat.py
../../../../third_party/markupsafe/_native.py
../../../gn_helpers.py
../util/__init__.py
../util/build_utils.py
../util/resource_utils.py
shrinker.py

@ -1,30 +0,0 @@
# Generated by running:
# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/unused_resources.pydeps build/android/gyp/unused_resources.py
../../../third_party/jinja2/__init__.py
../../../third_party/jinja2/_compat.py
../../../third_party/jinja2/asyncfilters.py
../../../third_party/jinja2/asyncsupport.py
../../../third_party/jinja2/bccache.py
../../../third_party/jinja2/compiler.py
../../../third_party/jinja2/defaults.py
../../../third_party/jinja2/environment.py
../../../third_party/jinja2/exceptions.py
../../../third_party/jinja2/filters.py
../../../third_party/jinja2/idtracking.py
../../../third_party/jinja2/lexer.py
../../../third_party/jinja2/loaders.py
../../../third_party/jinja2/nodes.py
../../../third_party/jinja2/optimizer.py
../../../third_party/jinja2/parser.py
../../../third_party/jinja2/runtime.py
../../../third_party/jinja2/tests.py
../../../third_party/jinja2/utils.py
../../../third_party/jinja2/visitor.py
../../../third_party/markupsafe/__init__.py
../../../third_party/markupsafe/_compat.py
../../../third_party/markupsafe/_native.py
../../gn_helpers.py
unused_resources.py
util/__init__.py
util/build_utils.py
util/resource_utils.py

@ -966,10 +966,7 @@ def main(argv):
parser.add_option('--resources-zip', help='Path to target\'s resources zip.')
parser.add_option('--package-name',
help='Java package name for these resources.')
parser.add_option('--android-manifest',
help='Path to the root android manifest.')
parser.add_option('--merged-android-manifest',
help='Path to the merged android manifest.')
parser.add_option('--android-manifest', help='Path to android manifest.')
parser.add_option('--resource-dirs', action='append', default=[],
help='GYP-list of resource dirs')
parser.add_option(
@ -1350,9 +1347,6 @@ def main(argv):
if options.android_manifest:
deps_info['android_manifest'] = options.android_manifest
if options.merged_android_manifest:
deps_info['merged_android_manifest'] = options.merged_android_manifest
if options.bundled_srcjars:
deps_info['bundled_srcjars'] = build_utils.ParseGnList(
options.bundled_srcjars)

@ -360,12 +360,6 @@ template("write_build_config") {
args += [ "--treat-as-locale-paks" ]
}
if (defined(invoker.merged_android_manifest)) {
args += [
"--merged-android-manifest",
rebase_path(invoker.merged_android_manifest, root_build_dir),
]
}
if (defined(invoker.android_manifest)) {
inputs += [ invoker.android_manifest ]
args += [
@ -2255,15 +2249,29 @@ if (enable_java_templates) {
# Use resource IDs provided by another APK target when compiling resources
# (via. "aapt2 link --stable-ids")
#
# short_resource_paths: (optional)
# Rename the paths within a the apk to be randomly generated short
# strings to reduce binary size.
#
# strip_resource_names: (optional)
# Strip resource names from the resources table of the apk.
#
# Output variables:
# arsc_output: Path to output .ap_ file (optional).
#
# proto_output: Path to output .proto.ap_ file (optional).
#
# optimized_arsc_output: Path to optimized .ap_ file (optional).
#
# optimized_proto_output: Path to optimized .proto.ap_ file (optional).
#
# r_text_out_path: (optional):
# Path for the corresponding generated R.txt file.
#
# resources_path_map_out_path: (optional):
# Path for the generated map between original resource paths and
# shortend resource paths.
#
# proguard_file: (optional)
# Path to proguard configuration file for this apk target.
#
@ -2293,6 +2301,9 @@ if (enable_java_templates) {
if (defined(invoker.arsc_output)) {
_arsc_output = invoker.arsc_output
}
if (defined(invoker.optimized_arsc_output)) {
_optimized_arsc_output = invoker.optimized_arsc_output
}
_final_srcjar_path = "${target_gen_dir}/${target_name}.srcjar"
_script = "//build/android/gyp/compile_resources.py"
@ -2360,6 +2371,36 @@ if (enable_java_templates) {
rebase_path(invoker.size_info_path, root_build_dir),
]
}
if (defined(_optimized_arsc_output)) {
_outputs += [ _optimized_arsc_output ]
_args += [
"--optimized-arsc-path",
rebase_path(_optimized_arsc_output, root_build_dir),
]
}
if (defined(invoker.optimized_proto_output)) {
_outputs += [ invoker.optimized_proto_output ]
_args += [
"--optimized-proto-path",
rebase_path(invoker.optimized_proto_output, root_build_dir),
]
}
if (defined(invoker.resources_config_paths)) {
_inputs += invoker.resources_config_paths
_rebased_resource_configs =
rebase_path(invoker.resources_config_paths, root_build_dir)
_args += [ "--resources-config-paths=${_rebased_resource_configs}" ]
}
if (defined(invoker.short_resource_paths) && invoker.short_resource_paths) {
_args += [ "--short-resource-paths" ]
if (defined(invoker.resources_path_map_out_path)) {
_outputs += [ invoker.resources_path_map_out_path ]
_args += [
"--resources-path-map-out-path",
rebase_path(invoker.resources_path_map_out_path, root_build_dir),
]
}
}
if (defined(invoker.r_java_root_package_name)) {
_args += [
@ -2368,6 +2409,10 @@ if (enable_java_templates) {
]
}
if (defined(invoker.strip_resource_names) && invoker.strip_resource_names) {
_args += [ "--strip-resource-names" ]
}
# Useful to have android:debuggable in the manifest even for Release
# builds. Just omit it for officai
if (debuggable_apks) {
@ -2594,115 +2639,35 @@ if (enable_java_templates) {
}
}
# A template that is used to optimize compiled resources using aapt2 optimize.
#
# proto_input_path:
# Path to input compiled .proto.ap_ file.
#
# short_resource_paths: (optional)
# Rename the paths within a the apk to be randomly generated short
# strings to reduce binary size.
#
# strip_resource_names: (optional)
# Strip resource names from the resources table of the apk.
#
# resources_configs_paths: (optional)
# List of resource configs to use for optimization.
#
# optimized_proto_output:
# Path to output optimized .proto.ap_ file.
#
# resources_path_map_out_path: (optional):
# Path for the generated map between original resource paths and
# shortened resource paths.
template("optimize_resources") {
forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
action_with_pydeps(target_name) {
forward_variables_from(invoker, [ "deps" ])
script = "//build/android/gyp/optimize_resources.py"
outputs = [ invoker.optimized_proto_output ]
inputs = [
android_sdk_tools_bundle_aapt2,
invoker.r_text_path,
invoker.proto_input_path,
]
args = [
"--aapt2-path",
rebase_path(android_sdk_tools_bundle_aapt2, root_build_dir),
"--r-text-in",
rebase_path(invoker.r_text_path, root_build_dir),
"--proto-path",
rebase_path(invoker.proto_input_path, root_build_dir),
"--optimized-proto-path",
rebase_path(invoker.optimized_proto_output, root_build_dir),
]
if (defined(invoker.resources_config_paths)) {
inputs += invoker.resources_config_paths
_rebased_resource_configs =
rebase_path(invoker.resources_config_paths, root_build_dir)
args += [ "--resources-config-paths=${_rebased_resource_configs}" ]
}
if (defined(invoker.short_resource_paths) &&
invoker.short_resource_paths) {
args += [ "--short-resource-paths" ]
if (defined(invoker.resources_path_map_out_path)) {
outputs += [ invoker.resources_path_map_out_path ]
args += [
"--resources-path-map-out-path",
rebase_path(invoker.resources_path_map_out_path, root_build_dir),
]
}
}
if (defined(invoker.strip_resource_names) &&
invoker.strip_resource_names) {
args += [ "--strip-resource-names" ]
}
}
}
# A template that is used to find unused resources.
template("unused_resources") {
_rebased_build_config = rebase_path(invoker.build_config, root_build_dir)
_shrinker_dep = "//build/android/gyp/resources_shrinker:resources_shrinker"
_shrinker_script = "$root_build_dir/bin/helper/resources_shrinker"
action_with_pydeps(target_name) {
forward_variables_from(invoker, TESTONLY_AND_VISIBILITY + [ "deps" ])
script = "//build/android/gyp/unused_resources.py"
depfile = "$target_gen_dir/${target_name}.d"
_unused_resources_script = "$root_build_dir/bin/helper/unused_resources"
inputs = [ _unused_resources_script ]
script = "//build/android/gyp/resources_shrinker/shrinker.py"
inputs = [
invoker.build_config,
invoker.proguard_mapping_path,
_shrinker_script,
]
outputs = [ invoker.output_config ]
if (!defined(deps)) {
deps = []
}
deps += [ "//build/android/unused_resources:unused_resources" ]
deps += [ _shrinker_dep ]
args = [
"--script",
rebase_path(_unused_resources_script, root_build_dir),
rebase_path(_shrinker_script, root_build_dir),
"--dependencies-res-zips=@FileArg($_rebased_build_config:deps_info:dependency_zips)",
"--proguard-mapping",
rebase_path(invoker.proguard_mapping_path, root_build_dir),
"--r-text=@FileArg($_rebased_build_config:deps_info:r_text_path)",
"--dex=@FileArg($_rebased_build_config:final_dex:path)",
"--android-manifest=@FileArg($_rebased_build_config:deps_info:android_manifest)",
"--output-config",
rebase_path(invoker.output_config, root_build_dir),
"--depfile",
rebase_path(depfile, root_build_dir),
]
if (defined(invoker.proguard_mapping_path)) {
inputs += [ invoker.proguard_mapping_path ]
args += [
"--proguard-mapping",
rebase_path(invoker.proguard_mapping_path, root_build_dir),
]
}
foreach(_build_config, invoker.module_build_configs) {
inputs += [ _build_config ]
_rebased_build_config = rebase_path(_build_config, root_build_dir)
args += [
"--dependencies-res-zips=@FileArg($_rebased_build_config:deps_info:dependency_zips)",
"--r-texts=@FileArg($_rebased_build_config:deps_info:r_text_path)",
"--dexes=@FileArg($_rebased_build_config:final_dex:path)",
"--android-manifests=@FileArg($_rebased_build_config:deps_info:merged_android_manifest)",
]
}
}
}
@ -3657,7 +3622,6 @@ if (enable_java_templates) {
[
"android_manifest",
"android_manifest_dep",
"merged_android_manifest",
"final_dex_path",
"loadable_modules",
"native_lib_placeholders",

@ -2077,8 +2077,6 @@ if (enable_java_templates) {
# uncompress_dex: Store final .dex files uncompressed in the apk.
# strip_resource_names: True if resource names should be stripped from the
# resources.arsc file in the apk or module.
# strip_unused_resources: True if unused resources should be stripped from
# the apk or module.
# short_resource_paths: True if resource paths should be shortened in the
# apk or module.
# resources_config_paths: List of paths to the aapt2 optimize config files
@ -2165,8 +2163,20 @@ if (enable_java_templates) {
if (!_is_bundle_module) {
_final_apk_path = invoker.final_apk_path
_final_rtxt_path = "${_final_apk_path}.R.txt"
}
_short_resource_paths =
defined(invoker.short_resource_paths) && invoker.short_resource_paths &&
enable_arsc_obfuscation
_strip_resource_names =
defined(invoker.strip_resource_names) && invoker.strip_resource_names &&
enable_arsc_obfuscation
_optimize_resources = _strip_resource_names || _short_resource_paths
if (!_is_bundle_module && _short_resource_paths) {
_final_pathmap_path = "${_final_apk_path}.pathmap.txt"
}
_res_size_info_path = "$target_out_dir/$target_name.ap_.info"
if (!_is_bundle_module) {
_final_apk_path_no_ext_list =
@ -2183,12 +2193,20 @@ if (enable_java_templates) {
if (_is_bundle_module) {
# Path to the intermediate proto-format resources zip file.
_proto_resources_path = "$target_out_dir/$target_name.proto.ap_"
if (_optimize_resources) {
_optimized_proto_resources_path =
"$target_out_dir/$target_name.optimized.proto.ap_"
}
} else {
# resource_sizes.py needs to be able to find the unpacked resources.arsc
# file based on apk name to compute normatlized size.
_resource_sizes_arsc_path =
"$root_out_dir/arsc/" +
rebase_path(_final_apk_path_no_ext, root_build_dir) + ".ap_"
if (_optimize_resources) {
_optimized_arsc_resources_path =
"$target_out_dir/$target_name.optimized.ap_"
}
}
if (defined(invoker.version_code)) {
@ -2431,6 +2449,11 @@ if (enable_java_templates) {
"${_shared_resources_allowlist_target}__compile_resources"
}
if (_short_resource_paths) {
_resources_path_map_out_path =
"${target_gen_dir}/${_template_name}_resources_path_map.txt"
}
_compile_resources_target = "${_template_name}__compile_resources"
_compile_resources_rtxt_out =
"${target_gen_dir}/${_compile_resources_target}_R.txt"
@ -2456,10 +2479,13 @@ if (enable_java_templates) {
"resource_exclusion_exceptions",
"resource_exclusion_regex",
"resource_values_filter_rules",
"resources_config_paths",
"shared_resources",
"shared_resources_allowlist_locales",
"uses_split",
])
short_resource_paths = _short_resource_paths
strip_resource_names = _strip_resource_names
android_manifest = _android_manifest
android_manifest_dep = ":$_merge_manifest_target"
version_code = _version_code
@ -2485,6 +2511,9 @@ if (enable_java_templates) {
if (_enable_main_dex_list) {
proguard_file_main_dex = _generated_proguard_main_dex_config
}
if (_short_resource_paths) {
resources_path_map_out_path = _resources_path_map_out_path
}
build_config = _build_config
build_config_dep = ":$_build_config_target"
@ -2521,6 +2550,9 @@ if (enable_java_templates) {
if (_is_bundle_module) {
is_bundle_module = true
proto_output = _proto_resources_path
if (_optimize_resources) {
optimized_proto_output = _optimized_proto_resources_path
}
if (defined(invoker.base_module_target)) {
include_resource =
@ -2528,6 +2560,8 @@ if (enable_java_templates) {
"/" + get_label_info(invoker.base_module_target, "name") + ".ap_"
_link_against = invoker.base_module_target
}
} else if (_optimize_resources) {
optimized_arsc_output = _optimized_arsc_resources_path
}
if (defined(_link_against)) {
@ -2550,55 +2584,6 @@ if (enable_java_templates) {
}
_srcjar_deps += [ ":$_compile_resources_target" ]
# We don't ship apks anymore, only optimize bundle builds
if (_is_bundle_module) {
_short_resource_paths =
defined(invoker.short_resource_paths) &&
invoker.short_resource_paths && enable_arsc_obfuscation
_strip_resource_names =
defined(invoker.strip_resource_names) &&
invoker.strip_resource_names && enable_arsc_obfuscation
_strip_unused_resources = defined(invoker.strip_unused_resources) &&
invoker.strip_unused_resources
_optimize_resources = _strip_resource_names || _short_resource_paths ||
_strip_unused_resources
}
if (_is_bundle_module && _optimize_resources) {
_optimized_proto_resources_path =
"$target_out_dir/$target_name.optimized.proto.ap_"
if (_short_resource_paths) {
_resources_path_map_out_path =
"${target_gen_dir}/${_template_name}_resources_path_map.txt"
}
_optimize_resources_target = "${_template_name}__optimize_resources"
optimize_resources(_optimize_resources_target) {
deps = _deps + [ ":$_compile_resources_target" ]
short_resource_paths = _short_resource_paths
strip_resource_names = _strip_resource_names
if (_short_resource_paths) {
resources_path_map_out_path = _resources_path_map_out_path
}
r_text_path = _compile_resources_rtxt_out
proto_input_path = _proto_resources_path
optimized_proto_output = _optimized_proto_resources_path
if (_strip_unused_resources) {
# These need to be kept in sync with the target names + output paths
# in the android_app_bundle template.
_unused_resources_target = "${_template_name}__unused_resources"
_unused_resources_config_path =
"$target_gen_dir/${_template_name}_unused_resources.config"
resources_config_paths = [ _unused_resources_config_path ]
deps += [ ":$_unused_resources_target" ]
} else {
resources_config_paths = []
}
if (defined(invoker.resources_config_paths)) {
resources_config_paths += invoker.resources_config_paths
}
}
}
if (defined(_resource_sizes_arsc_path)) {
_copy_arsc_target = "${_template_name}__copy_arsc"
copy(_copy_arsc_target) {
@ -2611,6 +2596,36 @@ if (enable_java_templates) {
_final_deps += [ ":$_copy_arsc_target" ]
}
if (!_is_bundle_module) {
# Output the R.txt file to a more easily discoverable location for
# archiving. This is necessary when stripping resource names so that we
# have an archive of resource names to ids for shipped apks (for
# debugging purposes). We copy the file rather than change the location
# of the original because other targets rely on the location of the R.txt
# file.
_copy_rtxt_target = "${_template_name}__copy_rtxt"
copy(_copy_rtxt_target) {
deps = [ ":$_compile_resources_target" ]
sources = [ _compile_resources_rtxt_out ]
outputs = [ _final_rtxt_path ]
}
_final_deps += [ ":$_copy_rtxt_target" ]
if (_short_resource_paths) {
# Do the same for path map
_copy_pathmap_target = "${_template_name}__copy_pathmap"
copy(_copy_pathmap_target) {
deps = [ ":$_compile_resources_target" ]
sources = [ _resources_path_map_out_path ]
outputs = [ _final_pathmap_path ]
# The monochrome_public_apk_checker test needs pathmap when run on swarming.
data = [ _final_pathmap_path ]
}
_final_deps += [ ":$_copy_pathmap_target" ]
}
}
_generate_native_libraries_java =
(!_is_bundle_module || _is_base_module) &&
(_native_libs_deps != [] || _secondary_abi_native_libs_deps != []) &&
@ -2773,7 +2788,6 @@ if (enable_java_templates) {
supports_android = true
requires_android = true
srcjar_deps = _srcjar_deps
merged_android_manifest = _android_manifest
if (defined(_final_dex_path)) {
final_dex_path = _final_dex_path
}
@ -3041,9 +3055,6 @@ if (enable_java_templates) {
":$_build_config_target",
":$_compile_resources_target",
] + _all_native_libs_deps
if (_optimize_resources) {
_final_deps += [ ":$_optimize_resources_target" ]
}
if (defined(_final_dex_target_dep)) {
not_needed([ "_final_dex_target_dep" ])
}
@ -3187,7 +3198,11 @@ if (enable_java_templates) {
deps += [ _final_dex_target_dep ]
}
packaged_resources_path = _arsc_resources_path
if (_optimize_resources) {
packaged_resources_path = _optimized_arsc_resources_path
} else {
packaged_resources_path = _arsc_resources_path
}
if (defined(_native_libs_filearg)) {
native_libs_filearg = _native_libs_filearg
@ -3466,6 +3481,7 @@ if (enable_java_templates) {
"resource_exclusion_regex",
"resource_ids_provider_dep",
"resource_values_filter_rules",
"resources_config_paths",
"require_native_mocks",
"secondary_abi_loadable_modules",
"secondary_abi_shared_libraries",
@ -3474,11 +3490,13 @@ if (enable_java_templates) {
"shared_resources",
"shared_resources_allowlist_locales",
"shared_resources_allowlist_target",
"short_resource_paths",
"sources",
"srcjar_deps",
"static_library_dependent_targets",
"static_library_provider",
"static_library_synchronized_proguard",
"strip_resource_names",
"target_sdk_version",
"testonly",
"uncompress_dex",
@ -3606,7 +3624,6 @@ if (enable_java_templates) {
"static_library_provider",
"static_library_synchronized_proguard",
"strip_resource_names",
"strip_unused_resources",
"target_sdk_version",
"testonly",
"uncompress_shared_libraries",
@ -4715,7 +4732,6 @@ if (enable_java_templates) {
_all_create_module_targets = []
_all_module_zip_paths = []
_all_module_build_configs = []
_all_module_unused_resources_deps = []
foreach(_module, _modules) {
_module_target = _module.module_target
_module_build_config = _module.build_config
@ -4734,6 +4750,7 @@ if (enable_java_templates) {
# this file *must* be named ${_module.name}.zip
_create_module_target = "${_target_name}__${_module.name}__create"
_module_zip_path = "$target_gen_dir/$target_name/${_module.name}.zip"
create_android_app_bundle_module(_create_module_target) {
forward_variables_from(invoker,
[
@ -4784,27 +4801,6 @@ if (enable_java_templates) {
]
_all_module_zip_paths += [ _module_zip_path ]
_all_module_build_configs += [ _module_build_config ]
_all_module_unused_resources_deps += [
"${_module_target}__compile_resources",
_dex_target_for_module,
_module_build_config_target,
]
}
if (defined(invoker.strip_unused_resources) &&
invoker.strip_unused_resources) {
# Resources only live in the base module so we define the unused resources
# target only on the base module target.
_unused_resources_target = "${_base_target_name}__unused_resources"
_unused_resources_config =
"${_base_target_gen_dir}/${_base_target_name}_unused_resources.config"
unused_resources(_unused_resources_target) {
deps = _all_module_unused_resources_deps
module_build_configs = _all_module_build_configs
if (_proguard_enabled) {
proguard_mapping_path = _proguard_mapping_path
}
output_config = _unused_resources_config
}
}
_all_rebased_module_zip_paths =

@ -179,23 +179,10 @@ template("chrome_public_common_apk_or_module_tmpl") {
png_to_webp = !is_java_debug
}
# We only optimize resources for bundles since APKs are not shipped.
# Resources only live in the base module atm as such we only need to set
# these on the base module
if (_is_bundle) {
# Removes metadata needed for Resources.getIdentifier("resource_name").
strip_resource_names = !is_java_debug
# Removes metadata needed for Resources.getIdentifier("resource_name").
strip_resource_names = !is_java_debug
# Shortens resource file names in apk eg: res/drawable/button.xml -> res/a.xml
short_resource_paths = true
# Removes unused resources from the apk. Only enabled on official builds
# since it adds a slow step and serializes the build graph causing fewer
# expensive tasks (eg: proguarding, resource optimization) to be run in
# parallel by adding dependencies between them (adds around 10-20
# seconds on my machine).
strip_unused_resources = is_official_build
}
short_resource_paths = true
if (!defined(aapt_locale_allowlist)) {
# Include resource strings files only for supported locales.
@ -552,16 +539,13 @@ template("monochrome_public_common_apk_or_module_tmpl") {
}
}
# We only optimize resources in bundles.
if (_is_bundle_module) {
# Resources config for blocklisting resource names from obfuscation
resources_config_paths = [
"//android_webview/aapt2.config",
"//chrome/android/aapt2.config",
]
if (defined(invoker.resources_config_paths)) {
resources_config_paths += invoker.resources_config_paths
}
# Resources config for blocklisting resource names from obfuscation
resources_config_paths = [
"//android_webview/aapt2.config",
"//chrome/android/aapt2.config",
]
if (defined(invoker.resources_config_paths)) {
resources_config_paths += invoker.resources_config_paths
}
if (defined(invoker.never_incremental)) {

@ -168,9 +168,6 @@ template("chrome_bundle") {
is_multi_abi = _is_multi_abi
validate_services = _enable_chrome_module
# For this to be respected, it must also be set on the base module target.
strip_unused_resources = is_official_build
# List of DFMs that are installed by default by wrapper scripts, to make
# testing easier. This removes the need to manually specify, e.g.,
# "-m dev_ui" on every install or run.

@ -138,7 +138,7 @@ java_prebuilt("com_android_tools_common_java") {
# This target does not come with most of its dependencies and is
# only meant to be used by the resources shrinker. If you wish to use
# this for other purposes, change buildCompileNoDeps in build.gradle.
visibility = [ "//build/android/unused_resources:*" ]
visibility = [ "//build/android/gyp/resources_shrinker:*" ]
}
# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
@ -150,7 +150,7 @@ java_prebuilt("com_android_tools_layoutlib_layoutlib_api_java") {
# This target does not come with most of its dependencies and is
# only meant to be used by the resources shrinker. If you wish to use
# this for other purposes, change buildCompileNoDeps in build.gradle.
visibility = [ "//build/android/unused_resources:*" ]
visibility = [ "//build/android/gyp/resources_shrinker:*" ]
}
# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
@ -162,7 +162,7 @@ java_prebuilt("com_android_tools_sdk_common_java") {
# This target does not come with most of its dependencies and is
# only meant to be used by the resources shrinker. If you wish to use
# this for other purposes, change buildCompileNoDeps in build.gradle.
visibility = [ "//build/android/unused_resources:*" ]
visibility = [ "//build/android/gyp/resources_shrinker:*" ]
}
# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.

@ -655,7 +655,7 @@ class BuildConfigGenerator extends DefaultTask {
sb.append(' # This target does not come with most of its dependencies and is\n')
sb.append(' # only meant to be used by the resources shrinker. If you wish to use\n')
sb.append(' # this for other purposes, change buildCompileNoDeps in build.gradle.\n')
sb.append(' visibility = [ "//build/android/unused_resources:*" ]\n')
sb.append(' visibility = [ "//build/android/gyp/resources_shrinker:*" ]\n')
break
case 'org_jetbrains_kotlinx_kotlinx_coroutines_android':
sb.append('requires_android = true')