| #!/usr/bin/env vpython3 | 
 |  | 
 | # Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. | 
 | # | 
 | # Use of this source code is governed by a BSD-style license | 
 | # that can be found in the LICENSE file in the root of the source | 
 | # tree. An additional intellectual property rights grant can be found | 
 | # in the file PATENTS.  All contributing project authors may | 
 | # be found in the AUTHORS file in the root of the source tree. | 
 | """Invoke clang-tidy tool. | 
 |  | 
 | Usage: clang_tidy.py file.cc [clang-tidy-args...] | 
 |  | 
 | Just a proof of concept! | 
 | We use an embedded clang-tidy whose version doesn't match clang++. | 
 | """ | 
 |  | 
 | import argparse | 
 | import os | 
 | import shutil | 
 | import subprocess | 
 | import sys | 
 | import tempfile | 
 | from presubmit_checks_lib.build_helpers import (GetClangTidyPath, | 
 |                                                 GetCompilationCommand) | 
 |  | 
 | # We enable all checkers by default for investigation purpose. | 
 | # This includes clang-analyzer-* checks. | 
 | # Individual checkers can be disabled via command line options. | 
 | # TODO(bugs.webrtc.com/10258): Select checkers relevant to webrtc guidelines. | 
 | CHECKER_OPTION = '-checks=*' | 
 |  | 
 |  | 
 | def Process(filepath, args): | 
 |   # Build directory is needed to gather compilation flags. | 
 |   # Create a temporary one (instead of reusing an existing one) | 
 |   # to keep the CLI simple and unencumbered. | 
 |   out_dir = tempfile.mkdtemp('clang_tidy') | 
 |  | 
 |   try: | 
 |     gn_args = []  # Use default build. | 
 |     command = GetCompilationCommand(filepath, gn_args, out_dir) | 
 |  | 
 |     # Remove warning flags. They aren't needed and they cause trouble | 
 |     # when clang-tidy doesn't match most recent clang. | 
 |     # Same battle for -f (e.g. -fcomplete-member-pointers). | 
 |     command = [ | 
 |         arg for arg in command | 
 |         if not (arg.startswith('-W') or arg.startswith('-f')) | 
 |     ] | 
 |  | 
 |     # Path from build dir. | 
 |     rel_path = os.path.relpath(os.path.abspath(filepath), out_dir) | 
 |  | 
 |     # Replace clang++ by clang-tidy | 
 |     command[0:1] = [GetClangTidyPath(), CHECKER_OPTION, rel_path | 
 |                     ] + args + ['--']  # Separator for clang flags. | 
 |     print("Running: %s" % ' '.join(command)) | 
 |     # Run from build dir so that relative paths are correct. | 
 |     p = subprocess.Popen(command, | 
 |                          cwd=out_dir, | 
 |                          stdout=sys.stdout, | 
 |                          stderr=sys.stderr) | 
 |     p.communicate() | 
 |     return p.returncode | 
 |   finally: | 
 |     shutil.rmtree(out_dir, ignore_errors=True) | 
 |  | 
 |  | 
 | def ValidateCC(filepath): | 
 |   """We can only analyze .cc files. Provide explicit message about that.""" | 
 |   if filepath.endswith('.cc'): | 
 |     return filepath | 
 |   msg = ('%s not supported.\n' | 
 |          'For now, we can only analyze translation units (.cc files).' % | 
 |          filepath) | 
 |   raise argparse.ArgumentTypeError(msg) | 
 |  | 
 |  | 
 | def Main(): | 
 |   description = ( | 
 |       "Run clang-tidy on single cc file.\n" | 
 |       "Use flags, defines and include paths as in default debug build.\n" | 
 |       "WARNING, this is a POC version with rough edges.") | 
 |   parser = argparse.ArgumentParser(description=description) | 
 |   parser.add_argument('filepath', | 
 |                       help='Specifies the path of the .cc file to analyze.', | 
 |                       type=ValidateCC) | 
 |   parser.add_argument('args', | 
 |                       nargs=argparse.REMAINDER, | 
 |                       help='Arguments passed to clang-tidy') | 
 |   parsed_args = parser.parse_args() | 
 |   return Process(parsed_args.filepath, parsed_args.args) | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |   sys.exit(Main()) |