addons: Clean up and clarify addons usage. (#2359)

* addons: Add '--recursive' arg. Clean up and clarify errors messages.

This commit introduce '--recursive' option for cppcheck addons.
Iff this option is set addon will recursively traverse directories in
given input files to find files with '.dump' suffix that would be
checked. Otherwise it will treat input directory as error (current
behaviour).

Add additional error handling with more clear error messages, clean up
the code.

* Add regex specifier

* Roll back --recursive option

* Update addons section in manual
This commit is contained in:
Georgy Komarov 2019-11-15 22:14:30 +03:00 committed by Daniel Marjamäki
parent 3a617fa04a
commit 3bd126486f
5 changed files with 126 additions and 95 deletions

View File

@ -358,10 +358,6 @@ def api01(data):
def get_args():
parser = cppcheckdata.ArgumentParser()
parser.add_argument("dumpfile", nargs='*', help="Path of dump files from cppcheck")
parser.add_argument('-q', '--quiet', action='store_true',
help='do not print "Checking ..." lines')
parser.add_argument('--cli', help='Addon is executed from Cppcheck', action='store_true')
parser.add_argument("-verify", help=argparse.SUPPRESS, action="store_true")
return parser.parse_args()
@ -371,6 +367,11 @@ if __name__ == '__main__':
if args.verify:
VERIFY = True
if not args.dumpfile:
if not args.quiet:
print("no input files.")
sys.exit(0)
for dumpfile in args.dumpfile:
if not args.quiet:
print('Checking %s...' % dumpfile)

View File

@ -10,6 +10,7 @@ from xml.etree import ElementTree
import argparse
from fnmatch import fnmatch
import json
import os
import sys
@ -849,8 +850,8 @@ class CppCheckFormatter(argparse.HelpFormatter):
def ArgumentParser():
"""
Returns an argparse argument parser with an already-added
argument definition for -t/--template
Returns an argparse argument parser with an already-added
argument definition for -t/--template
"""
parser = argparse.ArgumentParser(formatter_class=CppCheckFormatter)
parser.add_argument('-t', '--template', metavar='<text>',
@ -860,6 +861,14 @@ def ArgumentParser():
"'{file}({line}):({severity}) {message}' or\n"
"'{callstack} {message}'\n"
"Pre-defined templates: gcc, vs, edit")
parser.add_argument("dumpfile", nargs='*',
help="Path of dump files from cppcheck.")
parser.add_argument("--cli",
help="Addon is executed from Cppcheck",
action="store_true")
parser.add_argument("-q", "--quiet",
help='do not print "Checking ..." lines',
action="store_true")
return parser

View File

@ -2565,14 +2565,11 @@ def get_args():
parser.add_argument("--rule-texts", type=str, help=RULE_TEXTS_HELP)
parser.add_argument("--verify-rule-texts", help="Verify that all supported rules texts are present in given file and exit.", action="store_true")
parser.add_argument("--suppress-rules", type=str, help=SUPPRESS_RULES_HELP)
parser.add_argument("--quiet", help="Only print something when there is an error", action="store_true")
parser.add_argument("--no-summary", help="Hide summary of violations", action="store_true")
parser.add_argument("-verify", help=argparse.SUPPRESS, action="store_true")
parser.add_argument("-generate-table", help=argparse.SUPPRESS, action="store_true")
parser.add_argument("dumpfile", nargs='*', help="Path of dump file from cppcheck")
parser.add_argument("--show-suppressed-rules", help="Print rule suppression list", action="store_true")
parser.add_argument("-P", "--file-prefix", type=str, help="Prefix to strip when matching suppression file rules")
parser.add_argument("--cli", help="Addon is executed from Cppcheck", action="store_true")
parser.add_argument("-generate-table", help=argparse.SUPPRESS, action="store_true")
parser.add_argument("-verify", help=argparse.SUPPRESS, action="store_true")
return parser.parse_args()
@ -2606,63 +2603,68 @@ def main():
if args.file_prefix:
checker.setFilePrefix(args.file_prefix)
if args.dumpfile:
exitCode = 0
for item in args.dumpfile:
checker.parseDump(item)
if not args.dumpfile:
if not args.quiet:
print("No input files.")
sys.exit(0)
if settings.verify:
verify_expected = checker.get_verify_expected()
verify_actual = checker.get_verify_actual()
exitCode = 0
for item in args.dumpfile:
checker.parseDump(item)
for expected in verify_expected:
if expected not in verify_actual:
print('Expected but not seen: ' + expected)
exitCode = 1
for actual in verify_actual:
if actual not in verify_expected:
print('Not expected: ' + actual)
exitCode = 1
if settings.verify:
verify_expected = checker.get_verify_expected()
verify_actual = checker.get_verify_actual()
# Existing behavior of verify mode is to exit
# on the first un-expected output.
# TODO: Is this required? or can it be moved to after
# all input files have been processed
if exitCode != 0:
sys.exit(exitCode)
for expected in verify_expected:
if expected not in verify_actual:
print('Expected but not seen: ' + expected)
exitCode = 1
for actual in verify_actual:
if actual not in verify_expected:
print('Not expected: ' + actual)
exitCode = 1
# Under normal operation exit with a non-zero exit code
# if there were any violations.
if not settings.verify:
number_of_violations = len(checker.get_violations())
if number_of_violations > 0:
exitCode = 1
# Existing behavior of verify mode is to exit
# on the first un-expected output.
# TODO: Is this required? or can it be moved to after
# all input files have been processed
if exitCode != 0:
sys.exit(exitCode)
if settings.show_summary:
print("\nMISRA rules violations found:\n\t%s\n" % ("\n\t".join([ "%s: %d" % (viol, len(checker.get_violations(viol))) for viol in checker.get_violation_types()])))
# Under normal operation exit with a non-zero exit code
# if there were any violations.
if not settings.verify:
number_of_violations = len(checker.get_violations())
if number_of_violations > 0:
exitCode = 1
rules_violated = {}
for severity, ids in checker.get_violations():
for misra_id in ids:
rules_violated[misra_id] = rules_violated.get(misra_id, 0) + 1
print("MISRA rules violated:")
convert = lambda text: int(text) if text.isdigit() else text
misra_sort = lambda key: [ convert(c) for c in re.split('[.-]([0-9]*)', key) ]
for misra_id in sorted(rules_violated.keys(), key=misra_sort):
res = re.match(r'misra-c2012-([0-9]+)\\.([0-9]+)', misra_id)
if res is None:
num = 0
else:
num = int(res.group(1)) * 100 + int(res.group(2))
severity = '-'
if num in checker.ruleTexts:
severity = checker.ruleTexts[num].cppcheck_severity
print("\t%15s (%s): %d" % (misra_id, severity, rules_violated[misra_id]))
if settings.show_summary:
print("\nMISRA rules violations found:\n\t%s\n" % (
"\n\t".join(["%s: %d" % (viol, len(checker.get_violations(viol))) for viol in checker.get_violation_types()])))
if args.show_suppressed_rules:
checker.showSuppressedRules()
rules_violated = {}
for severity, ids in checker.get_violations():
for misra_id in ids:
rules_violated[misra_id] = rules_violated.get(misra_id, 0) + 1
print("MISRA rules violated:")
convert = lambda text: int(text) if text.isdigit() else text
misra_sort = lambda key: [ convert(c) for c in re.split(r'[\.-]([0-9]*)', key) ]
for misra_id in sorted(rules_violated.keys(), key=misra_sort):
res = re.match(r'misra-c2012-([0-9]+)\\.([0-9]+)', misra_id)
if res is None:
num = 0
else:
num = int(res.group(1)) * 100 + int(res.group(2))
severity = '-'
if num in checker.ruleTexts:
severity = checker.ruleTexts[num].cppcheck_severity
print("\t%15s (%s): %d" % (misra_id, severity, rules_violated[misra_id]))
sys.exit(exitCode)
if args.show_suppressed_rules:
checker.showSuppressedRules()
sys.exit(exitCode)
if __name__ == '__main__':

View File

@ -228,10 +228,6 @@ def check_y2038_safe(dumpfile, quiet=False):
def get_args():
parser = cppcheckdata.ArgumentParser()
parser.add_argument("dumpfile", nargs='*', help="Path of dump file from cppcheck")
parser.add_argument('-q', '--quiet', action='store_true',
help='do not print "Checking ..." lines')
parser.add_argument('--cli', help='Addon is executed from Cppcheck', action='store_true')
return parser.parse_args()
@ -241,19 +237,23 @@ if __name__ == '__main__':
exit_code = 0
quiet = not any((args.quiet, args.cli))
if args.dumpfile:
for dumpfile in args.dumpfile:
if not os.path.isfile(dumpfile):
print("Error: File not found: %s" % dumpfile)
sys.exit(127)
if not os.access(dumpfile, os.R_OK):
print("Error: Permission denied: %s" % dumpfile)
sys.exit(13)
if not args.quiet:
print('Checking ' + dumpfile + '...')
if not args.dumpfile:
if not args.quiet:
print("no input files.")
sys.exit(0)
y2038safe = check_y2038_safe(dumpfile, quiet)
if not y2038safe and exit_code == 0:
exit_code = 1
for dumpfile in args.dumpfile:
if not os.path.isfile(dumpfile):
print("Error: File not found: %s" % dumpfile)
sys.exit(127)
if not os.access(dumpfile, os.R_OK):
print("Error: Permission denied: %s" % dumpfile)
sys.exit(13)
if not args.quiet:
print('Checking ' + dumpfile + '...')
y2038safe = check_y2038_safe(dumpfile, quiet)
if not y2038safe and exit_code == 0:
exit_code = 1
sys.exit(exit_code)

View File

@ -597,40 +597,59 @@ Carriage return
# Addons
Addons are scripts with extra checks. Cppcheck is distributed with a few addons.
Addons are scripts that analyses Cppcheck dump files to check compatibility with secure coding standards and to locate various issues.
Cppcheck is distributed with a few addons which are listed below.
## Supported addons
### cert.py
[cert.py](https://github.com/danmar/cppcheck/blob/master/addons/cert.py) checks for compliance with the safe programming standard [SEI CERT](http://www.cert.org/secure-coding/).
### misra.py
[misra.py](https://github.com/danmar/cppcheck/blob/master/addons/misra.py) is used to verify compliance with MISRA C 2012 - a proprietary set of guidelines to avoid such questionable code, developed for embedded systems.
Since this standard is proprietary, cppcheck does not display error text by specifying only the number of violated rules (for example, [c2012-21.3]). If you want to display full texts for violated rules, you will need to create a text file containing MISRA rules, which you will have to pass when calling the script with `--rule-texts` key. Some examples of rule texts files available in [tests directory](https://github.com/danmar/cppcheck/blob/master/addons/test/misra/).
You can also suppress some unwanted rules using `--suppress-rules` option. Suppressed rules should be set as comma-separated listed, for example: `--suppress-rules 21.1,18.7`. The full list of supported rules is available on [Cppcheck](http://cppcheck.sourceforge.net/misra.php) home page.
### y2038.py
[y2038.py](https://github.com/danmar/cppcheck/blob/master/addons/y2038.py) checks Linux system for [year 2038 problem](https://en.wikipedia.org/wiki/Year_2038_problem) safety. This required [modified environment](https://github.com/3adev/y2038). See complete description [here](https://github.com/danmar/cppcheck/blob/master/addons/doc/y2038.txt).
### threadsafety.py
[threadsafety.py](https://github.com/danmar/cppcheck/blob/master/addons/threadsafety.py) analyse Cppcheck dump files to locate threadsafety issues like static local objects used by multiple threads.
## Running Addons
Addons are standalone scripts that are executed separately.
Addons could be run through Cppcheck command line utility as follows:
To manually run an addon:
cppcheck --addon=misra.py somefile.c
cppcheck --dump somefile.c
python misc.py somefile.c.dump
This will launch all Cppcheck checks and additionaly calls specific checks provided by selected addon.
To run the same addon through Cppcheck directly:
cppcheck --addon=misc.py somefile.c
Some addons need extra arguments. For example misra.py can be executed manually like this:
cppcheck --dump somefile.c
python misra.py --rule-texts=misra.txt somefile.c.dump
You can configure how you want to execute an addon in a json file, for example put this in misra.json:
Some addons need extra arguments. You can configure how you want to execute an addon in a json file. For example put this in misra.json:
{
"script": "misra.py",
"args": [ "--rule-texts=misra.txt" ]
"args": [
"--rule-texts=misra.txt",
"--suppress-rules 17.3,21.12"
]
}
And then the configuration can be executed on the cppcheck command line:
cppcheck --addon=misra.json somefile.c
## Help about an addon
By default Cppcheck would search addon at standard path which was specified in installation process. You also can set this path directly, for example:
You can read about how to use a Cppcheck addon by looking in the addon. The comments at the top of the file should have a description.
cppcheck --addon=/opt/cppcheck/configurations/my_misra.json somefile.c
This allows you create and manage multiple configuration files for different projects.
# Library configuration