blob: b5cce9066b48a50709b037f84138e4145356bc20 [file] [log] [blame]
Dirk Pranke45acd532021-03-24 06:52:401#!/usr/bin/env python
Avi Drissmaneb18c152022-09-15 20:11:092# Copyright 2013 The Chromium Authors
[email protected]c4ff1f52013-04-11 00:33:123# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Lists unused Java strings and other resources."""
7
Raul Tambre9dd16d22019-09-22 17:18:528from __future__ import print_function
9
[email protected]c4ff1f52013-04-11 00:33:1210import optparse
11import re
12import subprocess
13import sys
14
15
[email protected]0f00e532013-09-06 20:29:5916def GetLibraryResources(r_txt_paths):
17 """Returns the resources packaged in a list of libraries.
[email protected]c4ff1f52013-04-11 00:33:1218
19 Args:
[email protected]0f00e532013-09-06 20:29:5920 r_txt_paths: paths to each library's generated R.txt file which lists the
21 resources it contains.
[email protected]c4ff1f52013-04-11 00:33:1222
23 Returns:
[email protected]0f00e532013-09-06 20:29:5924 The resources in the libraries as a list of tuples (type, name). Example:
[email protected]c4ff1f52013-04-11 00:33:1225 [('drawable', 'arrow'), ('layout', 'month_picker'), ...]
26 """
[email protected]0f00e532013-09-06 20:29:5927 resources = []
28 for r_txt_path in r_txt_paths:
29 with open(r_txt_path, 'r') as f:
30 for line in f:
31 line = line.strip()
32 if not line:
33 continue
34 data_type, res_type, name, _ = line.split(None, 3)
35 assert data_type in ('int', 'int[]')
36 # Hide attrs, which are redundant with styleables and always appear
37 # unused, and hide ids, which are innocuous even if unused.
38 if res_type in ('attr', 'id'):
39 continue
40 resources.append((res_type, name))
41 return resources
[email protected]c4ff1f52013-04-11 00:33:1242
43
44def GetUsedResources(source_paths, resource_types):
45 """Returns the types and names of resources used in Java or resource files.
46
47 Args:
48 source_paths: a list of files or folders collectively containing all the
49 Java files, resource files, and the AndroidManifest.xml.
50 resource_types: a list of resource types to look for. Example:
51 ['string', 'drawable']
52
53 Returns:
54 The resources referenced by the Java and resource files as a list of tuples
55 (type, name). Example:
56 [('drawable', 'app_icon'), ('layout', 'month_picker'), ...]
57 """
58 type_regex = '|'.join(map(re.escape, resource_types))
Peter Wenf857c642017-05-25 17:34:0559 patterns = [
60 # @+drawable/id and @drawable/id
61 r'@((\+?))(%s)/(\w+)' % type_regex,
62 # package.R.style.id
63 r'\b((\w+\.)*)R\.(%s)\.(\w+)' % type_regex,
64 # <style name="child" parent="id">
65 r'<(())(%s).*parent="(\w+)">' % type_regex,
66 ]
[email protected]c4ff1f52013-04-11 00:33:1267 resources = []
68 for pattern in patterns:
69 p = subprocess.Popen(
70 ['grep', '-REIhoe', pattern] + source_paths,
71 stdout=subprocess.PIPE)
72 grep_out, grep_err = p.communicate()
73 # Check stderr instead of return code, since return code is 1 when no
74 # matches are found.
75 assert not grep_err, 'grep failed'
76 matches = re.finditer(pattern, grep_out)
77 for match in matches:
78 package = match.group(1)
79 if package == 'android.':
80 continue
81 type_ = match.group(3)
82 name = match.group(4)
83 resources.append((type_, name))
84 return resources
85
86
87def FormatResources(resources):
88 """Formats a list of resources for printing.
89
90 Args:
91 resources: a list of resources, given as (type, name) tuples.
92 """
93 return '\n'.join(['%-12s %s' % (t, n) for t, n in sorted(resources)])
94
95
96def ParseArgs(args):
[email protected]0f00e532013-09-06 20:29:5997 parser = optparse.OptionParser()
[email protected]c4ff1f52013-04-11 00:33:1298 parser.add_option('-v', help='Show verbose output', action='store_true')
[email protected]0f00e532013-09-06 20:29:5999 parser.add_option('-s', '--source-path', help='Specify a source folder path '
100 '(e.g. ui/android/java)', action='append', default=[])
101 parser.add_option('-r', '--r-txt-path', help='Specify a "first-party" R.txt '
102 'file (e.g. out/Debug/content_shell_apk/R.txt)',
103 action='append', default=[])
104 parser.add_option('-t', '--third-party-r-txt-path', help='Specify an R.txt '
105 'file for a third party library', action='append',
106 default=[])
[email protected]c4ff1f52013-04-11 00:33:12107 options, args = parser.parse_args(args=args)
[email protected]0f00e532013-09-06 20:29:59108 if args:
109 parser.error('positional arguments not allowed')
110 if not options.source_path:
111 parser.error('at least one source folder path must be specified with -s')
112 if not options.r_txt_path:
113 parser.error('at least one R.txt path must be specified with -r')
114 return (options.v, options.source_path, options.r_txt_path,
115 options.third_party_r_txt_path)
[email protected]c4ff1f52013-04-11 00:33:12116
117
118def main(args=None):
[email protected]0f00e532013-09-06 20:29:59119 verbose, source_paths, r_txt_paths, third_party_r_txt_paths = ParseArgs(args)
120 defined_resources = (set(GetLibraryResources(r_txt_paths)) -
121 set(GetLibraryResources(third_party_r_txt_paths)))
122 resource_types = list(set([r[0] for r in defined_resources]))
123 used_resources = set(GetUsedResources(source_paths, resource_types))
124 unused_resources = defined_resources - used_resources
125 undefined_resources = used_resources - defined_resources
[email protected]c4ff1f52013-04-11 00:33:12126
127 # aapt dump fails silently. Notify the user if things look wrong.
[email protected]0f00e532013-09-06 20:29:59128 if not defined_resources:
Raul Tambre9dd16d22019-09-22 17:18:52129 print(
130 'Warning: No resources found. Did you provide the correct R.txt paths?',
131 file=sys.stderr)
[email protected]c4ff1f52013-04-11 00:33:12132 if not used_resources:
Raul Tambre9dd16d22019-09-22 17:18:52133 print(
[email protected]0f00e532013-09-06 20:29:59134 'Warning: No resources referenced from Java or resource files. Did you '
Raul Tambre9dd16d22019-09-22 17:18:52135 'provide the correct source paths?',
136 file=sys.stderr)
[email protected]c4ff1f52013-04-11 00:33:12137 if undefined_resources:
Raul Tambre9dd16d22019-09-22 17:18:52138 print(
[email protected]c4ff1f52013-04-11 00:33:12139 'Warning: found %d "undefined" resources that are referenced by Java '
[email protected]0f00e532013-09-06 20:29:59140 'files or by other resources, but are not defined anywhere. Run with '
Raul Tambre9dd16d22019-09-22 17:18:52141 '-v to see them.' % len(undefined_resources),
142 file=sys.stderr)
[email protected]c4ff1f52013-04-11 00:33:12143
144 if verbose:
Raul Tambre9dd16d22019-09-22 17:18:52145 print('%d undefined resources:' % len(undefined_resources))
146 print(FormatResources(undefined_resources), '\n')
147 print('%d resources defined:' % len(defined_resources))
148 print(FormatResources(defined_resources), '\n')
149 print('%d used resources:' % len(used_resources))
150 print(FormatResources(used_resources), '\n')
151 print('%d unused resources:' % len(unused_resources))
152 print(FormatResources(unused_resources))
[email protected]c4ff1f52013-04-11 00:33:12153
154
155if __name__ == '__main__':
156 main()