Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(996)

Side by Side Diff: build/android/pylib/utils/findbugs.py

Issue 1000793002: [Android] Incorporate findbugs into android builds. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address cjhopman's comment + rebase Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « build/android/findbugs_filter/findbugs_exclude.xml ('k') | build/config/android/config.gni » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import optparse 5 import argparse
6 import logging
6 import os 7 import os
7 import re 8 import re
8 import shlex 9 import shlex
9 import subprocess
10 import sys 10 import sys
11 import xml.dom.minidom
11 12
12 from pylib import cmd_helper 13 from pylib import cmd_helper
13 from pylib import constants 14 from pylib import constants
14 15
15 16
16 def _PrintMessage(warnings, title, action, known_bugs_file): 17 _FINDBUGS_HOME = os.path.join(constants.DIR_SOURCE_ROOT, 'third_party',
17 if warnings: 18 'findbugs')
18 print 19 _FINDBUGS_JAR = os.path.join(_FINDBUGS_HOME, 'lib', 'findbugs.jar')
19 print '*' * 80 20 _FINDBUGS_MAX_HEAP = 768
20 print '%s warnings.' % title 21 _FINDBUGS_PLUGIN_PATH = os.path.join(
21 print '%s %s' % (action, known_bugs_file) 22 constants.DIR_SOURCE_ROOT, 'tools', 'android', 'findbugs_plugin', 'lib',
22 print '-' * 80 23 'chromiumPlugin.jar')
23 for warning in warnings:
24 print warning
25 print '-' * 80
26 print
27 24
28 25
29 def _StripLineNumbers(current_warnings): 26 def _ParseXmlResults(results_doc):
30 re_line = r':\[line.*?\]$' 27 warnings = set()
31 return [re.sub(re_line, '', x) for x in current_warnings] 28 for en in (n for n in results_doc.documentElement.childNodes
29 if n.nodeType == xml.dom.Node.ELEMENT_NODE):
30 if en.tagName == 'BugInstance':
31 warnings.add(_ParseBugInstance(en))
32 return warnings
32 33
33 34
34 def _DiffKnownWarnings(current_warnings_set, known_bugs_file): 35 def _GetMessage(node):
35 if os.path.exists(known_bugs_file): 36 for c in (n for n in node.childNodes
36 with open(known_bugs_file, 'r') as known_bugs: 37 if n.nodeType == xml.dom.Node.ELEMENT_NODE):
37 known_bugs_set = set(known_bugs.read().splitlines()) 38 if c.tagName == 'Message':
38 else: 39 if (len(c.childNodes) == 1
39 known_bugs_set = set() 40 and c.childNodes[0].nodeType == xml.dom.Node.TEXT_NODE):
40 41 return c.childNodes[0].data
41 new_warnings = current_warnings_set - known_bugs_set 42 return None
42 _PrintMessage(sorted(new_warnings), 'New', 'Please fix, or perhaps add to',
43 known_bugs_file)
44
45 obsolete_warnings = known_bugs_set - current_warnings_set
46 _PrintMessage(sorted(obsolete_warnings), 'Obsolete', 'Please remove from',
47 known_bugs_file)
48
49 count = len(new_warnings) + len(obsolete_warnings)
50 if count:
51 print '*** %d FindBugs warning%s! ***' % (count, 's' * (count > 1))
52 if len(new_warnings):
53 print '*** %d: new ***' % len(new_warnings)
54 if len(obsolete_warnings):
55 print '*** %d: obsolete ***' % len(obsolete_warnings)
56 print
57 print 'Alternatively, rebaseline with --rebaseline command option'
58 print
59 else:
60 print 'No new FindBugs warnings.'
61 print
62 return count
63 43
64 44
65 def _Rebaseline(current_warnings_set, known_bugs_file): 45 def _ParseBugInstance(node):
66 with file(known_bugs_file, 'w') as known_bugs: 46 bug = FindBugsWarning(node.getAttribute('type'))
67 for warning in sorted(current_warnings_set): 47 msg_parts = []
68 print >> known_bugs, warning 48 for c in (n for n in node.childNodes
69 return 0 49 if n.nodeType == xml.dom.Node.ELEMENT_NODE):
50 if c.tagName == 'Class':
51 msg_parts.append(_GetMessage(c))
52 elif c.tagName == 'Method':
53 msg_parts.append(_GetMessage(c))
54 elif c.tagName == 'Field':
55 msg_parts.append(_GetMessage(c))
56 elif c.tagName == 'SourceLine':
57 bug.file_name = c.getAttribute('sourcefile')
58 if c.hasAttribute('start'):
59 bug.start_line = int(c.getAttribute('start'))
60 if c.hasAttribute('end'):
61 bug.end_line = int(c.getAttribute('end'))
62 msg_parts.append(_GetMessage(c))
63 elif (c.tagName == 'ShortMessage' and len(c.childNodes) == 1
64 and c.childNodes[0].nodeType == xml.dom.Node.TEXT_NODE):
65 msg_parts.append(c.childNodes[0].data)
66 bug.message = tuple(m for m in msg_parts if m)
67 return bug
70 68
71 69
72 def _GetChromeJars(release_version): 70 class FindBugsWarning(object):
73 version = 'Debug' 71
74 if release_version: 72 def __init__(self, bug_type='', end_line=0, file_name='', message=None,
75 version = 'Release' 73 start_line=0):
76 path = os.path.join(constants.DIR_SOURCE_ROOT, 74 self.bug_type = bug_type
77 os.environ.get('CHROMIUM_OUT_DIR', 'out'), 75 self.end_line = end_line
78 version, 76 self.file_name = file_name
79 'lib.java') 77 if message is None:
80 cmd = 'find %s -name "*.jar"' % path 78 self.message = tuple()
81 out = cmd_helper.GetCmdOutput(shlex.split(cmd)) 79 else:
82 out = [p for p in out.splitlines() if not p.endswith('.dex.jar')] 80 self.message = message
83 if not out: 81 self.start_line = start_line
84 print 'No classes found in %s' % path 82
85 return ' '.join(out) 83 def __cmp__(self, other):
84 return (cmp(self.file_name, other.file_name)
85 or cmp(self.start_line, other.start_line)
86 or cmp(self.end_line, other.end_line)
87 or cmp(self.bug_type, other.bug_type)
88 or cmp(self.message, other.message))
89
90 def __eq__(self, other):
91 return self.__dict__ == other.__dict__
92
93 def __hash__(self):
94 return hash((self.bug_type, self.end_line, self.file_name, self.message,
95 self.start_line))
96
97 def __ne__(self, other):
98 return not self == other
99
100 def __str__(self):
101 return '%s: %s' % (self.bug_type, '\n '.join(self.message))
86 102
87 103
88 def _Run(exclude, known_bugs, classes_to_analyze, auxiliary_classes, 104 def Run(exclude, classes_to_analyze, auxiliary_classes, output_file,
89 rebaseline, release_version, findbug_args): 105 findbug_args, jars):
90 """Run the FindBugs. 106 """Run FindBugs.
91 107
92 Args: 108 Args:
93 exclude: the exclude xml file, refer to FindBugs's -exclude command option. 109 exclude: the exclude xml file, refer to FindBugs's -exclude command option.
94 known_bugs: the text file of known bugs. The bugs in it will not be
95 reported.
96 classes_to_analyze: the list of classes need to analyze, refer to FindBug's 110 classes_to_analyze: the list of classes need to analyze, refer to FindBug's
97 -onlyAnalyze command line option. 111 -onlyAnalyze command line option.
98 auxiliary_classes: the classes help to analyze, refer to FindBug's 112 auxiliary_classes: the classes help to analyze, refer to FindBug's
99 -auxclasspath command line option. 113 -auxclasspath command line option.
100 rebaseline: True if the known_bugs file needs rebaseline. 114 output_file: An optional path to dump XML results to.
101 release_version: True if the release version needs check, otherwise check 115 findbug_args: A list of addtional command line options to pass to Findbugs.
102 debug version.
103 findbug_args: addtional command line options needs pass to Findbugs.
104 """ 116 """
117 # TODO(jbudorick): Get this from the build system.
118 system_classes = [
119 os.path.join(constants.ANDROID_SDK_ROOT, 'platforms',
120 'android-%s' % constants.ANDROID_SDK_VERSION, 'android.jar')
121 ]
122 system_classes.extend(os.path.abspath(classes)
123 for classes in auxiliary_classes or [])
105 124
106 chrome_src = constants.DIR_SOURCE_ROOT 125 cmd = ['java',
107 sdk_root = constants.ANDROID_SDK_ROOT 126 '-classpath', '%s:' % _FINDBUGS_JAR,
108 sdk_version = constants.ANDROID_SDK_VERSION 127 '-Xmx%dm' % _FINDBUGS_MAX_HEAP,
128 '-Dfindbugs.home="%s"' % _FINDBUGS_HOME,
129 '-jar', _FINDBUGS_JAR,
130 '-textui', '-sortByClass',
131 '-pluginList', _FINDBUGS_PLUGIN_PATH, '-xml:withMessages']
132 if system_classes:
133 cmd.extend(['-auxclasspath', ':'.join(system_classes)])
134 if classes_to_analyze:
135 cmd.extend(['-onlyAnalyze', classes_to_analyze])
136 if exclude:
137 cmd.extend(['-exclude', os.path.abspath(exclude)])
138 if output_file:
139 cmd.extend(['-output', output_file])
140 if findbug_args:
141 cmd.extend(findbug_args)
142 cmd.extend(os.path.abspath(j) for j in jars or [])
109 143
110 system_classes = [] 144 if output_file:
111 system_classes.append(os.path.join(sdk_root, 'platforms', 145 cmd_helper.RunCmd(cmd)
112 'android-%s' % sdk_version, 'android.jar')) 146 results_doc = xml.dom.minidom.parse(output_file)
113 if auxiliary_classes: 147 else:
114 for classes in auxiliary_classes: 148 raw_out = cmd_helper.GetCmdOutput(cmd)
115 system_classes.append(os.path.abspath(classes)) 149 results_doc = xml.dom.minidom.parseString(raw_out)
116 150
117 findbugs_javacmd = 'java' 151 current_warnings_set = _ParseXmlResults(results_doc)
118 findbugs_home = os.path.join(chrome_src, 'third_party', 'findbugs')
119 findbugs_jar = os.path.join(findbugs_home, 'lib', 'findbugs.jar')
120 findbugs_pathsep = ':'
121 findbugs_maxheap = '768'
122 152
123 cmd = '%s ' % findbugs_javacmd 153 return (' '.join(cmd), current_warnings_set)
124 cmd = '%s -classpath %s%s' % (cmd, findbugs_jar, findbugs_pathsep)
125 cmd = '%s -Xmx%sm ' % (cmd, findbugs_maxheap)
126 cmd = '%s -Dfindbugs.home="%s" ' % (cmd, findbugs_home)
127 cmd = '%s -jar %s ' % (cmd, findbugs_jar)
128 154
129 cmd = '%s -textui -sortByClass ' % cmd
130 cmd = '%s -pluginList %s' % (cmd, os.path.join(chrome_src, 'tools', 'android',
131 'findbugs_plugin', 'lib',
132 'chromiumPlugin.jar'))
133 if len(system_classes):
134 cmd = '%s -auxclasspath %s ' % (cmd, ':'.join(system_classes))
135
136 if classes_to_analyze:
137 cmd = '%s -onlyAnalyze %s ' % (cmd, classes_to_analyze)
138
139 if exclude:
140 cmd = '%s -exclude %s ' % (cmd, os.path.abspath(exclude))
141
142 if findbug_args:
143 cmd = '%s %s ' % (cmd, findbug_args)
144
145 chrome_classes = _GetChromeJars(release_version)
146 if not chrome_classes:
147 return 1
148 cmd = '%s %s ' % (cmd, chrome_classes)
149
150 print
151 print '*' * 80
152 print 'Command used to run findbugs:'
153 print cmd
154 print '*' * 80
155 print
156
157 proc = subprocess.Popen(shlex.split(cmd),
158 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
159 out, _err = proc.communicate()
160 current_warnings_set = set(_StripLineNumbers(filter(None, out.splitlines())))
161
162 if rebaseline:
163 return _Rebaseline(current_warnings_set, known_bugs)
164 else:
165 return _DiffKnownWarnings(current_warnings_set, known_bugs)
166
167 def Run(options):
168 exclude_file = None
169 known_bugs_file = None
170
171 if options.exclude:
172 exclude_file = options.exclude
173 elif options.base_dir:
174 exclude_file = os.path.join(options.base_dir, 'findbugs_exclude.xml')
175
176 if options.known_bugs:
177 known_bugs_file = options.known_bugs
178 elif options.base_dir:
179 known_bugs_file = os.path.join(options.base_dir, 'findbugs_known_bugs.txt')
180
181 auxclasspath = None
182 if options.auxclasspath:
183 auxclasspath = options.auxclasspath.split(':')
184 return _Run(exclude_file, known_bugs_file, options.only_analyze, auxclasspath,
185 options.rebaseline, options.release_build, options.findbug_args)
186
187
188 def GetCommonParser():
189 parser = optparse.OptionParser()
190 parser.add_option('-r',
191 '--rebaseline',
192 action='store_true',
193 dest='rebaseline',
194 help='Rebaseline known findbugs issues.')
195
196 parser.add_option('-a',
197 '--auxclasspath',
198 action='store',
199 default=None,
200 dest='auxclasspath',
201 help='Set aux classpath for analysis.')
202
203 parser.add_option('-o',
204 '--only-analyze',
205 action='store',
206 default=None,
207 dest='only_analyze',
208 help='Only analyze the given classes and packages.')
209
210 parser.add_option('-e',
211 '--exclude',
212 action='store',
213 default=None,
214 dest='exclude',
215 help='Exclude bugs matching given filter.')
216
217 parser.add_option('-k',
218 '--known-bugs',
219 action='store',
220 default=None,
221 dest='known_bugs',
222 help='Not report the bugs in the given file.')
223
224 parser.add_option('-l',
225 '--release-build',
226 action='store_true',
227 dest='release_build',
228 help='Analyze release build instead of debug.')
229
230 parser.add_option('-f',
231 '--findbug-args',
232 action='store',
233 default=None,
234 dest='findbug_args',
235 help='Additional findbug arguments.')
236
237 parser.add_option('-b',
238 '--base-dir',
239 action='store',
240 default=None,
241 dest='base_dir',
242 help='Base directory for configuration file.')
243
244 return parser
245
246
247 def main():
248 parser = GetCommonParser()
249 options, _ = parser.parse_args()
250
251 return Run(options)
252
253
254 if __name__ == '__main__':
255 sys.exit(main())
OLDNEW
« no previous file with comments | « build/android/findbugs_filter/findbugs_exclude.xml ('k') | build/config/android/config.gni » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698