1#!/usr/bin/env python2.7 2 3# Copyright 2019, VIXL authors 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are met: 8# 9# * Redistributions of source code must retain the above copyright notice, 10# this list of conditions and the following disclaimer. 11# * Redistributions in binary form must reproduce the above copyright notice, 12# this list of conditions and the following disclaimer in the documentation 13# and/or other materials provided with the distribution. 14# * Neither the name of ARM Limited nor the names of its contributors may be 15# used to endorse or promote products derived from this software without 16# specific prior written permission. 17# 18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND 19# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28import argparse 29import multiprocessing 30import os 31import re 32import sys 33 34from clang_format import CLANG_FORMAT_VERSION_MAJOR, CLANG_FORMAT_VERSION_MINOR 35from threaded_tests import Test, TestQueue 36import config 37import printer 38import util 39 40CLANG_TIDY_VERSION_MAJOR = CLANG_FORMAT_VERSION_MAJOR 41CLANG_TIDY_VERSION_MINOR = CLANG_FORMAT_VERSION_MINOR 42 43DEFAULT_CLANG_TIDY = \ 44 'clang-tidy-{}.{}'.format(CLANG_TIDY_VERSION_MAJOR, 45 CLANG_TIDY_VERSION_MINOR) 46 47def BuildOptions(): 48 parser = argparse.ArgumentParser( 49 description = '''This tool runs `clang-tidy` on C++ files.''', 50 # Print default values. 51 formatter_class = argparse.ArgumentDefaultsHelpFormatter) 52 parser.add_argument('files', nargs='*') 53 parser.add_argument('--jobs', '-j', metavar='N', type=int, nargs='?', 54 default=multiprocessing.cpu_count(), 55 const=multiprocessing.cpu_count(), 56 help='''Runs the tests using N jobs. If the option is set 57 but no value is provided, the script will use as many jobs 58 as it thinks useful.''') 59 parser.add_argument('--clang-tidy', default=DEFAULT_CLANG_TIDY, 60 help='Path to clang-tidy.') 61 return parser.parse_args() 62 63def ClangTidyIsAvailable(clang_tidy): 64 if not util.IsCommandAvailable(clang_tidy): 65 return False 66 cmd = '%s -version' % clang_tidy 67 rc, version = util.getstatusoutput(cmd) 68 if rc != 0: 69 util.abort("Failed to execute %s: %s" % (cmd, version)) 70 m = re.search("LLVM version (\d)\.(\d)\.\d.*$", version.decode(), re.M) 71 if not m: 72 util.abort("Failed to get clang-tidy's version: %s" % version) 73 major, minor = m.groups() 74 return int(major) == CLANG_TIDY_VERSION_MAJOR and \ 75 int(minor) == CLANG_TIDY_VERSION_MINOR 76 77def FilterClangTidyLines(lines): 78 out = [] 79 print_context = False 80 for line in lines: 81 if ("Error: no checks enabled" in line) or ("USAGE" in line): 82 # We've incorrectly invoked or configured clang-tidy. This should never 83 # happen, but it if it does, make sure that the test fails. 84 return line 85 elif "error:" in line: 86 out.append(line) 87 print_context = True 88 elif "warning:" in line: 89 out.append(line) 90 print_context = True 91 elif print_context: 92 out.append(line) 93 return "\n".join(out) 94 95def FilterFiles(list_files): 96 return list(filter(lambda x: x.endswith('.cc'), list_files)) 97 98def RunTest(test): 99 cmd = " ".join(test.args['command']) 100 rc, p_out = util.getstatusoutput(cmd) 101 if rc != 0: 102 # This usually happens when the compiler hits a '#error' because of 103 # a missing define. 104 printer.Print("%sFATAL ERROR: failed to run command '%s': %s%s" % 105 (printer.COLOUR_RED, cmd, p_out, printer.NO_COLOUR)) 106 p_out = FilterClangTidyLines(p_out.split('\n')) 107 108 failed = (len(p_out) > 0) or (rc != 0) 109 110 if failed: 111 with Test.n_tests_failed.get_lock(): Test.n_tests_failed.value += 1 112 else: 113 with Test.n_tests_passed.get_lock(): Test.n_tests_passed.value += 1 114 115 printer.__print_lock__.acquire() 116 117 printer.UpdateProgress(test.shared.start_time, 118 Test.n_tests_passed.value, 119 Test.n_tests_failed.value, 120 test.shared.n_tests, 121 Test.n_tests_skipped.value, 122 test.shared.n_known_failures, 123 test.name, 124 prevent_next_overwrite = failed , 125 has_lock = True, 126 prefix = test.shared.progress_prefix) 127 128 if failed: 129 printer.Print(printer.COLOUR_RED + 'FAILED: '+ cmd + printer.NO_COLOUR, 130 has_lock = True) 131 printer.Print(p_out, has_lock = True) 132 133 printer.__print_lock__.release() 134 135def ClangTidyFiles(files, clang_tidy, jobs = 1, progress_prefix = ''): 136 if not ClangTidyIsAvailable(clang_tidy): 137 error_message = "`{}` version {}.{} not found. Please ensure it " \ 138 "is installed, in your PATH and the correct version." \ 139 .format(clang_tidy, 140 CLANG_TIDY_VERSION_MAJOR, 141 CLANG_TIDY_VERSION_MINOR) 142 print(printer.COLOUR_RED + error_message + printer.NO_COLOUR) 143 return -1 144 145 opts = ['--', '-DVIXL_INCLUDE_TARGET_AARCH64', '-DVIXL_CODE_BUFFER_MALLOC', 146 '-DVIXL_DEBUG','-DVIXL_INCLUDE_SIMLUATOR_AARCH64', 147 '-DVIXL_INCLUDE_TARGET_A32','-DVIXL_INCLUDE_TARGET_T32', 148 '-DVIXL_INCLUDE_TARGET_A64'] 149 opts += ['-I%s' % config.dir_src_vixl] 150 opts += ['-I%s' % config.dir_tests] 151 opts += ['-I%s' % config.dir_aarch64_examples] 152 opts += ['-I%s' % config.dir_aarch32_examples] 153 154 to_check = FilterFiles(files) 155 printer.Print("clang-tidy: %d files to check" % len(to_check)) 156 157 queue = TestQueue(prefix = progress_prefix) 158 159 cpp_versions = ['c++98', 'c++11'] 160 for file in to_check: 161 for cpp_version in cpp_versions: 162 command = [clang_tidy, file] + opts + ['-std=%s' % cpp_version] 163 queue.AddTest(file, command=command) 164 165 return queue.Run(jobs, True, RunTest) 166 167if __name__ == '__main__': 168 # Parse the arguments. 169 args = BuildOptions() 170 files = args.files or util.get_source_files() 171 172 rc = ClangTidyFiles(files, args.clang_tidy, args.jobs) 173 174 sys.exit(rc) 175