blob: 996c0eeaaf9afd83343a4f4cde22f8a6ceb5b5a8 [file] [log] [blame]
#!/usr/bin/env python
#
# Copyright (c) 2012 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.
import optparse
import os
import re
import shlex
import subprocess
import sys
def _PrintMessage(warnings, title, action, known_bugs_file):
if warnings:
print
print '*' * 80
print '%s warnings.' % title
print '%s %s' % (action, known_bugs_file)
print '-' * 80
for warning in warnings:
print warning
print '-' * 80
print
def _StripLineNumbers(current_warnings):
re_line = r':\[line.*?\]$'
return [re.sub(re_line, '', x) for x in current_warnings]
def _DiffKnownWarnings(current_warnings_set, known_bugs_file):
with open(known_bugs_file, 'r') as known_bugs:
known_bugs_set = set(known_bugs.read().splitlines())
new_warnings = current_warnings_set - known_bugs_set
_PrintMessage(sorted(new_warnings), 'New', 'Please fix, or perhaps add to',
known_bugs_file)
obsolete_warnings = known_bugs_set - current_warnings_set
_PrintMessage(sorted(obsolete_warnings), 'Obsolete', 'Please remove from',
known_bugs_file)
count = len(new_warnings) + len(obsolete_warnings)
if count:
print '*** %d FindBugs warning%s! ***' % (count, 's' * (count > 1))
if len(new_warnings):
print '*** %d: new ***' % len(new_warnings)
if len(obsolete_warnings):
print '*** %d: obsolete ***' % len(obsolete_warnings)
print
print 'Alternatively, rebaseline with --rebaseline command option'
print
else:
print 'No new FindBugs warnings.'
print
return count
def _Rebaseline(current_warnings_set, known_bugs_file):
with file(known_bugs_file, 'w') as known_bugs:
for warning in sorted(current_warnings_set):
print >>known_bugs, warning
return 0
def _GetChromeClasses(release_version):
chrome_src = os.getenv('CHROME_SRC')
version = 'Debug'
if release_version:
version = 'Release'
path = os.path.join(chrome_src, 'out', version)
cmd = 'find %s -name "*.class"' % path
proc = subprocess.Popen(shlex.split(cmd),
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = proc.communicate()
if not out:
print 'No classes found in %s' % path
return out
def _Run(exclude, known_bugs, classes_to_analyze, auxiliary_classes,
rebaseline, release_version, findbug_args):
"""Run the FindBugs.
Args:
exclude: the exclude xml file, refer to FindBugs's -exclude command option.
known_bugs: the text file of known bugs. The bugs in it will not be
reported.
classes_to_analyze: the list of classes need to analyze, refer to FindBug's
-onlyAnalyze command line option.
auxiliary_classes: the classes help to analyze, refer to FindBug's
-auxclasspath command line option.
rebaseline: True if the known_bugs file needs rebaseline.
release_version: True if the release version needs check, otherwise check
debug version.
findbug_args: addtional command line options needs pass to Findbugs.
"""
chrome_src = os.getenv('CHROME_SRC')
sdk_root = os.getenv('ANDROID_SDK_ROOT')
sdk_version = os.getenv('ANDROID_SDK_VERSION')
system_classes = []
system_classes.append(os.path.join(sdk_root, 'platforms',
'android-%s' % sdk_version, 'android.jar'))
if auxiliary_classes:
for classes in auxiliary_classes:
system_classes.append(os.path.abspath(classes))
cmd = '%s -textui -sortByClass ' % os.path.join(chrome_src, 'third_party',
'findbugs', 'bin', 'findbugs')
cmd = '%s -pluginList %s' % (cmd, os.path.join(chrome_src, 'tools', 'android',
'findbugs_plugin', 'lib',
'chromiumPlugin.jar'))
if len(system_classes):
cmd = '%s -auxclasspath %s ' % (cmd, ':'.join(system_classes))
if classes_to_analyze:
cmd = '%s -onlyAnalyze %s ' % (cmd, classes_to_analyze)
if exclude:
cmd = '%s -exclude %s ' % (cmd, os.path.abspath(exclude))
if findbug_args:
cmd = '%s %s ' % (cmd, fingbug_args)
chrome_classes = _GetChromeClasses(release_version)
if not chrome_classes:
return 1
cmd = '%s %s ' % (cmd, chrome_classes)
proc = subprocess.Popen(shlex.split(cmd),
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = proc.communicate()
current_warnings_set = set(_StripLineNumbers(filter(None, out.splitlines())))
if rebaseline:
return _Rebaseline(current_warnings_set, known_bugs)
else:
return _DiffKnownWarnings(current_warnings_set, known_bugs)
def Run(options):
exclude_file = None
known_bugs_file = None
if options.exclude:
exclude_file = options.exclude
elif options.base_dir:
exclude_file = os.path.join(options.base_dir, 'findbugs_exclude.xml')
if options.known_bugs:
known_bugs_file = options.known_bugs
elif options.base_dir:
known_bugs_file = os.path.join(options.base_dir, 'findbugs_known_bugs.txt')
auxclasspath = None
if options.auxclasspath:
auxclasspath = options.auxclasspath.split(':')
return _Run(exclude_file, known_bugs_file, options.only_analyze, auxclasspath,
options.rebaseline, options.release_build, options.findbug_args)
def GetCommonParser():
parser = optparse.OptionParser()
parser.add_option('-r',
'--rebaseline',
action='store_true',
dest='rebaseline',
help='Rebaseline known findbugs issues.')
parser.add_option('-a',
'--auxclasspath',
action='store',
default=None,
dest='auxclasspath',
help='Set aux classpath for analysis.')
parser.add_option('-o',
'--only-analyze',
action='store',
default=None,
dest='only_analyze',
help='Only analyze the given classes and packages.')
parser.add_option('-e',
'--exclude',
action='store',
default=None,
dest='exclude',
help='Exclude bugs matching given filter.')
parser.add_option('-k',
'--known-bugs',
action='store',
default=None,
dest='known_bugs',
help='Not report the bugs in the given file.')
parser.add_option('-l',
'--release-build',
action='store_true',
dest='release_build',
help='Analyze release build instead of debug.')
parser.add_option('-f',
'--findbug-args',
action='store',
default=None,
dest='findbug_args',
help='Additional findbug arguments.')
parser.add_option('-b',
'--base-dir',
action='store',
default=None,
dest='base_dir',
help='Base directory for configuration file.')
return parser
def CheckEnvironment():
if not (os.getenv('CHROME_SRC') and os.getenv('ANDROID_SDK_ROOT') and
os.getenv('ANDROID_SDK_VERSION')):
print 'Your build environment is not set up correctly.'
print 'Please source build/android/envsetup.sh.'
return False
return True
def main(argv):
parser = GetCommonParser()
options, _ = parser.parse_args()
return Run(options)
if __name__ == '__main__':
sys.exit(main(sys.argv))