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.
6 """Lists unused Java strings and other resources."""
14 def GetLibraryResources(r_txt_paths
):
15 """Returns the resources packaged in a list of libraries.
18 r_txt_paths: paths to each library's generated R.txt file which lists the
19 resources it contains.
22 The resources in the libraries as a list of tuples (type, name). Example:
23 [('drawable', 'arrow'), ('layout', 'month_picker'), ...]
26 for r_txt_path
in r_txt_paths
:
27 with
open(r_txt_path
, 'r') as f
:
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'):
38 resources
.append((res_type
, name
))
42 def GetUsedResources(source_paths
, resource_types
):
43 """Returns the types and names of resources used in Java or resource files.
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']
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'), ...]
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
]
60 for pattern
in patterns
:
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
67 assert not grep_err
, 'grep failed'
68 matches
= re
.finditer(pattern
, grep_out
)
70 package
= match
.group(1)
71 if package
== 'android.':
73 type_
= match
.group(3)
75 resources
.append((type_
, name
))
79 def FormatResources(resources
):
80 """Formats a list of resources for printing.
83 resources: a list of resources, given as (type, name) tuples.
85 return '\n'.join(['%-12s %s' % (t
, n
) for t
, n
in sorted(resources
)])
89 parser
= optparse
.OptionParser()
90 parser
.add_option('-v', help='Show verbose output', action
='store_true')
91 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',
99 options
, args
= parser
.parse_args(args
=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
)
111 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
119 # aapt dump fails silently. Notify the user if things look wrong.
120 if not defined_resources
:
121 print >> sys
.stderr
, (
122 'Warning: No resources found. Did you provide the correct R.txt paths?')
123 if not used_resources
:
124 print >> sys
.stderr
, (
125 'Warning: No resources referenced from Java or resource files. Did you '
126 'provide the correct source paths?')
127 if undefined_resources
:
128 print >> sys
.stderr
, (
129 'Warning: found %d "undefined" resources that are referenced by Java '
130 'files or by other resources, but are not defined anywhere. Run with '
131 '-v to see them.' % len(undefined_resources
))
134 print '%d undefined resources:' % len(undefined_resources
)
135 print FormatResources(undefined_resources
), '\n'
136 print '%d resources defined:' % len(defined_resources
)
137 print FormatResources(defined_resources
), '\n'
138 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
)
144 if __name__
== '__main__':