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.
9 from devil
.utils
import cmd_helper
10 from pylib
import constants
13 _FINDBUGS_HOME
= os
.path
.join(constants
.DIR_SOURCE_ROOT
, 'third_party',
15 _FINDBUGS_JAR
= os
.path
.join(_FINDBUGS_HOME
, 'lib', 'findbugs.jar')
16 _FINDBUGS_MAX_HEAP
= 768
17 _FINDBUGS_PLUGIN_PATH
= os
.path
.join(
18 constants
.DIR_SOURCE_ROOT
, 'tools', 'android', 'findbugs_plugin', 'lib',
22 def _ParseXmlResults(results_doc
):
24 for en
in (n
for n
in results_doc
.documentElement
.childNodes
25 if n
.nodeType
== xml
.dom
.Node
.ELEMENT_NODE
):
26 if en
.tagName
== 'BugInstance':
27 warnings
.add(_ParseBugInstance(en
))
31 def _GetMessage(node
):
32 for c
in (n
for n
in node
.childNodes
33 if n
.nodeType
== xml
.dom
.Node
.ELEMENT_NODE
):
34 if c
.tagName
== 'Message':
35 if (len(c
.childNodes
) == 1
36 and c
.childNodes
[0].nodeType
== xml
.dom
.Node
.TEXT_NODE
):
37 return c
.childNodes
[0].data
41 def _ParseBugInstance(node
):
42 bug
= FindBugsWarning(node
.getAttribute('type'))
44 for c
in (n
for n
in node
.childNodes
45 if n
.nodeType
== xml
.dom
.Node
.ELEMENT_NODE
):
46 if c
.tagName
== 'Class':
47 msg_parts
.append(_GetMessage(c
))
48 elif c
.tagName
== 'Method':
49 msg_parts
.append(_GetMessage(c
))
50 elif c
.tagName
== 'Field':
51 msg_parts
.append(_GetMessage(c
))
52 elif c
.tagName
== 'SourceLine':
53 bug
.file_name
= c
.getAttribute('sourcefile')
54 if c
.hasAttribute('start'):
55 bug
.start_line
= int(c
.getAttribute('start'))
56 if c
.hasAttribute('end'):
57 bug
.end_line
= int(c
.getAttribute('end'))
58 msg_parts
.append(_GetMessage(c
))
59 elif (c
.tagName
== 'ShortMessage' and len(c
.childNodes
) == 1
60 and c
.childNodes
[0].nodeType
== xml
.dom
.Node
.TEXT_NODE
):
61 msg_parts
.append(c
.childNodes
[0].data
)
62 bug
.message
= tuple(m
for m
in msg_parts
if m
)
66 class FindBugsWarning(object):
68 def __init__(self
, bug_type
='', end_line
=0, file_name
='', message
=None,
70 self
.bug_type
= bug_type
71 self
.end_line
= end_line
72 self
.file_name
= file_name
74 self
.message
= tuple()
76 self
.message
= message
77 self
.start_line
= start_line
79 def __cmp__(self
, other
):
80 return (cmp(self
.file_name
, other
.file_name
)
81 or cmp(self
.start_line
, other
.start_line
)
82 or cmp(self
.end_line
, other
.end_line
)
83 or cmp(self
.bug_type
, other
.bug_type
)
84 or cmp(self
.message
, other
.message
))
86 def __eq__(self
, other
):
87 return self
.__dict
__ == other
.__dict
__
90 return hash((self
.bug_type
, self
.end_line
, self
.file_name
, self
.message
,
93 def __ne__(self
, other
):
94 return not self
== other
97 return '%s: %s' % (self
.bug_type
, '\n '.join(self
.message
))
100 def Run(exclude
, classes_to_analyze
, auxiliary_classes
, output_file
,
105 exclude: the exclude xml file, refer to FindBugs's -exclude command option.
106 classes_to_analyze: the list of classes need to analyze, refer to FindBug's
107 -onlyAnalyze command line option.
108 auxiliary_classes: the classes help to analyze, refer to FindBug's
109 -auxclasspath command line option.
110 output_file: An optional path to dump XML results to.
111 findbug_args: A list of addtional command line options to pass to Findbugs.
113 # TODO(jbudorick): Get this from the build system.
115 os
.path
.join(constants
.ANDROID_SDK_ROOT
, 'platforms',
116 'android-%s' % constants
.ANDROID_SDK_VERSION
, 'android.jar')
118 system_classes
.extend(os
.path
.abspath(classes
)
119 for classes
in auxiliary_classes
or [])
122 '-classpath', '%s:' % _FINDBUGS_JAR
,
123 '-Xmx%dm' % _FINDBUGS_MAX_HEAP
,
124 '-Dfindbugs.home="%s"' % _FINDBUGS_HOME
,
125 '-jar', _FINDBUGS_JAR
,
126 '-textui', '-sortByClass',
127 '-pluginList', _FINDBUGS_PLUGIN_PATH
, '-xml:withMessages']
129 cmd
.extend(['-auxclasspath', ':'.join(system_classes
)])
130 if classes_to_analyze
:
131 cmd
.extend(['-onlyAnalyze', classes_to_analyze
])
133 cmd
.extend(['-exclude', os
.path
.abspath(exclude
)])
135 cmd
.extend(['-output', output_file
])
137 cmd
.extend(findbug_args
)
138 cmd
.extend(os
.path
.abspath(j
) for j
in jars
or [])
141 _
, _
, stderr
= cmd_helper
.GetCmdStatusOutputAndError(cmd
)
143 results_doc
= xml
.dom
.minidom
.parse(output_file
)
145 _
, raw_out
, stderr
= cmd_helper
.GetCmdStatusOutputAndError(cmd
)
146 results_doc
= xml
.dom
.minidom
.parseString(raw_out
)
148 for line
in stderr
.splitlines():
149 logging
.debug(' %s', line
)
151 current_warnings_set
= _ParseXmlResults(results_doc
)
153 return (' '.join(cmd
), current_warnings_set
)