Yves Gerey | 546ee61 | 2019-02-26 17:04:16 +0100 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. |
| 3 | # |
| 4 | # Use of this source code is governed by a BSD-style license |
| 5 | # that can be found in the LICENSE file in the root of the source |
| 6 | # tree. An additional intellectual property rights grant can be found |
| 7 | # in the file PATENTS. All contributing project authors may |
| 8 | # be found in the AUTHORS file in the root of the source tree. |
| 9 | |
| 10 | """Invoke clang-tidy tool. |
| 11 | |
| 12 | Usage: clang_tidy.py file.cc [clang-tidy-args...] |
| 13 | |
| 14 | Just a proof of concept! |
| 15 | We use an embedded clang-tidy whose version doesn't match clang++. |
| 16 | """ |
| 17 | |
| 18 | import argparse |
| 19 | import os |
| 20 | import shutil |
| 21 | import subprocess |
| 22 | import sys |
| 23 | import tempfile |
| 24 | #pylint: disable=relative-import |
| 25 | from presubmit_checks_lib.build_helpers import GetClangTidyPath, \ |
| 26 | GetCompilationCommand |
| 27 | |
| 28 | |
| 29 | # We enable all checkers by default for investigation purpose. |
| 30 | # This includes clang-analyzer-* checks. |
| 31 | # Individual checkers can be disabled via command line options. |
| 32 | # TODO(bugs.webrtc.com/10258): Select checkers relevant to webrtc guidelines. |
| 33 | CHECKER_OPTION = '-checks=*' |
| 34 | |
| 35 | |
| 36 | def Process(filepath, args): |
| 37 | # Build directory is needed to gather compilation flags. |
| 38 | # Create a temporary one (instead of reusing an existing one) |
| 39 | # to keep the CLI simple and unencumbered. |
| 40 | out_dir = tempfile.mkdtemp('clang_tidy') |
| 41 | |
| 42 | try: |
| 43 | gn_args = [] # Use default build. |
| 44 | command = GetCompilationCommand(filepath, gn_args, out_dir) |
| 45 | |
| 46 | # Remove warning flags. They aren't needed and they cause trouble |
| 47 | # when clang-tidy doesn't match most recent clang. |
| 48 | # Same battle for -f (e.g. -fcomplete-member-pointers). |
| 49 | command = [arg for arg in command if not (arg.startswith('-W') or |
| 50 | arg.startswith('-f'))] |
| 51 | |
| 52 | # Path from build dir. |
| 53 | rel_path = os.path.relpath(os.path.abspath(filepath), out_dir) |
| 54 | |
| 55 | # Replace clang++ by clang-tidy |
| 56 | command[0:1] = [GetClangTidyPath(), |
| 57 | CHECKER_OPTION, |
| 58 | rel_path] + args + ['--'] # Separator for clang flags. |
| 59 | print "Running: %s" % ' '.join(command) |
| 60 | # Run from build dir so that relative paths are correct. |
| 61 | p = subprocess.Popen(command, cwd=out_dir, |
| 62 | stdout=sys.stdout, stderr=sys.stderr) |
| 63 | p.communicate() |
| 64 | return p.returncode |
| 65 | finally: |
| 66 | shutil.rmtree(out_dir, ignore_errors=True) |
| 67 | |
| 68 | |
| 69 | def ValidateCC(filepath): |
| 70 | """We can only analyze .cc files. Provide explicit message about that.""" |
| 71 | if filepath.endswith('.cc'): |
| 72 | return filepath |
| 73 | msg = ('%s not supported.\n' |
| 74 | 'For now, we can only analyze translation units (.cc files).' % |
| 75 | filepath) |
| 76 | raise argparse.ArgumentTypeError(msg) |
| 77 | |
| 78 | |
| 79 | def Main(): |
| 80 | description = ( |
| 81 | "Run clang-tidy on single cc file.\n" |
| 82 | "Use flags, defines and include paths as in default debug build.\n" |
| 83 | "WARNING, this is a POC version with rough edges.") |
| 84 | parser = argparse.ArgumentParser(description=description) |
| 85 | parser.add_argument('filepath', |
| 86 | help='Specifies the path of the .cc file to analyze.', |
| 87 | type=ValidateCC) |
| 88 | parser.add_argument('args', |
| 89 | nargs=argparse.REMAINDER, |
| 90 | help='Arguments passed to clang-tidy') |
| 91 | parsed_args = parser.parse_args() |
| 92 | return Process(parsed_args.filepath, parsed_args.args) |
| 93 | |
| 94 | |
| 95 | if __name__ == '__main__': |
| 96 | sys.exit(Main()) |