Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / build / android / pylib / utils / findbugs.py
blobee2d5a72dca430ca222c9d1331593dbe8e5331d5
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.
5 import logging
6 import os
7 import xml.dom.minidom
9 from devil.utils import cmd_helper
10 from pylib import constants
13 _FINDBUGS_HOME = os.path.join(constants.DIR_SOURCE_ROOT, 'third_party',
14 'findbugs')
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',
19 'chromiumPlugin.jar')
22 def _ParseXmlResults(results_doc):
23 warnings = set()
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))
28 return warnings
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
38 return None
41 def _ParseBugInstance(node):
42 bug = FindBugsWarning(node.getAttribute('type'))
43 msg_parts = []
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)
63 return bug
66 class FindBugsWarning(object):
68 def __init__(self, bug_type='', end_line=0, file_name='', message=None,
69 start_line=0):
70 self.bug_type = bug_type
71 self.end_line = end_line
72 self.file_name = file_name
73 if message is None:
74 self.message = tuple()
75 else:
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__
89 def __hash__(self):
90 return hash((self.bug_type, self.end_line, self.file_name, self.message,
91 self.start_line))
93 def __ne__(self, other):
94 return not self == other
96 def __str__(self):
97 return '%s: %s' % (self.bug_type, '\n '.join(self.message))
100 def Run(exclude, classes_to_analyze, auxiliary_classes, output_file,
101 findbug_args, jars):
102 """Run FindBugs.
104 Args:
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.
114 system_classes = [
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 [])
121 cmd = ['java',
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']
128 if system_classes:
129 cmd.extend(['-auxclasspath', ':'.join(system_classes)])
130 if classes_to_analyze:
131 cmd.extend(['-onlyAnalyze', classes_to_analyze])
132 if exclude:
133 cmd.extend(['-exclude', os.path.abspath(exclude)])
134 if output_file:
135 cmd.extend(['-output', output_file])
136 if findbug_args:
137 cmd.extend(findbug_args)
138 cmd.extend(os.path.abspath(j) for j in jars or [])
140 if output_file:
141 _, _, stderr = cmd_helper.GetCmdStatusOutputAndError(cmd)
143 results_doc = xml.dom.minidom.parse(output_file)
144 else:
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)