Chromium Code Reviews| Index: tools/android/find_unused_resources.py |
| diff --git a/tools/android/find_unused_resources.py b/tools/android/find_unused_resources.py |
| index da86e734152749a6904ef08062715f9743b386e6..1e8fa48abc3ce7b29a8ccac66c07f2b324ad4ff7 100755 |
| --- a/tools/android/find_unused_resources.py |
| +++ b/tools/android/find_unused_resources.py |
| @@ -11,25 +11,32 @@ import subprocess |
| import sys |
| -def GetApkResources(apk_path): |
| - """Returns the types and names of resources packaged in an APK. |
| +def GetLibraryResources(r_txt_paths): |
| + """Returns the resources packaged in a list of libraries. |
| Args: |
| - apk_path: path to the APK. |
| + r_txt_paths: paths to each library's generated R.txt file which lists the |
| + resources it contains. |
| Returns: |
| - The resources in the APK as a list of tuples (type, name). Example: |
| + The resources in the libraries as a list of tuples (type, name). Example: |
| [('drawable', 'arrow'), ('layout', 'month_picker'), ...] |
| """ |
| - p = subprocess.Popen( |
| - ['aapt', 'dump', 'resources', apk_path], |
| - stdout=subprocess.PIPE) |
| - dump_out, _ = p.communicate() |
| - assert p.returncode == 0, 'aapt dump failed' |
| - matches = re.finditer( |
| - r'^\s+spec resource 0x[0-9a-fA-F]+ [\w.]+:(?P<type>\w+)/(?P<name>\w+)', |
| - dump_out, re.MULTILINE) |
| - return [m.group('type', 'name') for m in matches] |
| + resources = [] |
| + for r_txt_path in r_txt_paths: |
| + with open(r_txt_path, 'r') as f: |
| + for line in f: |
| + line = line.strip() |
| + if not line: |
| + continue |
| + data_type, res_type, name, _ = line.split(None, 3) |
|
cjhopman
2013/09/06 00:38:08
what's the format of these lines in R.txt?
newt (away)
2013/09/06 00:49:50
int string sign_in 0x7f0800ac
int styleable Theme_
|
| + assert data_type in ('int', 'int[]') |
| + # Hide attrs, which are redundant with styleables and always appear |
| + # unused, and hide ids, which are innocuous even if unused. |
| + if res_type in ('attr', 'id'): |
| + continue |
| + resources.append((res_type, name)) |
| + return resources |
| def GetUsedResources(source_paths, resource_types): |
| @@ -79,43 +86,55 @@ def FormatResources(resources): |
| def ParseArgs(args): |
| - usage = 'usage: %prog [-v] APK_PATH SOURCE_PATH...' |
| - parser = optparse.OptionParser(usage=usage) |
| + parser = optparse.OptionParser() |
| parser.add_option('-v', help='Show verbose output', action='store_true') |
| + parser.add_option('-s', '--source-path', help='Specify a source folder path ' |
| + '(e.g. ui/android/java)', action='append', default=[]) |
| + parser.add_option('-r', '--r-txt-path', help='Specify a "first-party" R.txt ' |
| + 'file (e.g. out/Debug/content_shell_apk/R.txt)', |
| + action='append', default=[]) |
| + parser.add_option('-t', '--third-party-r-txt-path', help='Specify an R.txt ' |
| + 'file for a third party library', action='append', |
| + default=[]) |
| options, args = parser.parse_args(args=args) |
| - if len(args) < 2: |
| - parser.error('must provide APK_PATH and SOURCE_PATH arguments') |
| - return options.v, args[0], args[1:] |
| + if args: |
| + parser.error('positional arguments not allowed') |
| + if not options.source_path: |
| + parser.error('at least one source folder path must be specified with -s') |
| + if not options.r_txt_path: |
| + parser.error('at least one R.txt path must be specified with -r') |
| + return (options.v, options.source_path, options.r_txt_path, |
| + options.third_party_r_txt_path) |
| def main(args=None): |
| - verbose, apk_path, source_paths = ParseArgs(args) |
| - apk_resources = GetApkResources(apk_path) |
| - resource_types = list(set([r[0] for r in apk_resources])) |
| - used_resources = GetUsedResources(source_paths, resource_types) |
| - unused_resources = set(apk_resources) - set(used_resources) |
| - undefined_resources = set(used_resources) - set(apk_resources) |
| + verbose, source_paths, r_txt_paths, third_party_r_txt_paths = ParseArgs(args) |
| + defined_resources = (set(GetLibraryResources(r_txt_paths)) - |
| + set(GetLibraryResources(third_party_r_txt_paths))) |
| + resource_types = list(set([r[0] for r in defined_resources])) |
| + used_resources = set(GetUsedResources(source_paths, resource_types)) |
| + unused_resources = defined_resources - used_resources |
| + undefined_resources = used_resources - defined_resources |
| # aapt dump fails silently. Notify the user if things look wrong. |
| - if not apk_resources: |
| + if not defined_resources: |
| print >> sys.stderr, ( |
| - 'Warning: No resources found in the APK. Did you provide the correct ' |
| - 'APK path?') |
| + 'Warning: No resources found. Did you provide the correct R.txt paths?') |
| if not used_resources: |
| print >> sys.stderr, ( |
| - 'Warning: No resources references from Java or resource files. Did you ' |
| + 'Warning: No resources referenced from Java or resource files. Did you ' |
| 'provide the correct source paths?') |
| if undefined_resources: |
| print >> sys.stderr, ( |
| 'Warning: found %d "undefined" resources that are referenced by Java ' |
| - 'files or by other resources, but are not in the APK. Run with -v to ' |
| - 'see them.' % len(undefined_resources)) |
| + 'files or by other resources, but are not defined anywhere. Run with ' |
| + '-v to see them.' % len(undefined_resources)) |
| if verbose: |
| print '%d undefined resources:' % len(undefined_resources) |
| print FormatResources(undefined_resources), '\n' |
| - print '%d resources packaged into the APK:' % len(apk_resources) |
| - print FormatResources(apk_resources), '\n' |
| + print '%d resources defined:' % len(defined_resources) |
| + print FormatResources(defined_resources), '\n' |
| print '%d used resources:' % len(used_resources) |
| print FormatResources(used_resources), '\n' |
| print '%d unused resources:' % len(unused_resources) |