OLD | NEW |
| (Empty) |
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. | |
4 | |
5 import argparse | |
6 import logging | |
7 import os | |
8 import re | |
9 import shlex | |
10 import sys | |
11 import xml.dom.minidom | |
12 | |
13 from pylib import cmd_helper | |
14 from pylib import constants | |
15 | |
16 | |
17 _FINDBUGS_HOME = os.path.join(constants.DIR_SOURCE_ROOT, 'third_party', | |
18 'findbugs') | |
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', | |
23 'chromiumPlugin.jar') | |
24 | |
25 | |
26 def _ParseXmlResults(results_doc): | |
27 warnings = set() | |
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 | |
33 | |
34 | |
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 | |
42 return None | |
43 | |
44 | |
45 def _ParseBugInstance(node): | |
46 bug = FindBugsWarning(node.getAttribute('type')) | |
47 msg_parts = [] | |
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) | |
67 return bug | |
68 | |
69 | |
70 class FindBugsWarning(object): | |
71 | |
72 def __init__(self, bug_type='', end_line=0, file_name='', message=None, | |
73 start_line=0): | |
74 self.bug_type = bug_type | |
75 self.end_line = end_line | |
76 self.file_name = file_name | |
77 if message is None: | |
78 self.message = tuple() | |
79 else: | |
80 self.message = message | |
81 self.start_line = start_line | |
82 | |
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)) | |
102 | |
103 | |
104 def Run(exclude, classes_to_analyze, auxiliary_classes, output_file, | |
105 findbug_args, jars): | |
106 """Run FindBugs. | |
107 | |
108 Args: | |
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. | |
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 []) | |
124 | |
125 cmd = ['java', | |
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'] | |
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 []) | |
143 | |
144 if output_file: | |
145 cmd_helper.RunCmd(cmd) | |
146 results_doc = xml.dom.minidom.parse(output_file) | |
147 else: | |
148 raw_out = cmd_helper.GetCmdOutput(cmd) | |
149 results_doc = xml.dom.minidom.parseString(raw_out) | |
150 | |
151 current_warnings_set = _ParseXmlResults(results_doc) | |
152 | |
153 return (' '.join(cmd), current_warnings_set) | |
154 | |
OLD | NEW |