1074075aeSMasahiro Yamada#!/usr/bin/env python3
26ad7cbc0SNathan Huckleberry# SPDX-License-Identifier: GPL-2.0
36ad7cbc0SNathan Huckleberry#
46ad7cbc0SNathan Huckleberry# Copyright (C) Google LLC, 2020
56ad7cbc0SNathan Huckleberry#
66ad7cbc0SNathan Huckleberry# Author: Nathan Huckleberry <nhuck@google.com>
76ad7cbc0SNathan Huckleberry#
86ad7cbc0SNathan Huckleberry"""A helper routine run clang-tidy and the clang static-analyzer on
96ad7cbc0SNathan Huckleberrycompile_commands.json.
106ad7cbc0SNathan Huckleberry"""
116ad7cbc0SNathan Huckleberry
126ad7cbc0SNathan Huckleberryimport argparse
136ad7cbc0SNathan Huckleberryimport json
146ad7cbc0SNathan Huckleberryimport multiprocessing
156ad7cbc0SNathan Huckleberryimport subprocess
166ad7cbc0SNathan Huckleberryimport sys
176ad7cbc0SNathan Huckleberry
186ad7cbc0SNathan Huckleberry
196ad7cbc0SNathan Huckleberrydef parse_arguments():
206ad7cbc0SNathan Huckleberry    """Set up and parses command-line arguments.
216ad7cbc0SNathan Huckleberry    Returns:
226ad7cbc0SNathan Huckleberry        args: Dict of parsed args
236ad7cbc0SNathan Huckleberry        Has keys: [path, type]
246ad7cbc0SNathan Huckleberry    """
256ad7cbc0SNathan Huckleberry    usage = """Run clang-tidy or the clang static-analyzer on a
266ad7cbc0SNathan Huckleberry        compilation database."""
276ad7cbc0SNathan Huckleberry    parser = argparse.ArgumentParser(description=usage)
286ad7cbc0SNathan Huckleberry
296ad7cbc0SNathan Huckleberry    type_help = "Type of analysis to be performed"
306ad7cbc0SNathan Huckleberry    parser.add_argument("type",
316ad7cbc0SNathan Huckleberry                        choices=["clang-tidy", "clang-analyzer"],
326ad7cbc0SNathan Huckleberry                        help=type_help)
336ad7cbc0SNathan Huckleberry    path_help = "Path to the compilation database to parse"
346ad7cbc0SNathan Huckleberry    parser.add_argument("path", type=str, help=path_help)
356ad7cbc0SNathan Huckleberry
366ad7cbc0SNathan Huckleberry    return parser.parse_args()
376ad7cbc0SNathan Huckleberry
386ad7cbc0SNathan Huckleberry
396ad7cbc0SNathan Huckleberrydef init(l, a):
406ad7cbc0SNathan Huckleberry    global lock
416ad7cbc0SNathan Huckleberry    global args
426ad7cbc0SNathan Huckleberry    lock = l
436ad7cbc0SNathan Huckleberry    args = a
446ad7cbc0SNathan Huckleberry
456ad7cbc0SNathan Huckleberry
466ad7cbc0SNathan Huckleberrydef run_analysis(entry):
476ad7cbc0SNathan Huckleberry    # Disable all checks, then re-enable the ones we want
4804518e4cSGuru Das Srinagesh    checks = []
4904518e4cSGuru Das Srinagesh    checks.append("-checks=-*")
506ad7cbc0SNathan Huckleberry    if args.type == "clang-tidy":
5104518e4cSGuru Das Srinagesh        checks.append("linuxkernel-*")
526ad7cbc0SNathan Huckleberry    else:
5304518e4cSGuru Das Srinagesh        checks.append("clang-analyzer-*")
5404518e4cSGuru Das Srinagesh        checks.append("-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling")
5504518e4cSGuru Das Srinagesh    p = subprocess.run(["clang-tidy", "-p", args.path, ",".join(checks), entry["file"]],
566ad7cbc0SNathan Huckleberry                       stdout=subprocess.PIPE,
576ad7cbc0SNathan Huckleberry                       stderr=subprocess.STDOUT,
586ad7cbc0SNathan Huckleberry                       cwd=entry["directory"])
596ad7cbc0SNathan Huckleberry    with lock:
606ad7cbc0SNathan Huckleberry        sys.stderr.buffer.write(p.stdout)
616ad7cbc0SNathan Huckleberry
626ad7cbc0SNathan Huckleberry
636ad7cbc0SNathan Huckleberrydef main():
64*87c7ee67SMasahiro Yamada    try:
656ad7cbc0SNathan Huckleberry        args = parse_arguments()
666ad7cbc0SNathan Huckleberry
676ad7cbc0SNathan Huckleberry        lock = multiprocessing.Lock()
686ad7cbc0SNathan Huckleberry        pool = multiprocessing.Pool(initializer=init, initargs=(lock, args))
696ad7cbc0SNathan Huckleberry        # Read JSON data into the datastore variable
706ad7cbc0SNathan Huckleberry        with open(args.path, "r") as f:
716ad7cbc0SNathan Huckleberry            datastore = json.load(f)
726ad7cbc0SNathan Huckleberry            pool.map(run_analysis, datastore)
73*87c7ee67SMasahiro Yamada    except BrokenPipeError:
74*87c7ee67SMasahiro Yamada        # Python flushes standard streams on exit; redirect remaining output
75*87c7ee67SMasahiro Yamada        # to devnull to avoid another BrokenPipeError at shutdown
76*87c7ee67SMasahiro Yamada        devnull = os.open(os.devnull, os.O_WRONLY)
77*87c7ee67SMasahiro Yamada        os.dup2(devnull, sys.stdout.fileno())
78*87c7ee67SMasahiro Yamada        sys.exit(1)  # Python exits with error code 1 on EPIPE
796ad7cbc0SNathan Huckleberry
806ad7cbc0SNathan Huckleberry
816ad7cbc0SNathan Huckleberryif __name__ == "__main__":
826ad7cbc0SNathan Huckleberry    main()
83