#!/usr/bin/env python3 # encoding: utf-8 # 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 os import sys from util import build_utils from util import resource_utils import action_helpers # build_utils adds //build to sys.path. def _FilterUnusedResources(r_text_in, r_text_out, unused_resources_config): removed_resources = set() with open(unused_resources_config, encoding='utf-8') as output_config: for line in output_config: # example line: attr/line_height#remove resource = line.split('#')[0] resource_type, resource_name = resource.split('/') removed_resources.add((resource_type, resource_name)) kept_lines = [] with open(r_text_in, encoding='utf-8') as infile: for line in infile: # example line: int attr line_height 0x7f0014ee resource_type, resource_name = line.split(' ')[1:3] if (resource_type, resource_name) not in removed_resources: kept_lines.append(line) with open(r_text_out, 'w', encoding='utf-8') as out_file: out_file.writelines(kept_lines) def main(args): parser = argparse.ArgumentParser() action_helpers.add_depfile_arg(parser) parser.add_argument('--script', required=True, help='Path to the unused resources detector script.') 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', required=True, help='Path to dex file, or zip with dex files.') parser.add_argument( '--proguard-mapping', help='Path to proguard mapping file for the optimized dex.') parser.add_argument('--r-text-in', required=True, help='Path to input R.txt') parser.add_argument( '--r-text-out', help='Path to output R.txt with unused resources removed.') parser.add_argument('--android-manifests', action='append', required=True, help='Path to AndroidManifest') parser.add_argument('--output-config', required=True, help='Path to output the aapt2 config to.') args = build_utils.ExpandFileArgs(args) options = parser.parse_args(args) options.dependencies_res_zips = (action_helpers.parse_gn_list( options.dependencies_res_zips)) # in case of no resources, short circuit early. if not options.dependencies_res_zips: build_utils.Touch(options.output_config) return with build_utils.TempDir() as temp_dir: dep_subdirs = [] for dependency_res_zip in options.dependencies_res_zips: dep_subdirs += resource_utils.ExtractDeps([dependency_res_zip], temp_dir) cmd = [ options.script, '--rtxts', options.r_text_in, '--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) if options.r_text_out: _FilterUnusedResources(options.r_text_in, options.r_text_out, options.output_config) if options.depfile: depfile_deps = (options.dependencies_res_zips + options.android_manifests + options.dexes) + [options.r_text_in] if options.proguard_mapping: depfile_deps.append(options.proguard_mapping) action_helpers.write_depfile(options.depfile, options.output_config, depfile_deps) if __name__ == '__main__': main(sys.argv[1:])