blob: 1e8fa48abc3ce7b29a8ccac66c07f2b324ad4ff7 [file] [log] [blame]
[email protected]c4ff1f52013-04-11 00:33:121#!/usr/bin/python
2# Copyright (c) 2013 The Chromium Authors. All rights reserved.
3# 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
8import optparse
9import re
10import subprocess
11import sys
12
13
[email protected]0f00e532013-09-06 20:29:5914def GetLibraryResources(r_txt_paths):
15 """Returns the resources packaged in a list of libraries.
[email protected]c4ff1f52013-04-11 00:33:1216
17 Args:
[email protected]0f00e532013-09-06 20:29:5918 r_txt_paths: paths to each library's generated R.txt file which lists the
19 resources it contains.
[email protected]c4ff1f52013-04-11 00:33:1220
21 Returns:
[email protected]0f00e532013-09-06 20:29:5922 The resources in the libraries as a list of tuples (type, name). Example:
[email protected]c4ff1f52013-04-11 00:33:1223 [('drawable', 'arrow'), ('layout', 'month_picker'), ...]
24 """
[email protected]0f00e532013-09-06 20:29:5925 resources = []
26 for r_txt_path in r_txt_paths:
27 with open(r_txt_path, 'r') as f:
28 for line in f:
29 line = line.strip()
30 if not line:
31 continue
32 data_type, res_type, name, _ = line.split(None, 3)
33 assert data_type in ('int', 'int[]')
34 # Hide attrs, which are redundant with styleables and always appear
35 # unused, and hide ids, which are innocuous even if unused.
36 if res_type in ('attr', 'id'):
37 continue
38 resources.append((res_type, name))
39 return resources
[email protected]c4ff1f52013-04-11 00:33:1240
41
42def GetUsedResources(source_paths, resource_types):
43 """Returns the types and names of resources used in Java or resource files.
44
45 Args:
46 source_paths: a list of files or folders collectively containing all the
47 Java files, resource files, and the AndroidManifest.xml.
48 resource_types: a list of resource types to look for. Example:
49 ['string', 'drawable']
50
51 Returns:
52 The resources referenced by the Java and resource files as a list of tuples
53 (type, name). Example:
54 [('drawable', 'app_icon'), ('layout', 'month_picker'), ...]
55 """
56 type_regex = '|'.join(map(re.escape, resource_types))
57 patterns = [r'@(())(%s)/(\w+)' % type_regex,
58 r'\b((\w+\.)*)R\.(%s)\.(\w+)' % type_regex]
59 resources = []
60 for pattern in patterns:
61 p = subprocess.Popen(
62 ['grep', '-REIhoe', pattern] + source_paths,
63 stdout=subprocess.PIPE)
64 grep_out, grep_err = p.communicate()
65 # Check stderr instead of return code, since return code is 1 when no
66 # matches are found.
67 assert not grep_err, 'grep failed'
68 matches = re.finditer(pattern, grep_out)
69 for match in matches:
70 package = match.group(1)
71 if package == 'android.':
72 continue
73 type_ = match.group(3)
74 name = match.group(4)
75 resources.append((type_, name))
76 return resources
77
78
79def FormatResources(resources):
80 """Formats a list of resources for printing.
81
82 Args:
83 resources: a list of resources, given as (type, name) tuples.
84 """
85 return '\n'.join(['%-12s %s' % (t, n) for t, n in sorted(resources)])
86
87
88def ParseArgs(args):
[email protected]0f00e532013-09-06 20:29:5989 parser = optparse.OptionParser()
[email protected]c4ff1f52013-04-11 00:33:1290 parser.add_option('-v', help='Show verbose output', action='store_true')
[email protected]0f00e532013-09-06 20:29:5991 parser.add_option('-s', '--source-path', help='Specify a source folder path '
92 '(e.g. ui/android/java)', action='append', default=[])
93 parser.add_option('-r', '--r-txt-path', help='Specify a "first-party" R.txt '
94 'file (e.g. out/Debug/content_shell_apk/R.txt)',
95 action='append', default=[])
96 parser.add_option('-t', '--third-party-r-txt-path', help='Specify an R.txt '
97 'file for a third party library', action='append',
98 default=[])
[email protected]c4ff1f52013-04-11 00:33:1299 options, args = parser.parse_args(args=args)
[email protected]0f00e532013-09-06 20:29:59100 if args:
101 parser.error('positional arguments not allowed')
102 if not options.source_path:
103 parser.error('at least one source folder path must be specified with -s')
104 if not options.r_txt_path:
105 parser.error('at least one R.txt path must be specified with -r')
106 return (options.v, options.source_path, options.r_txt_path,
107 options.third_party_r_txt_path)
[email protected]c4ff1f52013-04-11 00:33:12108
109
110def main(args=None):
[email protected]0f00e532013-09-06 20:29:59111 verbose, source_paths, r_txt_paths, third_party_r_txt_paths = ParseArgs(args)
112 defined_resources = (set(GetLibraryResources(r_txt_paths)) -
113 set(GetLibraryResources(third_party_r_txt_paths)))
114 resource_types = list(set([r[0] for r in defined_resources]))
115 used_resources = set(GetUsedResources(source_paths, resource_types))
116 unused_resources = defined_resources - used_resources
117 undefined_resources = used_resources - defined_resources
[email protected]c4ff1f52013-04-11 00:33:12118
119 # aapt dump fails silently. Notify the user if things look wrong.
[email protected]0f00e532013-09-06 20:29:59120 if not defined_resources:
[email protected]c4ff1f52013-04-11 00:33:12121 print >> sys.stderr, (
[email protected]0f00e532013-09-06 20:29:59122 'Warning: No resources found. Did you provide the correct R.txt paths?')
[email protected]c4ff1f52013-04-11 00:33:12123 if not used_resources:
124 print >> sys.stderr, (
[email protected]0f00e532013-09-06 20:29:59125 'Warning: No resources referenced from Java or resource files. Did you '
[email protected]c4ff1f52013-04-11 00:33:12126 'provide the correct source paths?')
127 if undefined_resources:
128 print >> sys.stderr, (
129 'Warning: found %d "undefined" resources that are referenced by Java '
[email protected]0f00e532013-09-06 20:29:59130 'files or by other resources, but are not defined anywhere. Run with '
131 '-v to see them.' % len(undefined_resources))
[email protected]c4ff1f52013-04-11 00:33:12132
133 if verbose:
134 print '%d undefined resources:' % len(undefined_resources)
135 print FormatResources(undefined_resources), '\n'
[email protected]0f00e532013-09-06 20:29:59136 print '%d resources defined:' % len(defined_resources)
137 print FormatResources(defined_resources), '\n'
[email protected]c4ff1f52013-04-11 00:33:12138 print '%d used resources:' % len(used_resources)
139 print FormatResources(used_resources), '\n'
140 print '%d unused resources:' % len(unused_resources)
141 print FormatResources(unused_resources)
142
143
144if __name__ == '__main__':
145 main()