123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- import argparse
- import json
- import os
- import re
- import shutil
- import sys
- import subprocess
- import yaml
- def setup_script(args):
- global tidy_config_validation
- sys.path.append(os.path.dirname(args.config_validation_script))
- import tidy_config_validation
- def parse_args():
- parser = argparse.ArgumentParser()
- parser.add_argument("--testing-src", required=True)
- parser.add_argument("--clang-tidy-bin", required=True)
- parser.add_argument("--config-validation-script", required=True)
- parser.add_argument("--ymake-python", required=True)
- parser.add_argument("--tidy-json", required=True)
- parser.add_argument("--source-root", required=True)
- parser.add_argument("--build-root", required=True)
- parser.add_argument("--default-config-file", required=True)
- parser.add_argument("--project-config-file", required=True)
- parser.add_argument("--export-fixes", required=True)
- parser.add_argument("--checks", required=False, default="")
- parser.add_argument("--header-filter", required=False, default=None)
- return parser.parse_known_args()
- def generate_compilation_database(clang_cmd, source_root, filename, path):
- compile_database = [
- {
- "file": filename,
- "command": subprocess.list2cmdline(clang_cmd),
- "directory": source_root,
- }
- ]
- compilation_database_json = os.path.join(path, "compile_commands.json")
- with open(compilation_database_json, "w") as afile:
- json.dump(compile_database, afile)
- return compilation_database_json
- def load_profile(path):
- if os.path.exists(path):
- files = os.listdir(path)
- if len(files) == 1:
- with open(os.path.join(path, files[0])) as afile:
- return json.load(afile)["profile"]
- elif len(files) > 1:
- return {
- "error": "found several profile files: {}".format(files),
- }
- return {
- "error": "profile file is missing",
- }
- def load_fixes(path):
- if os.path.exists(path):
- with open(path, 'r') as afile:
- return afile.read()
- else:
- return ""
- def is_generated(testing_src, build_root):
- return testing_src.startswith(build_root)
- def generate_outputs(output_json):
- output_obj = os.path.splitext(output_json)[0] + ".o"
- open(output_obj, "w").close()
- open(output_json, "w").close()
- def filter_configs(result_config, filtered_config):
- with open(result_config, 'r') as afile:
- input_config = yaml.safe_load(afile)
- result_config = tidy_config_validation.filter_config(input_config)
- with open(filtered_config, 'w') as afile:
- yaml.safe_dump(result_config, afile)
- def filter_cmd(cmd):
- skip = True
- for x in cmd:
- if not skip:
- yield x
- if '/wrapcc.py' in x:
- skip = False
- def walk(p):
- for a, b, c in os.walk(p):
- for x in c:
- yield os.path.join(a, x)
- def find_header(p, h):
- for x in walk(p):
- if x.endswith(h):
- return os.path.dirname(x)
- raise Exception('can not find inc dir')
- def main():
- args, clang_cmd = parse_args()
- if '/wrapcc.py' in str(clang_cmd):
- clang_cmd = list(filter_cmd(clang_cmd))
- setup_script(args)
- clang_tidy_bin = args.clang_tidy_bin
- output_json = args.tidy_json
- generate_outputs(output_json)
- if is_generated(args.testing_src, args.build_root):
- return
- if args.header_filter is None:
- # .pb.h files will be excluded because they are not in source_root
- header_filter = r"^" + re.escape(os.path.dirname(args.testing_src)) + r".*"
- else:
- header_filter = r"^(" + args.header_filter + r").*"
- def ensure_clean_dir(path):
- path = os.path.join(args.build_root, path)
- if os.path.exists(path):
- shutil.rmtree(path)
- os.makedirs(path)
- return path
- profile_tmpdir = ensure_clean_dir("profile_tmpdir")
- db_tmpdir = ensure_clean_dir("db_tmpdir")
- fixes_file = "fixes.txt"
- config_dir = ensure_clean_dir("config_dir")
- result_config_file = args.default_config_file
- if args.project_config_file != args.default_config_file:
- result_config = os.path.join(config_dir, "result_tidy_config.yaml")
- filtered_config = os.path.join(config_dir, "filtered_tidy_config.yaml")
- filter_configs(args.project_config_file, filtered_config)
- result_config_file = tidy_config_validation.merge_tidy_configs(
- base_config_path=args.default_config_file,
- additional_config_path=filtered_config,
- result_config_path=result_config,
- )
- compile_command_path = generate_compilation_database(clang_cmd, args.source_root, args.testing_src, db_tmpdir)
- cmd = [
- clang_tidy_bin,
- args.testing_src,
- "-p",
- compile_command_path,
- "--warnings-as-errors",
- "*,-clang-diagnostic-#pragma-messages",
- "--config-file",
- result_config_file,
- "--header-filter",
- header_filter,
- "--use-color",
- "--enable-check-profile",
- "--store-check-profile={}".format(profile_tmpdir),
- ]
- if args.export_fixes == "yes":
- cmd += ["--export-fixes", fixes_file]
- if args.checks:
- cmd += ["--checks", args.checks]
- print("cmd: {}".format(' '.join(cmd)))
- res = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- out, err = res.communicate()
- out = out.replace(args.source_root, "$(SOURCE_ROOT)")
- profile = load_profile(profile_tmpdir)
- testing_src = os.path.relpath(args.testing_src, args.source_root)
- tidy_fixes = load_fixes(fixes_file)
- with open(output_json, "wb") as afile:
- json.dump(
- {
- "file": testing_src,
- "exit_code": res.returncode,
- "profile": profile,
- "stderr": err,
- "stdout": out,
- "fixes": tidy_fixes,
- },
- afile,
- )
- if __name__ == "__main__":
- main()
|