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