• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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