blob: 73cb8fd4a88a8cd4825a352e2bd2aeb53c73d923 [file] [log] [blame]
armvixl4a102ba2014-07-14 09:02:40 +01001#!/usr/bin/env python2.7
2
armvixl5289c592015-03-02 13:52:04 +00003# Copyright 2015, ARM Limited
armvixl4a102ba2014-07-14 09:02:40 +01004# 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.
28
29import argparse
30import multiprocessing
31import re
32import subprocess
33import sys
34
35import git
36import printer
37import util
38
39
40# Google's cpplint.py from depot_tools is the linter used here.
41# These are positive rules, added to the set of rules that the linter checks.
42CPP_LINTER_RULES = '''
43build/class
44build/deprecated
45build/endif_comment
46build/forward_decl
47build/include_order
48build/printf_format
49build/storage_class
50legal/copyright
51readability/boost
52readability/braces
53readability/casting
54readability/constructors
55readability/fn_size
56readability/function
57readability/multiline_comment
58readability/multiline_string
59readability/streams
60readability/utf8
61runtime/arrays
62runtime/casting
63runtime/deprecated_fn
64runtime/explicit
65runtime/int
66runtime/memset
67runtime/mutex
68runtime/nonconf
69runtime/printf
70runtime/printf_format
71runtime/references
72runtime/rtti
73runtime/sizeof
74runtime/string
75runtime/virtual
76runtime/vlog
77whitespace/blank_line
78whitespace/braces
79whitespace/comma
80whitespace/comments
81whitespace/end_of_line
82whitespace/ending_newline
83whitespace/indent
84whitespace/labels
85whitespace/line_length
86whitespace/newline
87whitespace/operators
88whitespace/parens
89whitespace/tab
90whitespace/todo
91'''.split()
92
93
94
95def BuildOptions():
96 result = argparse.ArgumentParser(
97 description =
98 '''This tool lints the C++ files tracked by the git repository, and
99 produces a summary of the errors found.''',
100 # Print default values.
101 formatter_class=argparse.ArgumentDefaultsHelpFormatter)
102 result.add_argument('--jobs', '-j', metavar='N', type=int, nargs='?',
103 default=1, const=multiprocessing.cpu_count(),
104 help='''Runs the tests using N jobs. If the option is set
105 but no value is provided, the script will use as many jobs
106 as it thinks useful.''')
107 result.add_argument('--verbose', action='store_true',
108 help='Print verbose output.')
109 return result.parse_args()
110
111
112
113__lint_results_lock__ = multiprocessing.Lock()
114
115# Returns the number of errors in the file linted.
116def Lint(filename, lint_options, progress_prefix = '', verbose = False):
117 command = ['cpplint.py', lint_options, filename]
118 process = subprocess.Popen(command,
119 stdout=subprocess.PIPE,
120 stderr=subprocess.PIPE)
121
122 # Use a lock to avoid mixing the output for different files.
123 with __lint_results_lock__:
124 # Process the output as the process is running, until it exits.
125 LINT_ERROR_LINE_REGEXP = re.compile('\[[1-5]\]$')
126 LINT_DONE_PROC_LINE_REGEXP = re.compile('Done processing')
127 LINT_STATUS_LINE_REGEXP = re.compile('Total errors found')
128 while True:
129 retcode = process.poll()
130 while True:
131 line = process.stderr.readline()
132 if line == '': break
133 output_line = progress_prefix + line.rstrip('\r\n')
134
135 if LINT_ERROR_LINE_REGEXP.search(line):
136 printer.PrintOverwritableLine(output_line, verbose = verbose)
137 printer.EnsureNewLine()
138 elif LINT_DONE_PROC_LINE_REGEXP.search(line):
139 printer.PrintOverwritableLine(output_line, verbose = verbose)
140 elif LINT_STATUS_LINE_REGEXP.search(line):
141 status_line = line
142
143 if retcode != None: break;
144
145 if retcode == 0:
146 return 0
147
148 # Return the number of errors in this file.
149 res = re.search('\d+$', status_line)
150 n_errors_str = res.string[res.start():res.end()]
151 n_errors = int(n_errors_str)
152 status_line = \
153 progress_prefix + 'Total errors found in %s : %d' % (filename, n_errors)
154 printer.PrintOverwritableLine(status_line, verbose = verbose)
155 printer.EnsureNewLine()
156 return n_errors
157
158
159# The multiprocessing map_async function does not allow passing multiple
160# arguments directly, so use a wrapper.
161def LintWrapper(args):
162 return Lint(*args)
163
164
165# Returns the total number of errors found in the files linted.
166def LintFiles(files, lint_args = CPP_LINTER_RULES, jobs = 1, verbose = False,
167 progress_prefix = ''):
168 lint_options = '--filter=-,+' + ',+'.join(lint_args)
169 pool = multiprocessing.Pool(jobs)
170 # The '.get(9999999)' is workaround to allow killing the test script with
171 # ctrl+C from the shell. This bug is documented at
172 # http://bugs.python.org/issue8296.
173 tasks = [(f, lint_options, progress_prefix, verbose) for f in files]
174 results = pool.map_async(LintWrapper, tasks).get(9999999)
175 n_errors = sum(results)
176
177 printer.PrintOverwritableLine(
178 progress_prefix + 'Total errors found: %d' % n_errors)
179 printer.EnsureNewLine()
180 return n_errors
181
182
183def IsCppLintAvailable():
184 retcode, unused_output = util.getstatusoutput('which cpplint.py')
185 return retcode == 0
186
187
188CPP_EXT_REGEXP = re.compile('\.(cc|h)$')
armvixl5289c592015-03-02 13:52:04 +0000189SIM_TRACES_REGEXP = re.compile('trace-a64\.h$')
armvixl4a102ba2014-07-14 09:02:40 +0100190def is_linter_input(filename):
191 # Don't lint the simulator traces file; it takes a very long time to check
192 # and it's (mostly) generated automatically anyway.
193 if SIM_TRACES_REGEXP.search(filename): return False
194 # Otherwise, lint all C++ files.
195 return CPP_EXT_REGEXP.search(filename) != None
196default_tracked_files = git.get_tracked_files().split()
197default_tracked_files = filter(is_linter_input, default_tracked_files)
198
199if __name__ == '__main__':
200 # Parse the arguments.
201 args = BuildOptions()
202
203 retcode = LintFiles(default_tracked_files,
204 jobs = args.jobs, verbose = args.verbose)
205 sys.exit(retcode)