You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
7.2 KiB
204 lines
7.2 KiB
#!/usr/bin/env python2.7 |
|
|
|
# Copyright 2016, VIXL authors |
|
# All rights reserved. |
|
# |
|
# Redistribution and use in source and binary forms, with or without |
|
# modification, are permitted provided that the following conditions are met: |
|
# |
|
# * Redistributions of source code must retain the above copyright notice, |
|
# this list of conditions and the following disclaimer. |
|
# * Redistributions in binary form must reproduce the above copyright notice, |
|
# this list of conditions and the following disclaimer in the documentation |
|
# and/or other materials provided with the distribution. |
|
# * Neither the name of ARM Limited nor the names of its contributors may be |
|
# used to endorse or promote products derived from this software without |
|
# specific prior written permission. |
|
# |
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND |
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE |
|
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
|
|
import argparse |
|
import fnmatch |
|
import multiprocessing |
|
import os |
|
import signal |
|
import subprocess |
|
import sys |
|
import tempfile |
|
|
|
import config |
|
import git |
|
import printer |
|
import util |
|
|
|
|
|
is_output_redirected = not sys.stdout.isatty() |
|
|
|
# Catch SIGINT to gracefully exit when ctrl+C is pressed. |
|
def sigint_handler(signal, frame): |
|
sys.exit(1) |
|
signal.signal(signal.SIGINT, sigint_handler) |
|
|
|
def BuildOptions(): |
|
parser = argparse.ArgumentParser( |
|
description = '''This tool runs `clang-format` on C++ files. |
|
If no files are provided on the command-line, all C++ source files in `src`, |
|
`sample`, and `benchmarks` are processed. |
|
When available, `colordiff` is automatically used to clour the output.''', |
|
# Print default values. |
|
formatter_class = argparse.ArgumentDefaultsHelpFormatter) |
|
parser.add_argument('files', nargs = '*') |
|
parser.add_argument('--in-place', '-i', |
|
action = 'store_true', default = False, |
|
help = 'Edit files in place.') |
|
parser.add_argument('--jobs', '-j', metavar = 'N', type = int, nargs = '?', |
|
default = multiprocessing.cpu_count(), |
|
const = multiprocessing.cpu_count(), |
|
help = '''Runs the tests using N jobs. If the option is set |
|
but no value is provided, the script will use as many jobs |
|
as it thinks useful.''') |
|
return parser.parse_args() |
|
|
|
|
|
# Returns 0 if the file is correctly formatted, or 1 otherwise. |
|
def ClangFormat(filename, in_place = False, progress_prefix = ''): |
|
rc = 0 |
|
printer.PrintOverwritableLine('Processing %s' % filename, |
|
type = printer.LINE_TYPE_LINTER) |
|
|
|
cmd_format = ['clang-format-3.6', filename] |
|
temp_file, temp_file_name = tempfile.mkstemp(prefix = 'clang_format_') |
|
cmd_format_string = '$ ' + ' '.join(cmd_format) + ' > %s' % temp_file_name |
|
p_format = subprocess.Popen(cmd_format, |
|
stdout = temp_file, stderr = subprocess.STDOUT) |
|
|
|
rc += p_format.wait() |
|
|
|
cmd_diff = ['diff', '--unified', filename, temp_file_name] |
|
cmd_diff_string = '$ ' + ' '.join(cmd_diff) |
|
p_diff = subprocess.Popen(cmd_diff, |
|
stdout = subprocess.PIPE, stderr = subprocess.STDOUT) |
|
|
|
if util.IsCommandAvailable('colordiff') and not is_output_redirected: |
|
p_colordiff = subprocess.Popen( |
|
['colordiff', '--unified'], |
|
stdin = p_diff.stdout, |
|
stdout = subprocess.PIPE, stderr = subprocess.STDOUT) |
|
out, unused = p_colordiff.communicate() |
|
else: |
|
out, unused = p_diff.communicate() |
|
|
|
rc += p_diff.wait() |
|
|
|
if in_place: |
|
cmd_format = ['clang-format-3.6', '-i', filename] |
|
p_format = subprocess.Popen(cmd_format, |
|
stdout=temp_file, stderr=subprocess.STDOUT) |
|
|
|
if rc != 0: |
|
printer.Print('Incorrectly formatted file: ' + filename + '\n' + \ |
|
cmd_format_string + '\n' + \ |
|
cmd_diff_string + '\n' + \ |
|
out) |
|
|
|
os.remove(temp_file_name) |
|
|
|
return 0 if rc == 0 else 1 |
|
|
|
|
|
# The multiprocessing map_async function does not allow passing multiple |
|
# arguments directly, so use a wrapper. |
|
def ClangFormatWrapper(args): |
|
# Run under a try-catch to avoid flooding the output when the script is |
|
# interrupted from the keyboard with ctrl+C. |
|
try: |
|
return ClangFormat(*args) |
|
except: |
|
sys.exit(1) |
|
|
|
|
|
# Returns the total number of files incorrectly formatted. |
|
def ClangFormatFiles(files, in_place = False, jobs = 1, progress_prefix = ''): |
|
if not util.IsCommandAvailable('clang-format-3.6'): |
|
print( |
|
printer.COLOUR_RED + \ |
|
("`clang-format-3.6` not found. Please ensure it is installed " |
|
"and in your PATH.") + \ |
|
printer.NO_COLOUR) |
|
return -1 |
|
|
|
pool = multiprocessing.Pool(jobs) |
|
# The '.get(9999999)' is workaround to allow killing the test script with |
|
# ctrl+C from the shell. This bug is documented at |
|
# http://bugs.python.org/issue8296. |
|
tasks = [(f, in_place, progress_prefix) for f in files] |
|
# Run under a try-catch to avoid flooding the output when the script is |
|
# interrupted from the keyboard with ctrl+C. |
|
try: |
|
results = pool.map_async(ClangFormatWrapper, tasks).get(9999999) |
|
pool.close() |
|
pool.join() |
|
except KeyboardInterrupt: |
|
pool.terminate() |
|
sys.exit(1) |
|
rc = sum(results) |
|
|
|
printer.PrintOverwritableLine( |
|
progress_prefix + '%d files are incorrectly formatted.' % rc, |
|
type = printer.LINE_TYPE_LINTER) |
|
printer.EnsureNewLine() |
|
return rc |
|
|
|
|
|
def Find(path, filters = ['*'], excluded_dir = ""): |
|
files_found = [] |
|
|
|
def NameMatchesAnyFilter(name, ff): |
|
for f in ff: |
|
if fnmatch.fnmatch(name, f): |
|
return True |
|
return False |
|
|
|
for root, dirs, files in os.walk(path): |
|
files_found += [ |
|
os.path.join(root, fn) |
|
for fn in files |
|
# Include files which names match "filters". |
|
# Exclude files for which the base directory is "excluded_dir". |
|
if NameMatchesAnyFilter(os.path.relpath(fn), filters) and \ |
|
not os.path.dirname(os.path.join(root, fn)).endswith(excluded_dir) |
|
] |
|
return files_found |
|
|
|
|
|
def GetCppSourceFilesToFormat(): |
|
sources = [] |
|
source_dirs = [config.dir_aarch32_benchmarks, |
|
config.dir_aarch32_examples, |
|
config.dir_aarch64_benchmarks, |
|
config.dir_aarch64_examples, |
|
config.dir_tests, |
|
config.dir_src_vixl ] |
|
for directory in source_dirs: |
|
sources += Find(directory, ['*.h', '*.cc'], 'traces') |
|
return sources |
|
|
|
|
|
if __name__ == '__main__': |
|
# Parse the arguments. |
|
args = BuildOptions() |
|
files = args.files |
|
if not files: |
|
files = GetCppSourceFilesToFormat() |
|
|
|
rc = ClangFormatFiles(files, in_place = args.in_place, jobs = args.jobs) |
|
sys.exit(rc)
|
|
|