• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2012 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import optparse
6import os
7import re
8import shlex
9import subprocess
10import sys
11
12from pylib import cmd_helper
13from pylib import constants
14
15
16def _PrintMessage(warnings, title, action, known_bugs_file):
17  if warnings:
18    print
19    print '*' * 80
20    print '%s warnings.' % title
21    print '%s %s' % (action, known_bugs_file)
22    print '-' * 80
23    for warning in warnings:
24      print warning
25    print '-' * 80
26    print
27
28
29def _StripLineNumbers(current_warnings):
30  re_line = r':\[line.*?\]$'
31  return [re.sub(re_line, '', x) for x in current_warnings]
32
33
34def _DiffKnownWarnings(current_warnings_set, known_bugs_file):
35  with open(known_bugs_file, 'r') as known_bugs:
36    known_bugs_set = set(known_bugs.read().splitlines())
37
38  new_warnings = current_warnings_set - known_bugs_set
39  _PrintMessage(sorted(new_warnings), 'New', 'Please fix, or perhaps add to',
40                known_bugs_file)
41
42  obsolete_warnings = known_bugs_set - current_warnings_set
43  _PrintMessage(sorted(obsolete_warnings), 'Obsolete', 'Please remove from',
44                known_bugs_file)
45
46  count = len(new_warnings) + len(obsolete_warnings)
47  if count:
48    print '*** %d FindBugs warning%s! ***' % (count, 's' * (count > 1))
49    if len(new_warnings):
50      print '*** %d: new ***' % len(new_warnings)
51    if len(obsolete_warnings):
52      print '*** %d: obsolete ***' % len(obsolete_warnings)
53    print
54    print 'Alternatively,  rebaseline with --rebaseline command option'
55    print
56  else:
57    print 'No new FindBugs warnings.'
58  print
59  return count
60
61
62def _Rebaseline(current_warnings_set, known_bugs_file):
63  with file(known_bugs_file, 'w') as known_bugs:
64    for warning in sorted(current_warnings_set):
65      print >> known_bugs, warning
66  return 0
67
68
69def _GetChromeJars(release_version):
70  version = 'Debug'
71  if release_version:
72    version = 'Release'
73  path = os.path.join(constants.DIR_SOURCE_ROOT,
74                      os.environ.get('CHROMIUM_OUT_DIR', 'out'),
75                      version,
76                      'lib.java')
77  cmd = 'find %s -name "*.jar"' % path
78  out = cmd_helper.GetCmdOutput(shlex.split(cmd))
79  out = [p for p in out.splitlines() if not p.endswith('.dex.jar')]
80  if not out:
81    print 'No classes found in %s' % path
82  return ' '.join(out)
83
84
85def _Run(exclude, known_bugs, classes_to_analyze, auxiliary_classes,
86        rebaseline, release_version, findbug_args):
87  """Run the FindBugs.
88
89  Args:
90    exclude: the exclude xml file, refer to FindBugs's -exclude command option.
91    known_bugs: the text file of known bugs. The bugs in it will not be
92                reported.
93    classes_to_analyze: the list of classes need to analyze, refer to FindBug's
94                        -onlyAnalyze command line option.
95    auxiliary_classes: the classes help to analyze, refer to FindBug's
96                       -auxclasspath command line option.
97    rebaseline: True if the known_bugs file needs rebaseline.
98    release_version: True if the release version needs check, otherwise check
99                     debug version.
100    findbug_args: addtional command line options needs pass to Findbugs.
101  """
102
103  chrome_src = constants.DIR_SOURCE_ROOT
104  sdk_root = constants.ANDROID_SDK_ROOT
105  sdk_version = constants.ANDROID_SDK_VERSION
106
107  system_classes = []
108  system_classes.append(os.path.join(sdk_root, 'platforms',
109                                     'android-%s' % sdk_version, 'android.jar'))
110  if auxiliary_classes:
111    for classes in auxiliary_classes:
112      system_classes.append(os.path.abspath(classes))
113
114  findbugs_javacmd = 'java'
115  findbugs_home = os.path.join(chrome_src, 'third_party', 'findbugs')
116  findbugs_jar = os.path.join(findbugs_home, 'lib', 'findbugs.jar')
117  findbugs_pathsep = ':'
118  findbugs_maxheap = '768'
119
120  cmd = '%s ' % findbugs_javacmd
121  cmd = '%s -classpath %s%s' % (cmd, findbugs_jar, findbugs_pathsep)
122  cmd = '%s -Xmx%sm ' % (cmd, findbugs_maxheap)
123  cmd = '%s -Dfindbugs.home="%s" ' % (cmd, findbugs_home)
124  cmd = '%s -jar %s ' % (cmd, findbugs_jar)
125
126  cmd = '%s -textui -sortByClass ' % cmd
127  cmd = '%s -pluginList %s' % (cmd, os.path.join(chrome_src, 'tools', 'android',
128                                                 'findbugs_plugin', 'lib',
129                                                 'chromiumPlugin.jar'))
130  if len(system_classes):
131    cmd = '%s -auxclasspath %s ' % (cmd, ':'.join(system_classes))
132
133  if classes_to_analyze:
134    cmd = '%s -onlyAnalyze %s ' % (cmd, classes_to_analyze)
135
136  if exclude:
137    cmd = '%s -exclude %s ' % (cmd, os.path.abspath(exclude))
138
139  if findbug_args:
140    cmd = '%s %s ' % (cmd, findbug_args)
141
142  chrome_classes = _GetChromeJars(release_version)
143  if not chrome_classes:
144    return 1
145  cmd = '%s %s ' % (cmd, chrome_classes)
146
147  proc = subprocess.Popen(shlex.split(cmd),
148                          stdout=subprocess.PIPE, stderr=subprocess.PIPE)
149  out, _err = proc.communicate()
150  current_warnings_set = set(_StripLineNumbers(filter(None, out.splitlines())))
151
152  if rebaseline:
153    return _Rebaseline(current_warnings_set, known_bugs)
154  else:
155    return _DiffKnownWarnings(current_warnings_set, known_bugs)
156
157def Run(options):
158  exclude_file = None
159  known_bugs_file = None
160
161  if options.exclude:
162    exclude_file = options.exclude
163  elif options.base_dir:
164    exclude_file = os.path.join(options.base_dir, 'findbugs_exclude.xml')
165
166  if options.known_bugs:
167    known_bugs_file = options.known_bugs
168  elif options.base_dir:
169    known_bugs_file = os.path.join(options.base_dir, 'findbugs_known_bugs.txt')
170
171  auxclasspath = None
172  if options.auxclasspath:
173    auxclasspath = options.auxclasspath.split(':')
174  return _Run(exclude_file, known_bugs_file, options.only_analyze, auxclasspath,
175              options.rebaseline, options.release_build, options.findbug_args)
176
177
178def GetCommonParser():
179  parser = optparse.OptionParser()
180  parser.add_option('-r',
181                    '--rebaseline',
182                    action='store_true',
183                    dest='rebaseline',
184                    help='Rebaseline known findbugs issues.')
185
186  parser.add_option('-a',
187                    '--auxclasspath',
188                    action='store',
189                    default=None,
190                    dest='auxclasspath',
191                    help='Set aux classpath for analysis.')
192
193  parser.add_option('-o',
194                    '--only-analyze',
195                    action='store',
196                    default=None,
197                    dest='only_analyze',
198                    help='Only analyze the given classes and packages.')
199
200  parser.add_option('-e',
201                    '--exclude',
202                    action='store',
203                    default=None,
204                    dest='exclude',
205                    help='Exclude bugs matching given filter.')
206
207  parser.add_option('-k',
208                    '--known-bugs',
209                    action='store',
210                    default=None,
211                    dest='known_bugs',
212                    help='Not report the bugs in the given file.')
213
214  parser.add_option('-l',
215                    '--release-build',
216                    action='store_true',
217                    dest='release_build',
218                    help='Analyze release build instead of debug.')
219
220  parser.add_option('-f',
221                    '--findbug-args',
222                    action='store',
223                    default=None,
224                    dest='findbug_args',
225                    help='Additional findbug arguments.')
226
227  parser.add_option('-b',
228                    '--base-dir',
229                    action='store',
230                    default=None,
231                    dest='base_dir',
232                    help='Base directory for configuration file.')
233
234  return parser
235
236
237def main():
238  parser = GetCommonParser()
239  options, _ = parser.parse_args()
240
241  return Run(options)
242
243
244if __name__ == '__main__':
245  sys.exit(main())
246