run-clang-tools.py 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. #!/usr/bin/env python3
  2. # SPDX-License-Identifier: GPL-2.0
  3. #
  4. # Copyright (C) Google LLC, 2020
  5. #
  6. # Author: Nathan Huckleberry <[email protected]>
  7. #
  8. """A helper routine run clang-tidy and the clang static-analyzer on
  9. compile_commands.json.
  10. """
  11. import argparse
  12. import json
  13. import multiprocessing
  14. import subprocess
  15. import sys
  16. def parse_arguments():
  17. """Set up and parses command-line arguments.
  18. Returns:
  19. args: Dict of parsed args
  20. Has keys: [path, type]
  21. """
  22. usage = """Run clang-tidy or the clang static-analyzer on a
  23. compilation database."""
  24. parser = argparse.ArgumentParser(description=usage)
  25. type_help = "Type of analysis to be performed"
  26. parser.add_argument("type",
  27. choices=["clang-tidy", "clang-analyzer"],
  28. help=type_help)
  29. path_help = "Path to the compilation database to parse"
  30. parser.add_argument("path", type=str, help=path_help)
  31. return parser.parse_args()
  32. def init(l, a):
  33. global lock
  34. global args
  35. lock = l
  36. args = a
  37. def run_analysis(entry):
  38. # Disable all checks, then re-enable the ones we want
  39. checks = []
  40. checks.append("-checks=-*")
  41. if args.type == "clang-tidy":
  42. checks.append("linuxkernel-*")
  43. else:
  44. checks.append("clang-analyzer-*")
  45. checks.append("-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling")
  46. p = subprocess.run(["clang-tidy", "-p", args.path, ",".join(checks), entry["file"]],
  47. stdout=subprocess.PIPE,
  48. stderr=subprocess.STDOUT,
  49. cwd=entry["directory"])
  50. with lock:
  51. sys.stderr.buffer.write(p.stdout)
  52. def main():
  53. try:
  54. args = parse_arguments()
  55. lock = multiprocessing.Lock()
  56. pool = multiprocessing.Pool(initializer=init, initargs=(lock, args))
  57. # Read JSON data into the datastore variable
  58. with open(args.path, "r") as f:
  59. datastore = json.load(f)
  60. pool.map(run_analysis, datastore)
  61. except BrokenPipeError:
  62. # Python flushes standard streams on exit; redirect remaining output
  63. # to devnull to avoid another BrokenPipeError at shutdown
  64. devnull = os.open(os.devnull, os.O_WRONLY)
  65. os.dup2(devnull, sys.stdout.fileno())
  66. sys.exit(1) # Python exits with error code 1 on EPIPE
  67. if __name__ == "__main__":
  68. main()