1#!/usr/bin/env python3 2# encoding: utf-8 3# Copyright 2021 The Chromium Authors 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7import argparse 8import os 9import sys 10 11from util import build_utils 12from util import resource_utils 13import action_helpers # build_utils adds //build to sys.path. 14 15 16def _FilterUnusedResources(r_text_in, r_text_out, unused_resources_config): 17 removed_resources = set() 18 with open(unused_resources_config, encoding='utf-8') as output_config: 19 for line in output_config: 20 # example line: attr/line_height#remove 21 resource = line.split('#')[0] 22 resource_type, resource_name = resource.split('/') 23 removed_resources.add((resource_type, resource_name)) 24 kept_lines = [] 25 with open(r_text_in, encoding='utf-8') as infile: 26 for line in infile: 27 # example line: int attr line_height 0x7f0014ee 28 resource_type, resource_name = line.split(' ')[1:3] 29 if (resource_type, resource_name) not in removed_resources: 30 kept_lines.append(line) 31 32 with open(r_text_out, 'w', encoding='utf-8') as out_file: 33 out_file.writelines(kept_lines) 34 35 36def main(args): 37 parser = argparse.ArgumentParser() 38 39 action_helpers.add_depfile_arg(parser) 40 parser.add_argument('--script', 41 required=True, 42 help='Path to the unused resources detector script.') 43 parser.add_argument( 44 '--dependencies-res-zips', 45 required=True, 46 action='append', 47 help='Resources zip archives to investigate for unused resources.') 48 parser.add_argument('--dexes', 49 action='append', 50 required=True, 51 help='Path to dex file, or zip with dex files.') 52 parser.add_argument( 53 '--proguard-mapping', 54 help='Path to proguard mapping file for the optimized dex.') 55 parser.add_argument('--r-text-in', required=True, help='Path to input R.txt') 56 parser.add_argument( 57 '--r-text-out', 58 help='Path to output R.txt with unused resources removed.') 59 parser.add_argument('--android-manifests', 60 action='append', 61 required=True, 62 help='Path to AndroidManifest') 63 parser.add_argument('--output-config', 64 required=True, 65 help='Path to output the aapt2 config to.') 66 args = build_utils.ExpandFileArgs(args) 67 options = parser.parse_args(args) 68 options.dependencies_res_zips = (action_helpers.parse_gn_list( 69 options.dependencies_res_zips)) 70 71 # in case of no resources, short circuit early. 72 if not options.dependencies_res_zips: 73 build_utils.Touch(options.output_config) 74 return 75 76 with build_utils.TempDir() as temp_dir: 77 dep_subdirs = [] 78 for dependency_res_zip in options.dependencies_res_zips: 79 dep_subdirs += resource_utils.ExtractDeps([dependency_res_zip], temp_dir) 80 81 cmd = [ 82 options.script, 83 '--rtxts', 84 options.r_text_in, 85 '--manifests', 86 ':'.join(options.android_manifests), 87 '--resourceDirs', 88 ':'.join(dep_subdirs), 89 '--dexes', 90 ':'.join(options.dexes), 91 '--outputConfig', 92 options.output_config, 93 ] 94 if options.proguard_mapping: 95 cmd += [ 96 '--mapping', 97 options.proguard_mapping, 98 ] 99 build_utils.CheckOutput(cmd) 100 101 if options.r_text_out: 102 _FilterUnusedResources(options.r_text_in, options.r_text_out, 103 options.output_config) 104 105 if options.depfile: 106 depfile_deps = (options.dependencies_res_zips + options.android_manifests + 107 options.dexes) + [options.r_text_in] 108 if options.proguard_mapping: 109 depfile_deps.append(options.proguard_mapping) 110 action_helpers.write_depfile(options.depfile, options.output_config, 111 depfile_deps) 112 113 114if __name__ == '__main__': 115 main(sys.argv[1:]) 116