18c2ecf20Sopenharmony_ci#!/usr/bin/env python3
28c2ecf20Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0
38c2ecf20Sopenharmony_ci#
48c2ecf20Sopenharmony_ci# Copyright (C) Google LLC, 2020
58c2ecf20Sopenharmony_ci#
68c2ecf20Sopenharmony_ci# Author: Nathan Huckleberry <nhuck@google.com>
78c2ecf20Sopenharmony_ci#
88c2ecf20Sopenharmony_ci"""A helper routine run clang-tidy and the clang static-analyzer on
98c2ecf20Sopenharmony_cicompile_commands.json.
108c2ecf20Sopenharmony_ci"""
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ciimport argparse
138c2ecf20Sopenharmony_ciimport json
148c2ecf20Sopenharmony_ciimport multiprocessing
158c2ecf20Sopenharmony_ciimport os
168c2ecf20Sopenharmony_ciimport subprocess
178c2ecf20Sopenharmony_ciimport sys
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cidef parse_arguments():
218c2ecf20Sopenharmony_ci    """Set up and parses command-line arguments.
228c2ecf20Sopenharmony_ci    Returns:
238c2ecf20Sopenharmony_ci        args: Dict of parsed args
248c2ecf20Sopenharmony_ci        Has keys: [path, type]
258c2ecf20Sopenharmony_ci    """
268c2ecf20Sopenharmony_ci    usage = """Run clang-tidy or the clang static-analyzer on a
278c2ecf20Sopenharmony_ci        compilation database."""
288c2ecf20Sopenharmony_ci    parser = argparse.ArgumentParser(description=usage)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci    type_help = "Type of analysis to be performed"
318c2ecf20Sopenharmony_ci    parser.add_argument("type",
328c2ecf20Sopenharmony_ci                        choices=["clang-tidy", "clang-analyzer"],
338c2ecf20Sopenharmony_ci                        help=type_help)
348c2ecf20Sopenharmony_ci    path_help = "Path to the compilation database to parse"
358c2ecf20Sopenharmony_ci    parser.add_argument("path", type=str, help=path_help)
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci    return parser.parse_args()
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cidef init(l, a):
418c2ecf20Sopenharmony_ci    global lock
428c2ecf20Sopenharmony_ci    global args
438c2ecf20Sopenharmony_ci    lock = l
448c2ecf20Sopenharmony_ci    args = a
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cidef run_analysis(entry):
488c2ecf20Sopenharmony_ci    # Disable all checks, then re-enable the ones we want
498c2ecf20Sopenharmony_ci    checks = "-checks=-*,"
508c2ecf20Sopenharmony_ci    if args.type == "clang-tidy":
518c2ecf20Sopenharmony_ci        checks += "linuxkernel-*"
528c2ecf20Sopenharmony_ci    else:
538c2ecf20Sopenharmony_ci        checks += "clang-analyzer-*"
548c2ecf20Sopenharmony_ci    p = subprocess.run(["clang-tidy", "-p", args.path, checks, entry["file"]],
558c2ecf20Sopenharmony_ci                       stdout=subprocess.PIPE,
568c2ecf20Sopenharmony_ci                       stderr=subprocess.STDOUT,
578c2ecf20Sopenharmony_ci                       cwd=entry["directory"])
588c2ecf20Sopenharmony_ci    with lock:
598c2ecf20Sopenharmony_ci        sys.stderr.buffer.write(p.stdout)
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cidef main():
638c2ecf20Sopenharmony_ci    try:
648c2ecf20Sopenharmony_ci        args = parse_arguments()
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci        lock = multiprocessing.Lock()
678c2ecf20Sopenharmony_ci        pool = multiprocessing.Pool(initializer=init, initargs=(lock, args))
688c2ecf20Sopenharmony_ci        # Read JSON data into the datastore variable
698c2ecf20Sopenharmony_ci        with open(args.path, "r") as f:
708c2ecf20Sopenharmony_ci            datastore = json.load(f)
718c2ecf20Sopenharmony_ci            pool.map(run_analysis, datastore)
728c2ecf20Sopenharmony_ci    except BrokenPipeError:
738c2ecf20Sopenharmony_ci        # Python flushes standard streams on exit; redirect remaining output
748c2ecf20Sopenharmony_ci        # to devnull to avoid another BrokenPipeError at shutdown
758c2ecf20Sopenharmony_ci        devnull = os.open(os.devnull, os.O_WRONLY)
768c2ecf20Sopenharmony_ci        os.dup2(devnull, sys.stdout.fileno())
778c2ecf20Sopenharmony_ci        sys.exit(1)  # Python exits with error code 1 on EPIPE
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ciif __name__ == "__main__":
818c2ecf20Sopenharmony_ci    main()
82