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.
11 import xml
.dom
.minidom
13 from pylib
import cmd_helper
14 from pylib
import constants
17 _FINDBUGS_HOME
= os
.path
.join(constants
.DIR_SOURCE_ROOT
, 'third_party',
19 _FINDBUGS_JAR
= os
.path
.join(_FINDBUGS_HOME
, 'lib', 'findbugs.jar')
20 _FINDBUGS_MAX_HEAP
= 768
21 _FINDBUGS_PLUGIN_PATH
= os
.path
.join(
22 constants
.DIR_SOURCE_ROOT
, 'tools', 'android', 'findbugs_plugin', 'lib',
26 def _ParseXmlResults(results_doc
):
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
))
35 def _GetMessage(node
):
36 for c
in (n
for n
in node
.childNodes
37 if n
.nodeType
== xml
.dom
.Node
.ELEMENT_NODE
):
38 if c
.tagName
== 'Message':
39 if (len(c
.childNodes
) == 1
40 and c
.childNodes
[0].nodeType
== xml
.dom
.Node
.TEXT_NODE
):
41 return c
.childNodes
[0].data
45 def _ParseBugInstance(node
):
46 bug
= FindBugsWarning(node
.getAttribute('type'))
48 for c
in (n
for n
in node
.childNodes
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
)
70 class FindBugsWarning(object):
72 def __init__(self
, bug_type
='', end_line
=0, file_name
='', message
=None,
74 self
.bug_type
= bug_type
75 self
.end_line
= end_line
76 self
.file_name
= file_name
78 self
.message
= tuple()
80 self
.message
= message
81 self
.start_line
= start_line
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
))
90 def __eq__(self
, other
):
91 return self
.__dict
__ == other
.__dict
__
94 return hash((self
.bug_type
, self
.end_line
, self
.file_name
, self
.message
,
97 def __ne__(self
, other
):
98 return not self
== other
101 return '%s: %s' % (self
.bug_type
, '\n '.join(self
.message
))
104 def Run(exclude
, classes_to_analyze
, auxiliary_classes
, output_file
,
109 exclude: the exclude xml file, refer to FindBugs's -exclude command option.
110 classes_to_analyze: the list of classes need to analyze, refer to FindBug's
111 -onlyAnalyze command line option.
112 auxiliary_classes: the classes help to analyze, refer to FindBug's
113 -auxclasspath command line option.
114 output_file: An optional path to dump XML results to.
115 findbug_args: A list of addtional command line options to pass to Findbugs.
117 # TODO(jbudorick): Get this from the build system.
119 os
.path
.join(constants
.ANDROID_SDK_ROOT
, 'platforms',
120 'android-%s' % constants
.ANDROID_SDK_VERSION
, 'android.jar')
122 system_classes
.extend(os
.path
.abspath(classes
)
123 for classes
in auxiliary_classes
or [])
126 '-classpath', '%s:' % _FINDBUGS_JAR
,
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']
133 cmd
.extend(['-auxclasspath', ':'.join(system_classes
)])
134 if classes_to_analyze
:
135 cmd
.extend(['-onlyAnalyze', classes_to_analyze
])
137 cmd
.extend(['-exclude', os
.path
.abspath(exclude
)])
139 cmd
.extend(['-output', output_file
])
141 cmd
.extend(findbug_args
)
142 cmd
.extend(os
.path
.abspath(j
) for j
in jars
or [])
145 cmd_helper
.RunCmd(cmd
)
146 results_doc
= xml
.dom
.minidom
.parse(output_file
)
148 raw_out
= cmd_helper
.GetCmdOutput(cmd
)
149 results_doc
= xml
.dom
.minidom
.parseString(raw_out
)
151 current_warnings_set
= _ParseXmlResults(results_doc
)
153 return (' '.join(cmd
), current_warnings_set
)