ctdb-daemon: Use ctdb_parse_node_address() in ctdbd
[samba4-gss.git] / third_party / waf / waflib / extras / cppcheck.py
blob13ff42477fd4b28403295fb57c390ff73f2d397b
1 #! /usr/bin/env python
2 # -*- encoding: utf-8 -*-
3 # Michel Mooij, michel.mooij7@gmail.com
5 """
6 Tool Description
7 ================
8 This module provides a waf wrapper (i.e. waftool) around the C/C++ source code
9 checking tool 'cppcheck'.
11 See http://cppcheck.sourceforge.net/ for more information on the cppcheck tool
12 itself.
13 Note that many linux distributions already provide a ready to install version
14 of cppcheck. On fedora, for instance, it can be installed using yum:
16 'sudo yum install cppcheck'
19 Usage
20 =====
21 In order to use this waftool simply add it to the 'options' and 'configure'
22 functions of your main waf script as shown in the example below:
24 def options(opt):
25 opt.load('cppcheck', tooldir='./waftools')
27 def configure(conf):
28 conf.load('cppcheck')
30 Note that example shown above assumes that the cppcheck waftool is located in
31 the sub directory named 'waftools'.
33 When configured as shown in the example above, cppcheck will automatically
34 perform a source code analysis on all C/C++ build tasks that have been
35 defined in your waf build system.
37 The example shown below for a C program will be used as input for cppcheck when
38 building the task.
40 def build(bld):
41 bld.program(name='foo', src='foobar.c')
43 The result of the source code analysis will be stored both as xml and html
44 files in the build location for the task. Should any error be detected by
45 cppcheck the build will be aborted and a link to the html report will be shown.
46 By default, one index.html file is created for each task generator. A global
47 index.html file can be obtained by setting the following variable
48 in the configuration section:
50 conf.env.CPPCHECK_SINGLE_HTML = False
52 When needed source code checking by cppcheck can be disabled per task, per
53 detected error or warning for a particular task. It can be also be disabled for
54 all tasks.
56 In order to exclude a task from source code checking add the skip option to the
57 task as shown below:
59 def build(bld):
60 bld.program(
61 name='foo',
62 src='foobar.c'
63 cppcheck_skip=True
66 When needed problems detected by cppcheck may be suppressed using a file
67 containing a list of suppression rules. The relative or absolute path to this
68 file can be added to the build task as shown in the example below:
70 bld.program(
71 name='bar',
72 src='foobar.c',
73 cppcheck_suppress='bar.suppress'
76 A cppcheck suppress file should contain one suppress rule per line. Each of
77 these rules will be passed as an '--suppress=<rule>' argument to cppcheck.
79 Dependencies
80 ================
81 This waftool depends on the python pygments module, it is used for source code
82 syntax highlighting when creating the html reports. see http://pygments.org/ for
83 more information on this package.
85 Remarks
86 ================
87 The generation of the html report is originally based on the cppcheck-htmlreport.py
88 script that comes shipped with the cppcheck tool.
89 """
91 import sys
92 import xml.etree.ElementTree as ElementTree
93 from waflib import Task, TaskGen, Logs, Context, Options
95 PYGMENTS_EXC_MSG= '''
96 The required module 'pygments' could not be found. Please install it using your
97 platform package manager (e.g. apt-get or yum), using 'pip' or 'easy_install',
98 see 'http://pygments.org/download/' for installation instructions.
99 '''
101 try:
102 import pygments
103 from pygments import formatters, lexers
104 except ImportError as e:
105 Logs.warn(PYGMENTS_EXC_MSG)
106 raise e
109 def options(opt):
110 opt.add_option('--cppcheck-skip', dest='cppcheck_skip',
111 default=False, action='store_true',
112 help='do not check C/C++ sources (default=False)')
114 opt.add_option('--cppcheck-err-resume', dest='cppcheck_err_resume',
115 default=False, action='store_true',
116 help='continue in case of errors (default=False)')
118 opt.add_option('--cppcheck-bin-enable', dest='cppcheck_bin_enable',
119 default='warning,performance,portability,style,unusedFunction', action='store',
120 help="cppcheck option '--enable=' for binaries (default=warning,performance,portability,style,unusedFunction)")
122 opt.add_option('--cppcheck-lib-enable', dest='cppcheck_lib_enable',
123 default='warning,performance,portability,style', action='store',
124 help="cppcheck option '--enable=' for libraries (default=warning,performance,portability,style)")
126 opt.add_option('--cppcheck-std-c', dest='cppcheck_std_c',
127 default='c99', action='store',
128 help='cppcheck standard to use when checking C (default=c99)')
130 opt.add_option('--cppcheck-std-cxx', dest='cppcheck_std_cxx',
131 default='c++03', action='store',
132 help='cppcheck standard to use when checking C++ (default=c++03)')
134 opt.add_option('--cppcheck-check-config', dest='cppcheck_check_config',
135 default=False, action='store_true',
136 help='forced check for missing buildin include files, e.g. stdio.h (default=False)')
138 opt.add_option('--cppcheck-max-configs', dest='cppcheck_max_configs',
139 default='20', action='store',
140 help='maximum preprocessor (--max-configs) define iterations (default=20)')
142 opt.add_option('--cppcheck-jobs', dest='cppcheck_jobs',
143 default='1', action='store',
144 help='number of jobs (-j) to do the checking work (default=1)')
146 def configure(conf):
147 if conf.options.cppcheck_skip:
148 conf.env.CPPCHECK_SKIP = [True]
149 conf.env.CPPCHECK_STD_C = conf.options.cppcheck_std_c
150 conf.env.CPPCHECK_STD_CXX = conf.options.cppcheck_std_cxx
151 conf.env.CPPCHECK_MAX_CONFIGS = conf.options.cppcheck_max_configs
152 conf.env.CPPCHECK_BIN_ENABLE = conf.options.cppcheck_bin_enable
153 conf.env.CPPCHECK_LIB_ENABLE = conf.options.cppcheck_lib_enable
154 conf.env.CPPCHECK_JOBS = conf.options.cppcheck_jobs
155 if conf.options.cppcheck_jobs != '1' and ('unusedFunction' in conf.options.cppcheck_bin_enable or 'unusedFunction' in conf.options.cppcheck_lib_enable or 'all' in conf.options.cppcheck_bin_enable or 'all' in conf.options.cppcheck_lib_enable):
156 Logs.warn('cppcheck: unusedFunction cannot be used with multiple threads, cppcheck will disable it automatically')
157 conf.find_program('cppcheck', var='CPPCHECK')
159 # set to True to get a single index.html file
160 conf.env.CPPCHECK_SINGLE_HTML = False
162 @TaskGen.feature('c')
163 @TaskGen.feature('cxx')
164 def cppcheck_execute(self):
165 if hasattr(self.bld, 'conf'):
166 return
167 if len(self.env.CPPCHECK_SKIP) or Options.options.cppcheck_skip:
168 return
169 if getattr(self, 'cppcheck_skip', False):
170 return
171 task = self.create_task('cppcheck')
172 task.cmd = _tgen_create_cmd(self)
173 task.fatal = []
174 if not Options.options.cppcheck_err_resume:
175 task.fatal.append('error')
178 def _tgen_create_cmd(self):
179 features = getattr(self, 'features', [])
180 std_c = self.env.CPPCHECK_STD_C
181 std_cxx = self.env.CPPCHECK_STD_CXX
182 max_configs = self.env.CPPCHECK_MAX_CONFIGS
183 bin_enable = self.env.CPPCHECK_BIN_ENABLE
184 lib_enable = self.env.CPPCHECK_LIB_ENABLE
185 jobs = self.env.CPPCHECK_JOBS
187 cmd = self.env.CPPCHECK
188 args = ['--inconclusive','--report-progress','--verbose','--xml','--xml-version=2']
189 args.append('--max-configs=%s' % max_configs)
190 args.append('-j %s' % jobs)
192 if 'cxx' in features:
193 args.append('--language=c++')
194 args.append('--std=%s' % std_cxx)
195 else:
196 args.append('--language=c')
197 args.append('--std=%s' % std_c)
199 if Options.options.cppcheck_check_config:
200 args.append('--check-config')
202 if set(['cprogram','cxxprogram']) & set(features):
203 args.append('--enable=%s' % bin_enable)
204 else:
205 args.append('--enable=%s' % lib_enable)
207 for src in self.to_list(getattr(self, 'source', [])):
208 if not isinstance(src, str):
209 src = repr(src)
210 args.append(src)
211 for inc in self.to_incnodes(self.to_list(getattr(self, 'includes', []))):
212 if not isinstance(inc, str):
213 inc = repr(inc)
214 args.append('-I%s' % inc)
215 for inc in self.to_incnodes(self.to_list(self.env.INCLUDES)):
216 if not isinstance(inc, str):
217 inc = repr(inc)
218 args.append('-I%s' % inc)
219 return cmd + args
222 class cppcheck(Task.Task):
223 quiet = True
225 def run(self):
226 stderr = self.generator.bld.cmd_and_log(self.cmd, quiet=Context.STDERR, output=Context.STDERR)
227 self._save_xml_report(stderr)
228 defects = self._get_defects(stderr)
229 index = self._create_html_report(defects)
230 self._errors_evaluate(defects, index)
231 return 0
233 def _save_xml_report(self, s):
234 '''use cppcheck xml result string, add the command string used to invoke cppcheck
235 and save as xml file.
237 header = '%s\n' % s.splitlines()[0]
238 root = ElementTree.fromstring(s)
239 cmd = ElementTree.SubElement(root.find('cppcheck'), 'cmd')
240 cmd.text = str(self.cmd)
241 body = ElementTree.tostring(root).decode('us-ascii')
242 body_html_name = 'cppcheck-%s.xml' % self.generator.get_name()
243 if self.env.CPPCHECK_SINGLE_HTML:
244 body_html_name = 'cppcheck.xml'
245 node = self.generator.path.get_bld().find_or_declare(body_html_name)
246 node.write(header + body)
248 def _get_defects(self, xml_string):
249 '''evaluate the xml string returned by cppcheck (on sdterr) and use it to create
250 a list of defects.
252 defects = []
253 for error in ElementTree.fromstring(xml_string).iter('error'):
254 defect = {}
255 defect['id'] = error.get('id')
256 defect['severity'] = error.get('severity')
257 defect['msg'] = str(error.get('msg')).replace('<','&lt;')
258 defect['verbose'] = error.get('verbose')
259 for location in error.findall('location'):
260 defect['file'] = location.get('file')
261 defect['line'] = str(int(location.get('line')) - 1)
262 defects.append(defect)
263 return defects
265 def _create_html_report(self, defects):
266 files, css_style_defs = self._create_html_files(defects)
267 index = self._create_html_index(files)
268 self._create_css_file(css_style_defs)
269 return index
271 def _create_html_files(self, defects):
272 sources = {}
273 defects = [defect for defect in defects if 'file' in defect]
274 for defect in defects:
275 name = defect['file']
276 if not name in sources:
277 sources[name] = [defect]
278 else:
279 sources[name].append(defect)
281 files = {}
282 css_style_defs = None
283 bpath = self.generator.path.get_bld().abspath()
284 names = list(sources.keys())
285 for i in range(0,len(names)):
286 name = names[i]
287 if self.env.CPPCHECK_SINGLE_HTML:
288 htmlfile = 'cppcheck/%i.html' % (i)
289 else:
290 htmlfile = 'cppcheck/%s%i.html' % (self.generator.get_name(),i)
291 errors = sources[name]
292 files[name] = { 'htmlfile': '%s/%s' % (bpath, htmlfile), 'errors': errors }
293 css_style_defs = self._create_html_file(name, htmlfile, errors)
294 return files, css_style_defs
296 def _create_html_file(self, sourcefile, htmlfile, errors):
297 name = self.generator.get_name()
298 root = ElementTree.fromstring(CPPCHECK_HTML_FILE)
299 title = root.find('head/title')
300 title.text = 'cppcheck - report - %s' % name
302 body = root.find('body')
303 for div in body.findall('div'):
304 if div.get('id') == 'page':
305 page = div
306 break
307 for div in page.findall('div'):
308 if div.get('id') == 'header':
309 h1 = div.find('h1')
310 h1.text = 'cppcheck report - %s' % name
311 if div.get('id') == 'menu':
312 indexlink = div.find('a')
313 if self.env.CPPCHECK_SINGLE_HTML:
314 indexlink.attrib['href'] = 'index.html'
315 else:
316 indexlink.attrib['href'] = 'index-%s.html' % name
317 if div.get('id') == 'content':
318 content = div
319 srcnode = self.generator.bld.root.find_node(sourcefile)
320 hl_lines = [e['line'] for e in errors if 'line' in e]
321 formatter = CppcheckHtmlFormatter(linenos=True, style='colorful', hl_lines=hl_lines, lineanchors='line')
322 formatter.errors = [e for e in errors if 'line' in e]
323 css_style_defs = formatter.get_style_defs('.highlight')
324 lexer = pygments.lexers.guess_lexer_for_filename(sourcefile, "")
325 s = pygments.highlight(srcnode.read(), lexer, formatter)
326 table = ElementTree.fromstring(s)
327 content.append(table)
329 s = ElementTree.tostring(root, method='html').decode('us-ascii')
330 s = CCPCHECK_HTML_TYPE + s
331 node = self.generator.path.get_bld().find_or_declare(htmlfile)
332 node.write(s)
333 return css_style_defs
335 def _create_html_index(self, files):
336 name = self.generator.get_name()
337 root = ElementTree.fromstring(CPPCHECK_HTML_FILE)
338 title = root.find('head/title')
339 title.text = 'cppcheck - report - %s' % name
341 body = root.find('body')
342 for div in body.findall('div'):
343 if div.get('id') == 'page':
344 page = div
345 break
346 for div in page.findall('div'):
347 if div.get('id') == 'header':
348 h1 = div.find('h1')
349 h1.text = 'cppcheck report - %s' % name
350 if div.get('id') == 'content':
351 content = div
352 self._create_html_table(content, files)
353 if div.get('id') == 'menu':
354 indexlink = div.find('a')
355 if self.env.CPPCHECK_SINGLE_HTML:
356 indexlink.attrib['href'] = 'index.html'
357 else:
358 indexlink.attrib['href'] = 'index-%s.html' % name
360 s = ElementTree.tostring(root, method='html').decode('us-ascii')
361 s = CCPCHECK_HTML_TYPE + s
362 index_html_name = 'cppcheck/index-%s.html' % name
363 if self.env.CPPCHECK_SINGLE_HTML:
364 index_html_name = 'cppcheck/index.html'
365 node = self.generator.path.get_bld().find_or_declare(index_html_name)
366 node.write(s)
367 return node
369 def _create_html_table(self, content, files):
370 table = ElementTree.fromstring(CPPCHECK_HTML_TABLE)
371 for name, val in files.items():
372 f = val['htmlfile']
373 s = '<tr><td colspan="4"><a href="%s">%s</a></td></tr>\n' % (f,name)
374 row = ElementTree.fromstring(s)
375 table.append(row)
377 errors = sorted(val['errors'], key=lambda e: int(e['line']) if 'line' in e else sys.maxint)
378 for e in errors:
379 if not 'line' in e:
380 s = '<tr><td></td><td>%s</td><td>%s</td><td>%s</td></tr>\n' % (e['id'], e['severity'], e['msg'])
381 else:
382 attr = ''
383 if e['severity'] == 'error':
384 attr = 'class="error"'
385 s = '<tr><td><a href="%s#line-%s">%s</a></td>' % (f, e['line'], e['line'])
386 s+= '<td>%s</td><td>%s</td><td %s>%s</td></tr>\n' % (e['id'], e['severity'], attr, e['msg'])
387 row = ElementTree.fromstring(s)
388 table.append(row)
389 content.append(table)
391 def _create_css_file(self, css_style_defs):
392 css = str(CPPCHECK_CSS_FILE)
393 if css_style_defs:
394 css = "%s\n%s\n" % (css, css_style_defs)
395 node = self.generator.path.get_bld().find_or_declare('cppcheck/style.css')
396 node.write(css)
398 def _errors_evaluate(self, errors, http_index):
399 name = self.generator.get_name()
400 fatal = self.fatal
401 severity = [err['severity'] for err in errors]
402 problems = [err for err in errors if err['severity'] != 'information']
404 if set(fatal) & set(severity):
405 exc = "\n"
406 exc += "\nccpcheck detected fatal error(s) in task '%s', see report for details:" % name
407 exc += "\n file://%r" % (http_index)
408 exc += "\n"
409 self.generator.bld.fatal(exc)
411 elif len(problems):
412 msg = "\nccpcheck detected (possible) problem(s) in task '%s', see report for details:" % name
413 msg += "\n file://%r" % http_index
414 msg += "\n"
415 Logs.error(msg)
418 class CppcheckHtmlFormatter(pygments.formatters.HtmlFormatter):
419 errors = []
421 def wrap(self, source, outfile):
422 line_no = 1
423 for i, t in super(CppcheckHtmlFormatter, self).wrap(source, outfile):
424 # If this is a source code line we want to add a span tag at the end.
425 if i == 1:
426 for error in self.errors:
427 if int(error['line']) == line_no:
428 t = t.replace('\n', CPPCHECK_HTML_ERROR % error['msg'])
429 line_no += 1
430 yield i, t
433 CCPCHECK_HTML_TYPE = \
434 '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">\n'
436 CPPCHECK_HTML_FILE = """
437 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd" [<!ENTITY nbsp "&#160;">]>
438 <html>
439 <head>
440 <title>cppcheck - report - XXX</title>
441 <link href="style.css" rel="stylesheet" type="text/css" />
442 <style type="text/css">
443 </style>
444 </head>
445 <body class="body">
446 <div id="page-header">&nbsp;</div>
447 <div id="page">
448 <div id="header">
449 <h1>cppcheck report - XXX</h1>
450 </div>
451 <div id="menu">
452 <a href="index.html">Defect list</a>
453 </div>
454 <div id="content">
455 </div>
456 <div id="footer">
457 <div>cppcheck - a tool for static C/C++ code analysis</div>
458 <div>
459 Internet: <a href="http://cppcheck.sourceforge.net">http://cppcheck.sourceforge.net</a><br/>
460 Forum: <a href="http://apps.sourceforge.net/phpbb/cppcheck/">http://apps.sourceforge.net/phpbb/cppcheck/</a><br/>
461 IRC: #cppcheck at irc.freenode.net
462 </div>
463 &nbsp;
464 </div>
465 &nbsp;
466 </div>
467 <div id="page-footer">&nbsp;</div>
468 </body>
469 </html>
472 CPPCHECK_HTML_TABLE = """
473 <table>
474 <tr>
475 <th>Line</th>
476 <th>Id</th>
477 <th>Severity</th>
478 <th>Message</th>
479 </tr>
480 </table>
483 CPPCHECK_HTML_ERROR = \
484 '<span style="background: #ffaaaa;padding: 3px;">&lt;--- %s</span>\n'
486 CPPCHECK_CSS_FILE = """
487 body.body {
488 font-family: Arial;
489 font-size: 13px;
490 background-color: black;
491 padding: 0px;
492 margin: 0px;
495 .error {
496 font-family: Arial;
497 font-size: 13px;
498 background-color: #ffb7b7;
499 padding: 0px;
500 margin: 0px;
503 th, td {
504 min-width: 100px;
505 text-align: left;
508 #page-header {
509 clear: both;
510 width: 1200px;
511 margin: 20px auto 0px auto;
512 height: 10px;
513 border-bottom-width: 2px;
514 border-bottom-style: solid;
515 border-bottom-color: #aaaaaa;
518 #page {
519 width: 1160px;
520 margin: auto;
521 border-left-width: 2px;
522 border-left-style: solid;
523 border-left-color: #aaaaaa;
524 border-right-width: 2px;
525 border-right-style: solid;
526 border-right-color: #aaaaaa;
527 background-color: White;
528 padding: 20px;
531 #page-footer {
532 clear: both;
533 width: 1200px;
534 margin: auto;
535 height: 10px;
536 border-top-width: 2px;
537 border-top-style: solid;
538 border-top-color: #aaaaaa;
541 #header {
542 width: 100%;
543 height: 70px;
544 background-image: url(logo.png);
545 background-repeat: no-repeat;
546 background-position: left top;
547 border-bottom-style: solid;
548 border-bottom-width: thin;
549 border-bottom-color: #aaaaaa;
552 #menu {
553 margin-top: 5px;
554 text-align: left;
555 float: left;
556 width: 100px;
557 height: 300px;
560 #menu > a {
561 margin-left: 10px;
562 display: block;
565 #content {
566 float: left;
567 width: 1020px;
568 margin: 5px;
569 padding: 0px 10px 10px 10px;
570 border-left-style: solid;
571 border-left-width: thin;
572 border-left-color: #aaaaaa;
575 #footer {
576 padding-bottom: 5px;
577 padding-top: 5px;
578 border-top-style: solid;
579 border-top-width: thin;
580 border-top-color: #aaaaaa;
581 clear: both;
582 font-size: 10px;
585 #footer > div {
586 float: left;
587 width: 33%;