<filename>tools/android/find_unused_resources.py<gh_stars>1-10
#!/usr/bin/python
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Lists unused Java strings and other resources."""

import optparse
import re
import subprocess
import sys


def GetApkResources(apk_path):
  """Returns the types and names of resources packaged in an APK.

  Args:
    apk_path: path to the APK.

  Returns:
    The resources in the APK 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]


def GetUsedResources(source_paths, resource_types):
  """Returns the types and names of resources used in Java or resource files.

  Args:
    source_paths: a list of files or folders collectively containing all the
        Java files, resource files, and the AndroidManifest.xml.
    resource_types: a list of resource types to look for.  Example:
        ['string', 'drawable']

  Returns:
    The resources referenced by the Java and resource files as a list of tuples
    (type, name).  Example:
    [('drawable', 'app_icon'), ('layout', 'month_picker'), ...]
  """
  type_regex = '|'.join(map(re.escape, resource_types))
  patterns = [r'@(())(%s)/(\w+)' % type_regex,
              r'\b((\w+\.)*)R\.(%s)\.(\w+)' % type_regex]
  resources = []
  for pattern in patterns:
    p = subprocess.Popen(
        ['grep', '-REIhoe', pattern] + source_paths,
        stdout=subprocess.PIPE)
    grep_out, grep_err = p.communicate()
    # Check stderr instead of return code, since return code is 1 when no
    # matches are found.
    assert not grep_err, 'grep failed'
    matches = re.finditer(pattern, grep_out)
    for match in matches:
      package = match.group(1)
      if package == 'android.':
        continue
      type_ = match.group(3)
      name = match.group(4)
      resources.append((type_, name))
  return resources


def FormatResources(resources):
  """Formats a list of resources for printing.

  Args:
    resources: a list of resources, given as (type, name) tuples.
  """
  return '\n'.join(['%-12s %s' % (t, n) for t, n in sorted(resources)])


def ParseArgs(args):
  usage = 'usage: %prog [-v] APK_PATH SOURCE_PATH...'
  parser = optparse.OptionParser(usage=usage)
  parser.add_option('-v', help='Show verbose output', action='store_true')
  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:]


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)

  # aapt dump fails silently. Notify the user if things look wrong.
  if not apk_resources:
    print >> sys.stderr, (
        'Warning: No resources found in the APK. Did you provide the correct '
        'APK path?')
  if not used_resources:
    print >> sys.stderr, (
        'Warning: No resources references 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))

  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 used resources:' % len(used_resources)
    print FormatResources(used_resources), '\n'
    print '%d unused resources:' % len(unused_resources)
  print FormatResources(unused_resources)


if __name__ == '__main__':
  main()

<filename>tools/android/find_unused_resources.py<gh_stars>1-10
#!/usr/bin/python
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Lists unused Java strings and other resources."""

import optparse
import re
import subprocess
import sys


def GetApkResources(apk_path):
  """Returns the types and names of resources packaged in an APK.

  Args:
    apk_path: path to the APK.

  Returns:
    The resources in the APK 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]


def GetUsedResources(source_paths, resource_types):
  """Returns the types and names of resources used in Java or resource files.

  Args:
    source_paths: a list of files or folders collectively containing all the
        Java files, resource files, and the AndroidManifest.xml.
    resource_types: a list of resource types to look for.  Example:
        ['string', 'drawable']

  Returns:
    The resources referenced by the Java and resource files as a list of tuples
    (type, name).  Example:
    [('drawable', 'app_icon'), ('layout', 'month_picker'), ...]
  """
  type_regex = '|'.join(map(re.escape, resource_types))
  patterns = [r'@(())(%s)/(\w+)' % type_regex,
              r'\b((\w+\.)*)R\.(%s)\.(\w+)' % type_regex]
  resources = []
  for pattern in patterns:
    p = subprocess.Popen(
        ['grep', '-REIhoe', pattern] + source_paths,
        stdout=subprocess.PIPE)
    grep_out, grep_err = p.communicate()
    # Check stderr instead of return code, since return code is 1 when no
    # matches are found.
    assert not grep_err, 'grep failed'
    matches = re.finditer(pattern, grep_out)
    for match in matches:
      package = match.group(1)
      if package == 'android.':
        continue
      type_ = match.group(3)
      name = match.group(4)
      resources.append((type_, name))
  return resources


def FormatResources(resources):
  """Formats a list of resources for printing.

  Args:
    resources: a list of resources, given as (type, name) tuples.
  """
  return '\n'.join(['%-12s %s' % (t, n) for t, n in sorted(resources)])


def ParseArgs(args):
  usage = 'usage: %prog [-v] APK_PATH SOURCE_PATH...'
  parser = optparse.OptionParser(usage=usage)
  parser.add_option('-v', help='Show verbose output', action='store_true')
  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:]


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)

  # aapt dump fails silently. Notify the user if things look wrong.
  if not apk_resources:
    print >> sys.stderr, (
        'Warning: No resources found in the APK. Did you provide the correct '
        'APK path?')
  if not used_resources:
    print >> sys.stderr, (
        'Warning: No resources references 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))

  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 used resources:' % len(used_resources)
    print FormatResources(used_resources), '\n'
    print '%d unused resources:' % len(unused_resources)
  print FormatResources(unused_resources)


if __name__ == '__main__':
  main()

#!/usr/bin/python # Copyright (c) 2013 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. Lists unused Java strings and other resources. Returns the types and names of resources packaged in an APK.    Args:     apk_path: path to the APK.    Returns:     The resources in the APK as a list of tuples (type, name). Example:     [('drawable', 'arrow'), ('layout', 'month_picker'), ...] Returns the types and names of resources used in Java or resource files.    Args:     source_paths: a list of files or folders collectively containing all the         Java files, resource files, and the AndroidManifest.xml.     resource_types: a list of resource types to look for.  Example:         ['string', 'drawable']    Returns:     The resources referenced by the Java and resource files as a list of tuples     (type, name).  Example:     [('drawable', 'app_icon'), ('layout', 'month_picker'), ...] # Check stderr instead of return code, since return code is 1 when no # matches are found. Formats a list of resources for printing.    Args:     resources: a list of resources, given as (type, name) tuples. # aapt dump fails silently. Notify the user if things look wrong.