blob: ac9b6684aa090780dcc20ab43ba8cf5d9468d529 [file] [log] [blame]
armvixl0f35e362016-05-10 13:57:58 +01001#!/usr/bin/env python2.7
2
Alexandre Ramesb78f1392016-07-01 14:22:22 +01003# Copyright 2016, VIXL authors
armvixl0f35e362016-05-10 13:57:58 +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 fnmatch
31import multiprocessing
32import os
33import signal
34import subprocess
35import sys
36import tempfile
37
38import config
39import git
40import printer
41import util
42
43
44is_output_redirected = not sys.stdout.isatty()
45
46# Catch SIGINT to gracefully exit when ctrl+C is pressed.
47def sigint_handler(signal, frame):
48 sys.exit(1)
49signal.signal(signal.SIGINT, sigint_handler)
50
51def BuildOptions():
52 parser = argparse.ArgumentParser(
53 description = '''This tool runs `clang-format` on C++ files.
54 If no files are provided on the command-line, all C++ source files in `src`,
55 `sample`, and `benchmarks` are processed.
56 When available, `colordiff` is automatically used to clour the output.''',
57 # Print default values.
58 formatter_class = argparse.ArgumentDefaultsHelpFormatter)
59 parser.add_argument('files', nargs = '*')
60 parser.add_argument('--in-place', '-i',
61 action = 'store_true', default = False,
62 help = 'Edit files in place.')
63 parser.add_argument('--jobs', '-j', metavar = 'N', type = int, nargs = '?',
64 default = multiprocessing.cpu_count(),
65 const = multiprocessing.cpu_count(),
66 help = '''Runs the tests using N jobs. If the option is set
67 but no value is provided, the script will use as many jobs
68 as it thinks useful.''')
69 return parser.parse_args()
70
71
72# Returns 0 if the file is correctly formatted, or 1 otherwise.
73def ClangFormat(filename, in_place = False, progress_prefix = ''):
74 rc = 0
75 printer.PrintOverwritableLine('Processing %s' % filename,
76 type = printer.LINE_TYPE_LINTER)
77
Pierre Langlois1bce0072017-06-06 17:58:58 +010078 cmd_format = ['clang-format-3.8', filename]
armvixl0f35e362016-05-10 13:57:58 +010079 temp_file, temp_file_name = tempfile.mkstemp(prefix = 'clang_format_')
80 cmd_format_string = '$ ' + ' '.join(cmd_format) + ' > %s' % temp_file_name
81 p_format = subprocess.Popen(cmd_format,
82 stdout = temp_file, stderr = subprocess.STDOUT)
83
84 rc += p_format.wait()
85
86 cmd_diff = ['diff', '--unified', filename, temp_file_name]
87 cmd_diff_string = '$ ' + ' '.join(cmd_diff)
88 p_diff = subprocess.Popen(cmd_diff,
89 stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
90
91 if util.IsCommandAvailable('colordiff') and not is_output_redirected:
92 p_colordiff = subprocess.Popen(
93 ['colordiff', '--unified'],
94 stdin = p_diff.stdout,
95 stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
96 out, unused = p_colordiff.communicate()
97 else:
98 out, unused = p_diff.communicate()
99
100 rc += p_diff.wait()
101
102 if in_place:
Pierre Langlois1bce0072017-06-06 17:58:58 +0100103 cmd_format = ['clang-format-3.8', '-i', filename]
armvixl0f35e362016-05-10 13:57:58 +0100104 p_format = subprocess.Popen(cmd_format,
105 stdout=temp_file, stderr=subprocess.STDOUT)
106
107 if rc != 0:
108 printer.Print('Incorrectly formatted file: ' + filename + '\n' + \
109 cmd_format_string + '\n' + \
110 cmd_diff_string + '\n' + \
111 out)
112
Jacob Bramleybe4b7522016-10-19 14:27:52 +0100113 os.remove(temp_file_name)
114
armvixl0f35e362016-05-10 13:57:58 +0100115 return 0 if rc == 0 else 1
116
117
118# The multiprocessing map_async function does not allow passing multiple
119# arguments directly, so use a wrapper.
120def ClangFormatWrapper(args):
121 # Run under a try-catch to avoid flooding the output when the script is
122 # interrupted from the keyboard with ctrl+C.
123 try:
124 return ClangFormat(*args)
125 except:
126 sys.exit(1)
127
128
129# Returns the total number of files incorrectly formatted.
130def ClangFormatFiles(files, in_place = False, jobs = 1, progress_prefix = ''):
Pierre Langlois1bce0072017-06-06 17:58:58 +0100131 if not util.IsCommandAvailable('clang-format-3.8'):
armvixl0f35e362016-05-10 13:57:58 +0100132 print(
133 printer.COLOUR_RED + \
Pierre Langlois1bce0072017-06-06 17:58:58 +0100134 ("`clang-format-3.8` not found. Please ensure it is installed "
armvixl0f35e362016-05-10 13:57:58 +0100135 "and in your PATH.") + \
136 printer.NO_COLOUR)
137 return -1
138
139 pool = multiprocessing.Pool(jobs)
140 # The '.get(9999999)' is workaround to allow killing the test script with
141 # ctrl+C from the shell. This bug is documented at
142 # http://bugs.python.org/issue8296.
143 tasks = [(f, in_place, progress_prefix) for f in files]
144 # Run under a try-catch to avoid flooding the output when the script is
145 # interrupted from the keyboard with ctrl+C.
146 try:
147 results = pool.map_async(ClangFormatWrapper, tasks).get(9999999)
148 pool.close()
149 pool.join()
150 except KeyboardInterrupt:
151 pool.terminate()
152 sys.exit(1)
153 rc = sum(results)
154
155 printer.PrintOverwritableLine(
156 progress_prefix + '%d files are incorrectly formatted.' % rc,
157 type = printer.LINE_TYPE_LINTER)
158 printer.EnsureNewLine()
159 return rc
160
161
Pierre Langloisbde2e4b2017-01-24 17:41:26 +0000162def Find(path, filters = ['*'], excluded_dir = ""):
armvixl0f35e362016-05-10 13:57:58 +0100163 files_found = []
164
165 def NameMatchesAnyFilter(name, ff):
166 for f in ff:
167 if fnmatch.fnmatch(name, f):
168 return True
169 return False
170
171 for root, dirs, files in os.walk(path):
Pierre Langloisbde2e4b2017-01-24 17:41:26 +0000172 files_found += [
173 os.path.join(root, fn)
174 for fn in files
175 # Include files which names match "filters".
176 # Exclude files for which the base directory is "excluded_dir".
177 if NameMatchesAnyFilter(os.path.relpath(fn), filters) and \
178 not os.path.dirname(os.path.join(root, fn)).endswith(excluded_dir)
179 ]
armvixl0f35e362016-05-10 13:57:58 +0100180 return files_found
181
182
183def GetCppSourceFilesToFormat():
184 sources = []
Vincent Belliard32cf2542016-07-14 10:04:09 -0700185 source_dirs = [config.dir_aarch32_benchmarks,
186 config.dir_aarch32_examples,
Alexandre Ramesd3832962016-07-04 15:03:43 +0100187 config.dir_aarch64_benchmarks,
188 config.dir_aarch64_examples,
Pierre Langloisbde2e4b2017-01-24 17:41:26 +0000189 config.dir_tests,
armvixl0f35e362016-05-10 13:57:58 +0100190 config.dir_src_vixl ]
191 for directory in source_dirs:
Pierre Langloisbde2e4b2017-01-24 17:41:26 +0000192 sources += Find(directory, ['*.h', '*.cc'], 'traces')
armvixl0f35e362016-05-10 13:57:58 +0100193 return sources
194
195
196if __name__ == '__main__':
197 # Parse the arguments.
198 args = BuildOptions()
199 files = args.files
200 if not files:
201 files = GetCppSourceFilesToFormat()
202
203 rc = ClangFormatFiles(files, in_place = args.in_place, jobs = args.jobs)
204 sys.exit(rc)