Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / build / android / pylib / utils / findbugs.py
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 optparse
6 import os
7 import re
8 import shlex
9 import subprocess
10 import sys
11
12 from pylib import cmd_helper
13 from pylib import constants
14
15
16 def _PrintMessage(warnings, title, action, known_bugs_file):
17   if warnings:
18     print
19     print '*' * 80
20     print '%s warnings.' % title
21     print '%s %s' % (action, known_bugs_file)
22     print '-' * 80
23     for warning in warnings:
24       print warning
25     print '-' * 80
26     print
27
28
29 def _StripLineNumbers(current_warnings):
30   re_line = r':\[line.*?\]$'
31   return [re.sub(re_line, '', x) for x in current_warnings]
32
33
34 def _DiffKnownWarnings(current_warnings_set, known_bugs_file):
35   with open(known_bugs_file, 'r') as known_bugs:
36     known_bugs_set = set(known_bugs.read().splitlines())
37
38   new_warnings = current_warnings_set - known_bugs_set
39   _PrintMessage(sorted(new_warnings), 'New', 'Please fix, or perhaps add to',
40                 known_bugs_file)
41
42   obsolete_warnings = known_bugs_set - current_warnings_set
43   _PrintMessage(sorted(obsolete_warnings), 'Obsolete', 'Please remove from',
44                 known_bugs_file)
45
46   count = len(new_warnings) + len(obsolete_warnings)
47   if count:
48     print '*** %d FindBugs warning%s! ***' % (count, 's' * (count > 1))
49     if len(new_warnings):
50       print '*** %d: new ***' % len(new_warnings)
51     if len(obsolete_warnings):
52       print '*** %d: obsolete ***' % len(obsolete_warnings)
53     print
54     print 'Alternatively,  rebaseline with --rebaseline command option'
55     print
56   else:
57     print 'No new FindBugs warnings.'
58   print
59   return count
60
61
62 def _Rebaseline(current_warnings_set, known_bugs_file):
63   with file(known_bugs_file, 'w') as known_bugs:
64     for warning in sorted(current_warnings_set):
65       print >> known_bugs, warning
66   return 0
67
68
69 def _GetChromeClasses(release_version):
70   version = 'Debug'
71   if release_version:
72     version = 'Release'
73   path = os.path.join(constants.DIR_SOURCE_ROOT, 'out', version)
74   cmd = 'find %s -name "*.class"' % path
75   out = cmd_helper.GetCmdOutput(shlex.split(cmd))
76   if not out:
77     print 'No classes found in %s' % path
78   return out
79
80
81 def _Run(exclude, known_bugs, classes_to_analyze, auxiliary_classes,
82         rebaseline, release_version, findbug_args):
83   """Run the FindBugs.
84
85   Args:
86     exclude: the exclude xml file, refer to FindBugs's -exclude command option.
87     known_bugs: the text file of known bugs. The bugs in it will not be
88                 reported.
89     classes_to_analyze: the list of classes need to analyze, refer to FindBug's
90                         -onlyAnalyze command line option.
91     auxiliary_classes: the classes help to analyze, refer to FindBug's
92                        -auxclasspath command line option.
93     rebaseline: True if the known_bugs file needs rebaseline.
94     release_version: True if the release version needs check, otherwise check
95                      debug version.
96     findbug_args: addtional command line options needs pass to Findbugs.
97   """
98
99   chrome_src = constants.DIR_SOURCE_ROOT
100   sdk_root = constants.ANDROID_SDK_ROOT
101   sdk_version = constants.ANDROID_SDK_VERSION
102
103   system_classes = []
104   system_classes.append(os.path.join(sdk_root, 'platforms',
105                                      'android-%s' % sdk_version, 'android.jar'))
106   if auxiliary_classes:
107     for classes in auxiliary_classes:
108       system_classes.append(os.path.abspath(classes))
109
110   findbugs_javacmd = 'java'
111   findbugs_home = os.path.join(chrome_src, 'third_party', 'findbugs')
112   findbugs_jar = os.path.join(findbugs_home, 'lib', 'findbugs.jar')
113   findbugs_pathsep = ':'
114   findbugs_maxheap = '768'
115
116   cmd = '%s ' % findbugs_javacmd
117   cmd = '%s -classpath %s%s' % (cmd, findbugs_jar, findbugs_pathsep)
118   cmd = '%s -Xmx%sm ' % (cmd, findbugs_maxheap)
119   cmd = '%s -Dfindbugs.home="%s" ' % (cmd, findbugs_home)
120   cmd = '%s -jar %s ' % (cmd, findbugs_jar)
121
122   cmd = '%s -textui -sortByClass ' % cmd
123   cmd = '%s -pluginList %s' % (cmd, os.path.join(chrome_src, 'tools', 'android',
124                                                  'findbugs_plugin', 'lib',
125                                                  'chromiumPlugin.jar'))
126   if len(system_classes):
127     cmd = '%s -auxclasspath %s ' % (cmd, ':'.join(system_classes))
128
129   if classes_to_analyze:
130     cmd = '%s -onlyAnalyze %s ' % (cmd, classes_to_analyze)
131
132   if exclude:
133     cmd = '%s -exclude %s ' % (cmd, os.path.abspath(exclude))
134
135   if findbug_args:
136     cmd = '%s %s ' % (cmd, findbug_args)
137
138   chrome_classes = _GetChromeClasses(release_version)
139   if not chrome_classes:
140     return 1
141   cmd = '%s %s ' % (cmd, chrome_classes)
142
143   proc = subprocess.Popen(shlex.split(cmd),
144                           stdout=subprocess.PIPE, stderr=subprocess.PIPE)
145   out, _err = proc.communicate()
146   current_warnings_set = set(_StripLineNumbers(filter(None, out.splitlines())))
147
148   if rebaseline:
149     return _Rebaseline(current_warnings_set, known_bugs)
150   else:
151     return _DiffKnownWarnings(current_warnings_set, known_bugs)
152
153 def Run(options):
154   exclude_file = None
155   known_bugs_file = None
156
157   if options.exclude:
158     exclude_file = options.exclude
159   elif options.base_dir:
160     exclude_file = os.path.join(options.base_dir, 'findbugs_exclude.xml')
161
162   if options.known_bugs:
163     known_bugs_file = options.known_bugs
164   elif options.base_dir:
165     known_bugs_file = os.path.join(options.base_dir, 'findbugs_known_bugs.txt')
166
167   auxclasspath = None
168   if options.auxclasspath:
169     auxclasspath = options.auxclasspath.split(':')
170   return _Run(exclude_file, known_bugs_file, options.only_analyze, auxclasspath,
171               options.rebaseline, options.release_build, options.findbug_args)
172
173
174 def GetCommonParser():
175   parser = optparse.OptionParser()
176   parser.add_option('-r',
177                     '--rebaseline',
178                     action='store_true',
179                     dest='rebaseline',
180                     help='Rebaseline known findbugs issues.')
181
182   parser.add_option('-a',
183                     '--auxclasspath',
184                     action='store',
185                     default=None,
186                     dest='auxclasspath',
187                     help='Set aux classpath for analysis.')
188
189   parser.add_option('-o',
190                     '--only-analyze',
191                     action='store',
192                     default=None,
193                     dest='only_analyze',
194                     help='Only analyze the given classes and packages.')
195
196   parser.add_option('-e',
197                     '--exclude',
198                     action='store',
199                     default=None,
200                     dest='exclude',
201                     help='Exclude bugs matching given filter.')
202
203   parser.add_option('-k',
204                     '--known-bugs',
205                     action='store',
206                     default=None,
207                     dest='known_bugs',
208                     help='Not report the bugs in the given file.')
209
210   parser.add_option('-l',
211                     '--release-build',
212                     action='store_true',
213                     dest='release_build',
214                     help='Analyze release build instead of debug.')
215
216   parser.add_option('-f',
217                     '--findbug-args',
218                     action='store',
219                     default=None,
220                     dest='findbug_args',
221                     help='Additional findbug arguments.')
222
223   parser.add_option('-b',
224                     '--base-dir',
225                     action='store',
226                     default=None,
227                     dest='base_dir',
228                     help='Base directory for configuration file.')
229
230   return parser
231
232
233 def main():
234   parser = GetCommonParser()
235   options, _ = parser.parse_args()
236
237   return Run(options)
238
239
240 if __name__ == '__main__':
241   sys.exit(main())