|
@@ -0,0 +1,98 @@
|
|
|
+import subprocess
|
|
|
+import sys
|
|
|
+import os
|
|
|
+import re
|
|
|
+import argparse
|
|
|
+import yaml
|
|
|
+
|
|
|
+CLANG_SA_CONFIG='static_analyzer.yaml'
|
|
|
+
|
|
|
+def parse_args():
|
|
|
+ parser = argparse.ArgumentParser()
|
|
|
+ parser.add_argument("--testing-src", required=True)
|
|
|
+ parser.add_argument("--clang-bin", required=True)
|
|
|
+ parser.add_argument("--source-root", required=True)
|
|
|
+ parser.add_argument("--config-file", required=True)
|
|
|
+ parser.add_argument("--plugins-begin", dest='plugins', action='append', nargs='+', required=True)
|
|
|
+ parser.add_argument("--plugins-end", action='store_true', required=True)
|
|
|
+ return parser.parse_known_args()
|
|
|
+
|
|
|
+def find_config(config_path):
|
|
|
+ # For unifying config files names
|
|
|
+ basename = os.path.basename(config_path)
|
|
|
+ if basename != CLANG_SA_CONFIG:
|
|
|
+ msg = "The config file should be called {}, but {} passed".format(CLANG_SA_CONFIG, basename)
|
|
|
+ raise ValueError(msg)
|
|
|
+ if not os.path.isfile(config_path):
|
|
|
+ raise ValueError("Cant find config file {}".format(config_path))
|
|
|
+ return config_path
|
|
|
+
|
|
|
+def parse_config(config_file):
|
|
|
+ conf = None
|
|
|
+ try:
|
|
|
+ with open(config_file, 'r') as afile:
|
|
|
+ conf = yaml.safe_load(afile)
|
|
|
+ except:
|
|
|
+ conf = None
|
|
|
+ return conf
|
|
|
+
|
|
|
+def should_analyze(filename, conf):
|
|
|
+ include_files = conf.get('include_files')
|
|
|
+ exclude_files = conf.get('exclude_files')
|
|
|
+
|
|
|
+ if not include_files:
|
|
|
+ return False
|
|
|
+
|
|
|
+ include = re.match(include_files, filename)
|
|
|
+ exclude = re.match(exclude_files, filename) if exclude_files else False
|
|
|
+
|
|
|
+ return include and not exclude
|
|
|
+
|
|
|
+def load_plugins(conf, plugins):
|
|
|
+ load_cmds = []
|
|
|
+ for plugin in filter(lambda path: os.path.isfile(path), plugins):
|
|
|
+ load_cmds.extend(["-Xclang", "-load", "-Xclang", plugin])
|
|
|
+ return load_cmds
|
|
|
+
|
|
|
+def main():
|
|
|
+ args, clang_cmd = parse_args()
|
|
|
+
|
|
|
+ # Try to find config file and parse them
|
|
|
+ config_file = find_config(args.config_file)
|
|
|
+ conf = parse_config(config_file)
|
|
|
+
|
|
|
+ # Ensure we have at least one check
|
|
|
+ if ('checks' not in conf) or (not conf['checks']):
|
|
|
+ raise ValueError("There are no checks in the config file")
|
|
|
+
|
|
|
+ # Ensure that file match regex
|
|
|
+ if not should_analyze(args.testing_src, conf):
|
|
|
+ return 0
|
|
|
+
|
|
|
+ # Prepare args
|
|
|
+ analyzer_opts = [
|
|
|
+ '-Wno-unused-command-line-argument',
|
|
|
+ '--analyze',
|
|
|
+ '--analyzer-outputtext',
|
|
|
+ '--analyzer-no-default-checks'
|
|
|
+ ]
|
|
|
+ analyzer_opts.extend(['-Xanalyzer', '-analyzer-werror'])
|
|
|
+ analyzer_opts.extend(['-Xanalyzer', '-analyzer-checker=' + ','.join(conf['checks'])])
|
|
|
+
|
|
|
+ # Load additional plugins
|
|
|
+ analyzer_opts.extend(load_plugins(conf, args.plugins[0]))
|
|
|
+
|
|
|
+ run_cmd = [args.clang_bin, args.testing_src] + clang_cmd + analyzer_opts
|
|
|
+ p = subprocess.run(run_cmd)
|
|
|
+
|
|
|
+ return p.returncode
|
|
|
+
|
|
|
+if __name__ == '__main__':
|
|
|
+ ret_code = 0
|
|
|
+ try:
|
|
|
+ ret_code = main()
|
|
|
+ except Exception as e:
|
|
|
+ print >> sys.stderr, "\n[Error]: " + str(e)
|
|
|
+ ret_code = 1
|
|
|
+ exit(ret_code)
|
|
|
+
|