190b0414b6c794be58f34813f84c2c06e6a15be91armvixl#!/usr/bin/env python2.7 290b0414b6c794be58f34813f84c2c06e6a15be91armvixl 30cc8b6ece4b3e757e11a906a81ece292437713abarmvixl# Copyright 2015, ARM Limited 490b0414b6c794be58f34813f84c2c06e6a15be91armvixl# All rights reserved. 590b0414b6c794be58f34813f84c2c06e6a15be91armvixl# 690b0414b6c794be58f34813f84c2c06e6a15be91armvixl# Redistribution and use in source and binary forms, with or without 790b0414b6c794be58f34813f84c2c06e6a15be91armvixl# modification, are permitted provided that the following conditions are met: 890b0414b6c794be58f34813f84c2c06e6a15be91armvixl# 990b0414b6c794be58f34813f84c2c06e6a15be91armvixl# * Redistributions of source code must retain the above copyright notice, 1090b0414b6c794be58f34813f84c2c06e6a15be91armvixl# this list of conditions and the following disclaimer. 1190b0414b6c794be58f34813f84c2c06e6a15be91armvixl# * Redistributions in binary form must reproduce the above copyright notice, 1290b0414b6c794be58f34813f84c2c06e6a15be91armvixl# this list of conditions and the following disclaimer in the documentation 1390b0414b6c794be58f34813f84c2c06e6a15be91armvixl# and/or other materials provided with the distribution. 1490b0414b6c794be58f34813f84c2c06e6a15be91armvixl# * Neither the name of ARM Limited nor the names of its contributors may be 1590b0414b6c794be58f34813f84c2c06e6a15be91armvixl# used to endorse or promote products derived from this software without 1690b0414b6c794be58f34813f84c2c06e6a15be91armvixl# specific prior written permission. 1790b0414b6c794be58f34813f84c2c06e6a15be91armvixl# 1890b0414b6c794be58f34813f84c2c06e6a15be91armvixl# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND 1990b0414b6c794be58f34813f84c2c06e6a15be91armvixl# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 2090b0414b6c794be58f34813f84c2c06e6a15be91armvixl# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 2190b0414b6c794be58f34813f84c2c06e6a15be91armvixl# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 2290b0414b6c794be58f34813f84c2c06e6a15be91armvixl# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2390b0414b6c794be58f34813f84c2c06e6a15be91armvixl# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 2490b0414b6c794be58f34813f84c2c06e6a15be91armvixl# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 2590b0414b6c794be58f34813f84c2c06e6a15be91armvixl# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2690b0414b6c794be58f34813f84c2c06e6a15be91armvixl# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2790b0414b6c794be58f34813f84c2c06e6a15be91armvixl# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2890b0414b6c794be58f34813f84c2c06e6a15be91armvixl 2990b0414b6c794be58f34813f84c2c06e6a15be91armvixlimport argparse 3090b0414b6c794be58f34813f84c2c06e6a15be91armvixlimport multiprocessing 3190b0414b6c794be58f34813f84c2c06e6a15be91armvixlimport re 325ee436a3a51863de7b5dc9326cf02895248050c2armvixlimport signal 3390b0414b6c794be58f34813f84c2c06e6a15be91armvixlimport subprocess 3490b0414b6c794be58f34813f84c2c06e6a15be91armvixlimport sys 3590b0414b6c794be58f34813f84c2c06e6a15be91armvixl 365ee436a3a51863de7b5dc9326cf02895248050c2armvixlimport config 3790b0414b6c794be58f34813f84c2c06e6a15be91armvixlimport git 3890b0414b6c794be58f34813f84c2c06e6a15be91armvixlimport printer 3990b0414b6c794be58f34813f84c2c06e6a15be91armvixlimport util 4090b0414b6c794be58f34813f84c2c06e6a15be91armvixl 4190b0414b6c794be58f34813f84c2c06e6a15be91armvixl 425ee436a3a51863de7b5dc9326cf02895248050c2armvixl# Catch SIGINT to gracefully exit when ctrl+C is pressed. 435ee436a3a51863de7b5dc9326cf02895248050c2armvixldef sigint_handler(signal, frame): 445ee436a3a51863de7b5dc9326cf02895248050c2armvixl sys.exit(1) 455ee436a3a51863de7b5dc9326cf02895248050c2armvixlsignal.signal(signal.SIGINT, sigint_handler) 4690b0414b6c794be58f34813f84c2c06e6a15be91armvixl 4790b0414b6c794be58f34813f84c2c06e6a15be91armvixldef BuildOptions(): 4890b0414b6c794be58f34813f84c2c06e6a15be91armvixl result = argparse.ArgumentParser( 4990b0414b6c794be58f34813f84c2c06e6a15be91armvixl description = 5090b0414b6c794be58f34813f84c2c06e6a15be91armvixl '''This tool lints the C++ files tracked by the git repository, and 5190b0414b6c794be58f34813f84c2c06e6a15be91armvixl produces a summary of the errors found.''', 5290b0414b6c794be58f34813f84c2c06e6a15be91armvixl # Print default values. 5390b0414b6c794be58f34813f84c2c06e6a15be91armvixl formatter_class=argparse.ArgumentDefaultsHelpFormatter) 5490b0414b6c794be58f34813f84c2c06e6a15be91armvixl result.add_argument('--jobs', '-j', metavar='N', type=int, nargs='?', 5590b0414b6c794be58f34813f84c2c06e6a15be91armvixl default=1, const=multiprocessing.cpu_count(), 5690b0414b6c794be58f34813f84c2c06e6a15be91armvixl help='''Runs the tests using N jobs. If the option is set 5790b0414b6c794be58f34813f84c2c06e6a15be91armvixl but no value is provided, the script will use as many jobs 5890b0414b6c794be58f34813f84c2c06e6a15be91armvixl as it thinks useful.''') 5990b0414b6c794be58f34813f84c2c06e6a15be91armvixl return result.parse_args() 6090b0414b6c794be58f34813f84c2c06e6a15be91armvixl 6190b0414b6c794be58f34813f84c2c06e6a15be91armvixl 6290b0414b6c794be58f34813f84c2c06e6a15be91armvixl 6390b0414b6c794be58f34813f84c2c06e6a15be91armvixl__lint_results_lock__ = multiprocessing.Lock() 6490b0414b6c794be58f34813f84c2c06e6a15be91armvixl 6590b0414b6c794be58f34813f84c2c06e6a15be91armvixl# Returns the number of errors in the file linted. 665ee436a3a51863de7b5dc9326cf02895248050c2armvixldef Lint(filename, progress_prefix = ''): 675ee436a3a51863de7b5dc9326cf02895248050c2armvixl command = ['cpplint.py', filename] 6890b0414b6c794be58f34813f84c2c06e6a15be91armvixl process = subprocess.Popen(command, 6990b0414b6c794be58f34813f84c2c06e6a15be91armvixl stdout=subprocess.PIPE, 7090b0414b6c794be58f34813f84c2c06e6a15be91armvixl stderr=subprocess.PIPE) 7190b0414b6c794be58f34813f84c2c06e6a15be91armvixl 7290b0414b6c794be58f34813f84c2c06e6a15be91armvixl # Use a lock to avoid mixing the output for different files. 7390b0414b6c794be58f34813f84c2c06e6a15be91armvixl with __lint_results_lock__: 7490b0414b6c794be58f34813f84c2c06e6a15be91armvixl # Process the output as the process is running, until it exits. 7590b0414b6c794be58f34813f84c2c06e6a15be91armvixl LINT_ERROR_LINE_REGEXP = re.compile('\[[1-5]\]$') 7690b0414b6c794be58f34813f84c2c06e6a15be91armvixl LINT_DONE_PROC_LINE_REGEXP = re.compile('Done processing') 7790b0414b6c794be58f34813f84c2c06e6a15be91armvixl LINT_STATUS_LINE_REGEXP = re.compile('Total errors found') 7890b0414b6c794be58f34813f84c2c06e6a15be91armvixl while True: 7990b0414b6c794be58f34813f84c2c06e6a15be91armvixl retcode = process.poll() 8090b0414b6c794be58f34813f84c2c06e6a15be91armvixl while True: 8190b0414b6c794be58f34813f84c2c06e6a15be91armvixl line = process.stderr.readline() 8290b0414b6c794be58f34813f84c2c06e6a15be91armvixl if line == '': break 8390b0414b6c794be58f34813f84c2c06e6a15be91armvixl output_line = progress_prefix + line.rstrip('\r\n') 8490b0414b6c794be58f34813f84c2c06e6a15be91armvixl 8590b0414b6c794be58f34813f84c2c06e6a15be91armvixl if LINT_ERROR_LINE_REGEXP.search(line): 865ee436a3a51863de7b5dc9326cf02895248050c2armvixl printer.PrintOverwritableLine(output_line, 875ee436a3a51863de7b5dc9326cf02895248050c2armvixl type = printer.LINE_TYPE_LINTER) 8890b0414b6c794be58f34813f84c2c06e6a15be91armvixl printer.EnsureNewLine() 8990b0414b6c794be58f34813f84c2c06e6a15be91armvixl elif LINT_DONE_PROC_LINE_REGEXP.search(line): 905ee436a3a51863de7b5dc9326cf02895248050c2armvixl printer.PrintOverwritableLine(output_line, 915ee436a3a51863de7b5dc9326cf02895248050c2armvixl type = printer.LINE_TYPE_LINTER) 9290b0414b6c794be58f34813f84c2c06e6a15be91armvixl elif LINT_STATUS_LINE_REGEXP.search(line): 9390b0414b6c794be58f34813f84c2c06e6a15be91armvixl status_line = line 9490b0414b6c794be58f34813f84c2c06e6a15be91armvixl 9590b0414b6c794be58f34813f84c2c06e6a15be91armvixl if retcode != None: break; 9690b0414b6c794be58f34813f84c2c06e6a15be91armvixl 9790b0414b6c794be58f34813f84c2c06e6a15be91armvixl if retcode == 0: 9890b0414b6c794be58f34813f84c2c06e6a15be91armvixl return 0 9990b0414b6c794be58f34813f84c2c06e6a15be91armvixl 10090b0414b6c794be58f34813f84c2c06e6a15be91armvixl # Return the number of errors in this file. 10190b0414b6c794be58f34813f84c2c06e6a15be91armvixl res = re.search('\d+$', status_line) 10290b0414b6c794be58f34813f84c2c06e6a15be91armvixl n_errors_str = res.string[res.start():res.end()] 10390b0414b6c794be58f34813f84c2c06e6a15be91armvixl n_errors = int(n_errors_str) 10490b0414b6c794be58f34813f84c2c06e6a15be91armvixl status_line = \ 10590b0414b6c794be58f34813f84c2c06e6a15be91armvixl progress_prefix + 'Total errors found in %s : %d' % (filename, n_errors) 1065ee436a3a51863de7b5dc9326cf02895248050c2armvixl printer.PrintOverwritableLine(status_line, type = printer.LINE_TYPE_LINTER) 10790b0414b6c794be58f34813f84c2c06e6a15be91armvixl printer.EnsureNewLine() 10890b0414b6c794be58f34813f84c2c06e6a15be91armvixl return n_errors 10990b0414b6c794be58f34813f84c2c06e6a15be91armvixl 11090b0414b6c794be58f34813f84c2c06e6a15be91armvixl 11190b0414b6c794be58f34813f84c2c06e6a15be91armvixl# The multiprocessing map_async function does not allow passing multiple 11290b0414b6c794be58f34813f84c2c06e6a15be91armvixl# arguments directly, so use a wrapper. 11390b0414b6c794be58f34813f84c2c06e6a15be91armvixldef LintWrapper(args): 1145ee436a3a51863de7b5dc9326cf02895248050c2armvixl # Run under a try-catch to avoid flooding the output when the script is 1155ee436a3a51863de7b5dc9326cf02895248050c2armvixl # interrupted from the keyboard with ctrl+C. 1165ee436a3a51863de7b5dc9326cf02895248050c2armvixl try: 1175ee436a3a51863de7b5dc9326cf02895248050c2armvixl return Lint(*args) 1185ee436a3a51863de7b5dc9326cf02895248050c2armvixl except: 1195ee436a3a51863de7b5dc9326cf02895248050c2armvixl sys.exit(1) 12090b0414b6c794be58f34813f84c2c06e6a15be91armvixl 12190b0414b6c794be58f34813f84c2c06e6a15be91armvixl 12290b0414b6c794be58f34813f84c2c06e6a15be91armvixl# Returns the total number of errors found in the files linted. 1235ee436a3a51863de7b5dc9326cf02895248050c2armvixldef LintFiles(files, jobs = 1, progress_prefix = ''): 1245ee436a3a51863de7b5dc9326cf02895248050c2armvixl if not IsCppLintAvailable(): 1255ee436a3a51863de7b5dc9326cf02895248050c2armvixl print( 1265ee436a3a51863de7b5dc9326cf02895248050c2armvixl printer.COLOUR_RED + \ 1275ee436a3a51863de7b5dc9326cf02895248050c2armvixl ("cpplint.py not found. Please ensure the depot" 1285ee436a3a51863de7b5dc9326cf02895248050c2armvixl " tools are installed and in your PATH. See" 1295ee436a3a51863de7b5dc9326cf02895248050c2armvixl " http://dev.chromium.org/developers/how-tos/install-depot-tools for" 1305ee436a3a51863de7b5dc9326cf02895248050c2armvixl " details.") + \ 1315ee436a3a51863de7b5dc9326cf02895248050c2armvixl printer.NO_COLOUR) 1325ee436a3a51863de7b5dc9326cf02895248050c2armvixl return -1 1335ee436a3a51863de7b5dc9326cf02895248050c2armvixl 13490b0414b6c794be58f34813f84c2c06e6a15be91armvixl pool = multiprocessing.Pool(jobs) 13590b0414b6c794be58f34813f84c2c06e6a15be91armvixl # The '.get(9999999)' is workaround to allow killing the test script with 13690b0414b6c794be58f34813f84c2c06e6a15be91armvixl # ctrl+C from the shell. This bug is documented at 13790b0414b6c794be58f34813f84c2c06e6a15be91armvixl # http://bugs.python.org/issue8296. 1385ee436a3a51863de7b5dc9326cf02895248050c2armvixl tasks = [(f, progress_prefix) for f in files] 1395ee436a3a51863de7b5dc9326cf02895248050c2armvixl # Run under a try-catch to avoid flooding the output when the script is 1405ee436a3a51863de7b5dc9326cf02895248050c2armvixl # interrupted from the keyboard with ctrl+C. 1415ee436a3a51863de7b5dc9326cf02895248050c2armvixl try: 1425ee436a3a51863de7b5dc9326cf02895248050c2armvixl results = pool.map_async(LintWrapper, tasks).get(9999999) 1435ee436a3a51863de7b5dc9326cf02895248050c2armvixl pool.close() 1445ee436a3a51863de7b5dc9326cf02895248050c2armvixl pool.join() 1455ee436a3a51863de7b5dc9326cf02895248050c2armvixl except KeyboardInterrupt: 1465ee436a3a51863de7b5dc9326cf02895248050c2armvixl pool.terminate() 1475ee436a3a51863de7b5dc9326cf02895248050c2armvixl sys.exit(1) 14890b0414b6c794be58f34813f84c2c06e6a15be91armvixl n_errors = sum(results) 14990b0414b6c794be58f34813f84c2c06e6a15be91armvixl 15090b0414b6c794be58f34813f84c2c06e6a15be91armvixl printer.PrintOverwritableLine( 15190b0414b6c794be58f34813f84c2c06e6a15be91armvixl progress_prefix + 'Total errors found: %d' % n_errors) 15290b0414b6c794be58f34813f84c2c06e6a15be91armvixl printer.EnsureNewLine() 15390b0414b6c794be58f34813f84c2c06e6a15be91armvixl return n_errors 15490b0414b6c794be58f34813f84c2c06e6a15be91armvixl 15590b0414b6c794be58f34813f84c2c06e6a15be91armvixl 15690b0414b6c794be58f34813f84c2c06e6a15be91armvixldef IsCppLintAvailable(): 15790b0414b6c794be58f34813f84c2c06e6a15be91armvixl retcode, unused_output = util.getstatusoutput('which cpplint.py') 15890b0414b6c794be58f34813f84c2c06e6a15be91armvixl return retcode == 0 15990b0414b6c794be58f34813f84c2c06e6a15be91armvixl 16090b0414b6c794be58f34813f84c2c06e6a15be91armvixl 16190b0414b6c794be58f34813f84c2c06e6a15be91armvixlCPP_EXT_REGEXP = re.compile('\.(cc|h)$') 16290b0414b6c794be58f34813f84c2c06e6a15be91armvixldef is_linter_input(filename): 1635ee436a3a51863de7b5dc9326cf02895248050c2armvixl # lint all C++ files. 16490b0414b6c794be58f34813f84c2c06e6a15be91armvixl return CPP_EXT_REGEXP.search(filename) != None 1655ee436a3a51863de7b5dc9326cf02895248050c2armvixl 1665ee436a3a51863de7b5dc9326cf02895248050c2armvixldef GetDefaultTrackedFiles(): 1675ee436a3a51863de7b5dc9326cf02895248050c2armvixl if git.is_git_repository_root(config.dir_root): 1685ee436a3a51863de7b5dc9326cf02895248050c2armvixl default_tracked_files = git.get_tracked_files().split() 1695ee436a3a51863de7b5dc9326cf02895248050c2armvixl default_tracked_files = filter(is_linter_input, default_tracked_files) 1705ee436a3a51863de7b5dc9326cf02895248050c2armvixl return 0, default_tracked_files 1715ee436a3a51863de7b5dc9326cf02895248050c2armvixl else: 1725ee436a3a51863de7b5dc9326cf02895248050c2armvixl printer.Print(printer.COLOUR_ORANGE + 'WARNING: This script is not run ' \ 1735ee436a3a51863de7b5dc9326cf02895248050c2armvixl 'from its Git repository. The linter will not run.' + \ 1745ee436a3a51863de7b5dc9326cf02895248050c2armvixl printer.NO_COLOUR) 1755ee436a3a51863de7b5dc9326cf02895248050c2armvixl return 1, [] 17690b0414b6c794be58f34813f84c2c06e6a15be91armvixl 17790b0414b6c794be58f34813f84c2c06e6a15be91armvixlif __name__ == '__main__': 17890b0414b6c794be58f34813f84c2c06e6a15be91armvixl # Parse the arguments. 17990b0414b6c794be58f34813f84c2c06e6a15be91armvixl args = BuildOptions() 18090b0414b6c794be58f34813f84c2c06e6a15be91armvixl 1815ee436a3a51863de7b5dc9326cf02895248050c2armvixl retcode, default_tracked_files = GetDefaultTrackedFiles() 1825ee436a3a51863de7b5dc9326cf02895248050c2armvixl if retcode: 1835ee436a3a51863de7b5dc9326cf02895248050c2armvixl sys.exit(retcode) 18490b0414b6c794be58f34813f84c2c06e6a15be91armvixl retcode = LintFiles(default_tracked_files, 1855ee436a3a51863de7b5dc9326cf02895248050c2armvixl jobs = args.jobs) 18690b0414b6c794be58f34813f84c2c06e6a15be91armvixl sys.exit(retcode) 187