- add sources.
[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   cmd = '%s -textui -sortByClass ' % os.path.join(chrome_src, 'third_party',
111                                                   'findbugs', 'bin', 'findbugs')
112   cmd = '%s -pluginList %s' % (cmd, os.path.join(chrome_src, 'tools', 'android',
113                                                  'findbugs_plugin', 'lib',
114                                                  'chromiumPlugin.jar'))
115   if len(system_classes):
116     cmd = '%s -auxclasspath %s ' % (cmd, ':'.join(system_classes))
117
118   if classes_to_analyze:
119     cmd = '%s -onlyAnalyze %s ' % (cmd, classes_to_analyze)
120
121   if exclude:
122     cmd = '%s -exclude %s ' % (cmd, os.path.abspath(exclude))
123
124   if findbug_args:
125     cmd = '%s %s ' % (cmd, findbug_args)
126
127
128   chrome_classes = _GetChromeClasses(release_version)
129   if not chrome_classes:
130     return 1
131   cmd = '%s %s ' % (cmd, chrome_classes)
132
133   proc = subprocess.Popen(shlex.split(cmd),
134                           stdout=subprocess.PIPE, stderr=subprocess.PIPE)
135   out, err = proc.communicate()
136   current_warnings_set = set(_StripLineNumbers(filter(None, out.splitlines())))
137
138   if rebaseline:
139     return _Rebaseline(current_warnings_set, known_bugs)
140   else:
141     return _DiffKnownWarnings(current_warnings_set, known_bugs)
142
143 def Run(options):
144   exclude_file = None
145   known_bugs_file = None
146
147   if options.exclude:
148     exclude_file = options.exclude
149   elif options.base_dir:
150     exclude_file = os.path.join(options.base_dir, 'findbugs_exclude.xml')
151
152   if options.known_bugs:
153     known_bugs_file = options.known_bugs
154   elif options.base_dir:
155     known_bugs_file = os.path.join(options.base_dir, 'findbugs_known_bugs.txt')
156
157   auxclasspath = None
158   if options.auxclasspath:
159     auxclasspath = options.auxclasspath.split(':')
160   return _Run(exclude_file, known_bugs_file, options.only_analyze, auxclasspath,
161               options.rebaseline, options.release_build, options.findbug_args)
162
163
164 def GetCommonParser():
165   parser = optparse.OptionParser()
166   parser.add_option('-r',
167                     '--rebaseline',
168                     action='store_true',
169                     dest='rebaseline',
170                     help='Rebaseline known findbugs issues.')
171
172   parser.add_option('-a',
173                     '--auxclasspath',
174                     action='store',
175                     default=None,
176                     dest='auxclasspath',
177                     help='Set aux classpath for analysis.')
178
179   parser.add_option('-o',
180                     '--only-analyze',
181                     action='store',
182                     default=None,
183                     dest='only_analyze',
184                     help='Only analyze the given classes and packages.')
185
186   parser.add_option('-e',
187                     '--exclude',
188                     action='store',
189                     default=None,
190                     dest='exclude',
191                     help='Exclude bugs matching given filter.')
192
193   parser.add_option('-k',
194                     '--known-bugs',
195                     action='store',
196                     default=None,
197                     dest='known_bugs',
198                     help='Not report the bugs in the given file.')
199
200   parser.add_option('-l',
201                     '--release-build',
202                     action='store_true',
203                     dest='release_build',
204                     help='Analyze release build instead of debug.')
205
206   parser.add_option('-f',
207                     '--findbug-args',
208                     action='store',
209                     default=None,
210                     dest='findbug_args',
211                     help='Additional findbug arguments.')
212
213   parser.add_option('-b',
214                     '--base-dir',
215                     action='store',
216                     default=None,
217                     dest='base_dir',
218                     help='Base directory for configuration file.')
219
220   return parser
221
222
223 def main(argv):
224   parser = GetCommonParser()
225   options, _ = parser.parse_args()
226
227   return Run(options)
228
229
230 if __name__ == '__main__':
231   sys.exit(main(sys.argv))