162306a36Sopenharmony_ci#!/usr/bin/env python3
262306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0
362306a36Sopenharmony_ci#
462306a36Sopenharmony_ci# Copyright (C) Google LLC, 2020
562306a36Sopenharmony_ci#
662306a36Sopenharmony_ci# Author: Nathan Huckleberry <nhuck@google.com>
762306a36Sopenharmony_ci#
862306a36Sopenharmony_ci"""A helper routine run clang-tidy and the clang static-analyzer on
962306a36Sopenharmony_cicompile_commands.json.
1062306a36Sopenharmony_ci"""
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ciimport argparse
1362306a36Sopenharmony_ciimport json
1462306a36Sopenharmony_ciimport multiprocessing
1562306a36Sopenharmony_ciimport subprocess
1662306a36Sopenharmony_ciimport sys
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cidef parse_arguments():
2062306a36Sopenharmony_ci    """Set up and parses command-line arguments.
2162306a36Sopenharmony_ci    Returns:
2262306a36Sopenharmony_ci        args: Dict of parsed args
2362306a36Sopenharmony_ci        Has keys: [path, type]
2462306a36Sopenharmony_ci    """
2562306a36Sopenharmony_ci    usage = """Run clang-tidy or the clang static-analyzer on a
2662306a36Sopenharmony_ci        compilation database."""
2762306a36Sopenharmony_ci    parser = argparse.ArgumentParser(description=usage)
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci    type_help = "Type of analysis to be performed"
3062306a36Sopenharmony_ci    parser.add_argument("type",
3162306a36Sopenharmony_ci                        choices=["clang-tidy", "clang-analyzer"],
3262306a36Sopenharmony_ci                        help=type_help)
3362306a36Sopenharmony_ci    path_help = "Path to the compilation database to parse"
3462306a36Sopenharmony_ci    parser.add_argument("path", type=str, help=path_help)
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci    return parser.parse_args()
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cidef init(l, a):
4062306a36Sopenharmony_ci    global lock
4162306a36Sopenharmony_ci    global args
4262306a36Sopenharmony_ci    lock = l
4362306a36Sopenharmony_ci    args = a
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cidef run_analysis(entry):
4762306a36Sopenharmony_ci    # Disable all checks, then re-enable the ones we want
4862306a36Sopenharmony_ci    checks = []
4962306a36Sopenharmony_ci    checks.append("-checks=-*")
5062306a36Sopenharmony_ci    if args.type == "clang-tidy":
5162306a36Sopenharmony_ci        checks.append("linuxkernel-*")
5262306a36Sopenharmony_ci    else:
5362306a36Sopenharmony_ci        checks.append("clang-analyzer-*")
5462306a36Sopenharmony_ci        checks.append("-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling")
5562306a36Sopenharmony_ci    p = subprocess.run(["clang-tidy", "-p", args.path, ",".join(checks), entry["file"]],
5662306a36Sopenharmony_ci                       stdout=subprocess.PIPE,
5762306a36Sopenharmony_ci                       stderr=subprocess.STDOUT,
5862306a36Sopenharmony_ci                       cwd=entry["directory"])
5962306a36Sopenharmony_ci    with lock:
6062306a36Sopenharmony_ci        sys.stderr.buffer.write(p.stdout)
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cidef main():
6462306a36Sopenharmony_ci    try:
6562306a36Sopenharmony_ci        args = parse_arguments()
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci        lock = multiprocessing.Lock()
6862306a36Sopenharmony_ci        pool = multiprocessing.Pool(initializer=init, initargs=(lock, args))
6962306a36Sopenharmony_ci        # Read JSON data into the datastore variable
7062306a36Sopenharmony_ci        with open(args.path, "r") as f:
7162306a36Sopenharmony_ci            datastore = json.load(f)
7262306a36Sopenharmony_ci            pool.map(run_analysis, datastore)
7362306a36Sopenharmony_ci    except BrokenPipeError:
7462306a36Sopenharmony_ci        # Python flushes standard streams on exit; redirect remaining output
7562306a36Sopenharmony_ci        # to devnull to avoid another BrokenPipeError at shutdown
7662306a36Sopenharmony_ci        devnull = os.open(os.devnull, os.O_WRONLY)
7762306a36Sopenharmony_ci        os.dup2(devnull, sys.stdout.fileno())
7862306a36Sopenharmony_ci        sys.exit(1)  # Python exits with error code 1 on EPIPE
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ciif __name__ == "__main__":
8262306a36Sopenharmony_ci    main()
83