1*f5c631daSSadaf Ebrahimi#!/usr/bin/env python2.7 2*f5c631daSSadaf Ebrahimi 3*f5c631daSSadaf Ebrahimi# Copyright 2016, VIXL authors 4*f5c631daSSadaf Ebrahimi# All rights reserved. 5*f5c631daSSadaf Ebrahimi# 6*f5c631daSSadaf Ebrahimi# Redistribution and use in source and binary forms, with or without 7*f5c631daSSadaf Ebrahimi# modification, are permitted provided that the following conditions are met: 8*f5c631daSSadaf Ebrahimi# 9*f5c631daSSadaf Ebrahimi# * Redistributions of source code must retain the above copyright notice, 10*f5c631daSSadaf Ebrahimi# this list of conditions and the following disclaimer. 11*f5c631daSSadaf Ebrahimi# * Redistributions in binary form must reproduce the above copyright notice, 12*f5c631daSSadaf Ebrahimi# this list of conditions and the following disclaimer in the documentation 13*f5c631daSSadaf Ebrahimi# and/or other materials provided with the distribution. 14*f5c631daSSadaf Ebrahimi# * Neither the name of ARM Limited nor the names of its contributors may be 15*f5c631daSSadaf Ebrahimi# used to endorse or promote products derived from this software without 16*f5c631daSSadaf Ebrahimi# specific prior written permission. 17*f5c631daSSadaf Ebrahimi# 18*f5c631daSSadaf Ebrahimi# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND 19*f5c631daSSadaf Ebrahimi# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20*f5c631daSSadaf Ebrahimi# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21*f5c631daSSadaf Ebrahimi# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22*f5c631daSSadaf Ebrahimi# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23*f5c631daSSadaf Ebrahimi# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24*f5c631daSSadaf Ebrahimi# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25*f5c631daSSadaf Ebrahimi# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26*f5c631daSSadaf Ebrahimi# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27*f5c631daSSadaf Ebrahimi# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28*f5c631daSSadaf Ebrahimi 29*f5c631daSSadaf Ebrahimiimport argparse 30*f5c631daSSadaf Ebrahimiimport multiprocessing 31*f5c631daSSadaf Ebrahimiimport os 32*f5c631daSSadaf Ebrahimiimport re 33*f5c631daSSadaf Ebrahimiimport subprocess 34*f5c631daSSadaf Ebrahimiimport sys 35*f5c631daSSadaf Ebrahimiimport tempfile 36*f5c631daSSadaf Ebrahimi 37*f5c631daSSadaf Ebrahimifrom threaded_tests import Test, TestQueue 38*f5c631daSSadaf Ebrahimiimport printer 39*f5c631daSSadaf Ebrahimiimport util 40*f5c631daSSadaf Ebrahimi 41*f5c631daSSadaf EbrahimiCLANG_FORMAT_VERSION_MAJOR = 4 42*f5c631daSSadaf EbrahimiCLANG_FORMAT_VERSION_MINOR = 0 43*f5c631daSSadaf Ebrahimi 44*f5c631daSSadaf EbrahimiDEFAULT_CLANG_FORMAT = \ 45*f5c631daSSadaf Ebrahimi 'clang-format-{}.{}'.format(CLANG_FORMAT_VERSION_MAJOR, 46*f5c631daSSadaf Ebrahimi CLANG_FORMAT_VERSION_MINOR) 47*f5c631daSSadaf Ebrahimi 48*f5c631daSSadaf Ebrahimiis_output_redirected = not sys.stdout.isatty() 49*f5c631daSSadaf Ebrahimi 50*f5c631daSSadaf Ebrahimidef BuildOptions(): 51*f5c631daSSadaf Ebrahimi parser = argparse.ArgumentParser( 52*f5c631daSSadaf Ebrahimi description = '''This tool runs `clang-format` on C++ files. 53*f5c631daSSadaf Ebrahimi If no files are provided on the command-line, all C++ source files are 54*f5c631daSSadaf Ebrahimi processed, except for the test traces. 55*f5c631daSSadaf Ebrahimi When available, `colordiff` is automatically used to colour the output.''', 56*f5c631daSSadaf Ebrahimi # Print default values. 57*f5c631daSSadaf Ebrahimi formatter_class = argparse.ArgumentDefaultsHelpFormatter) 58*f5c631daSSadaf Ebrahimi parser.add_argument('files', nargs = '*') 59*f5c631daSSadaf Ebrahimi parser.add_argument('--clang-format', default=DEFAULT_CLANG_FORMAT, 60*f5c631daSSadaf Ebrahimi help='Path to clang-format.') 61*f5c631daSSadaf Ebrahimi parser.add_argument('--in-place', '-i', 62*f5c631daSSadaf Ebrahimi action = 'store_true', default = False, 63*f5c631daSSadaf Ebrahimi help = 'Edit files in place.') 64*f5c631daSSadaf Ebrahimi parser.add_argument('--jobs', '-j', metavar = 'N', type = int, nargs = '?', 65*f5c631daSSadaf Ebrahimi default = multiprocessing.cpu_count(), 66*f5c631daSSadaf Ebrahimi const = multiprocessing.cpu_count(), 67*f5c631daSSadaf Ebrahimi help = '''Runs the tests using N jobs. If the option is set 68*f5c631daSSadaf Ebrahimi but no value is provided, the script will use as many jobs 69*f5c631daSSadaf Ebrahimi as it thinks useful.''') 70*f5c631daSSadaf Ebrahimi return parser.parse_args() 71*f5c631daSSadaf Ebrahimi 72*f5c631daSSadaf Ebrahimi 73*f5c631daSSadaf Ebrahimidef ClangFormatIsAvailable(clang_format): 74*f5c631daSSadaf Ebrahimi if not util.IsCommandAvailable(clang_format): 75*f5c631daSSadaf Ebrahimi return False 76*f5c631daSSadaf Ebrahimi cmd = '%s -version' % clang_format 77*f5c631daSSadaf Ebrahimi rc, version = util.getstatusoutput(cmd) 78*f5c631daSSadaf Ebrahimi if rc != 0: 79*f5c631daSSadaf Ebrahimi util.abort("Failed to execute %s: %s" % (cmd, version)) 80*f5c631daSSadaf Ebrahimi m = re.search("^clang-format version (\d)\.(\d)\.\d.*$", 81*f5c631daSSadaf Ebrahimi version.decode(), re.M) 82*f5c631daSSadaf Ebrahimi if not m: 83*f5c631daSSadaf Ebrahimi util.abort("Failed to get clang-format's version: %s" % version) 84*f5c631daSSadaf Ebrahimi major, minor = m.groups() 85*f5c631daSSadaf Ebrahimi return int(major) == CLANG_FORMAT_VERSION_MAJOR and \ 86*f5c631daSSadaf Ebrahimi int(minor) == CLANG_FORMAT_VERSION_MINOR 87*f5c631daSSadaf Ebrahimi 88*f5c631daSSadaf Ebrahimidef RunTest(test): 89*f5c631daSSadaf Ebrahimi filename = test.args['filename'] 90*f5c631daSSadaf Ebrahimi clang_format = test.args['clang_format'] 91*f5c631daSSadaf Ebrahimi in_place = test.args['in_place'] 92*f5c631daSSadaf Ebrahimi 93*f5c631daSSadaf Ebrahimi rc = 0 94*f5c631daSSadaf Ebrahimi 95*f5c631daSSadaf Ebrahimi cmd_format = [clang_format, filename] 96*f5c631daSSadaf Ebrahimi temp_file, temp_file_name = tempfile.mkstemp(prefix = 'clang_format_') 97*f5c631daSSadaf Ebrahimi cmd_format_string = '$ ' + ' '.join(cmd_format) + ' > %s' % temp_file_name 98*f5c631daSSadaf Ebrahimi p_format = subprocess.Popen(cmd_format, 99*f5c631daSSadaf Ebrahimi stdout = temp_file, stderr = subprocess.STDOUT) 100*f5c631daSSadaf Ebrahimi 101*f5c631daSSadaf Ebrahimi rc += p_format.wait() 102*f5c631daSSadaf Ebrahimi 103*f5c631daSSadaf Ebrahimi cmd_diff = ['diff', '--unified', filename, temp_file_name] 104*f5c631daSSadaf Ebrahimi cmd_diff_string = '$ ' + ' '.join(cmd_diff) 105*f5c631daSSadaf Ebrahimi p_diff = subprocess.Popen(cmd_diff, 106*f5c631daSSadaf Ebrahimi stdout = subprocess.PIPE, stderr = subprocess.STDOUT) 107*f5c631daSSadaf Ebrahimi 108*f5c631daSSadaf Ebrahimi if util.IsCommandAvailable('colordiff') and not is_output_redirected: 109*f5c631daSSadaf Ebrahimi p_colordiff = subprocess.Popen( 110*f5c631daSSadaf Ebrahimi ['colordiff', '--unified'], 111*f5c631daSSadaf Ebrahimi stdin = p_diff.stdout, 112*f5c631daSSadaf Ebrahimi stdout = subprocess.PIPE, stderr = subprocess.STDOUT) 113*f5c631daSSadaf Ebrahimi out, unused = p_colordiff.communicate() 114*f5c631daSSadaf Ebrahimi else: 115*f5c631daSSadaf Ebrahimi out, unused = p_diff.communicate() 116*f5c631daSSadaf Ebrahimi 117*f5c631daSSadaf Ebrahimi rc += p_diff.wait() 118*f5c631daSSadaf Ebrahimi 119*f5c631daSSadaf Ebrahimi if in_place: 120*f5c631daSSadaf Ebrahimi cmd_format = [clang_format, '-i', filename] 121*f5c631daSSadaf Ebrahimi p_format = subprocess.Popen(cmd_format, 122*f5c631daSSadaf Ebrahimi stdout=temp_file, stderr=subprocess.STDOUT) 123*f5c631daSSadaf Ebrahimi 124*f5c631daSSadaf Ebrahimi if rc != 0: 125*f5c631daSSadaf Ebrahimi with Test.n_tests_failed.get_lock(): Test.n_tests_failed.value += 1 126*f5c631daSSadaf Ebrahimi else: 127*f5c631daSSadaf Ebrahimi with Test.n_tests_passed.get_lock(): Test.n_tests_passed.value += 1 128*f5c631daSSadaf Ebrahimi 129*f5c631daSSadaf Ebrahimi printer.__print_lock__.acquire() 130*f5c631daSSadaf Ebrahimi 131*f5c631daSSadaf Ebrahimi printer.UpdateProgress(test.shared.start_time, 132*f5c631daSSadaf Ebrahimi Test.n_tests_passed.value, 133*f5c631daSSadaf Ebrahimi Test.n_tests_failed.value, 134*f5c631daSSadaf Ebrahimi test.shared.n_tests, 135*f5c631daSSadaf Ebrahimi Test.n_tests_skipped.value, 136*f5c631daSSadaf Ebrahimi test.shared.n_known_failures, 137*f5c631daSSadaf Ebrahimi test.name, 138*f5c631daSSadaf Ebrahimi prevent_next_overwrite = rc != 0, 139*f5c631daSSadaf Ebrahimi has_lock = True, 140*f5c631daSSadaf Ebrahimi prefix = test.shared.progress_prefix) 141*f5c631daSSadaf Ebrahimi 142*f5c631daSSadaf Ebrahimi if rc != 0: 143*f5c631daSSadaf Ebrahimi printer.Print('Incorrectly formatted file: ' + filename + '\n' + \ 144*f5c631daSSadaf Ebrahimi cmd_format_string + '\n' + \ 145*f5c631daSSadaf Ebrahimi cmd_diff_string + '\n' + \ 146*f5c631daSSadaf Ebrahimi out, has_lock = True) 147*f5c631daSSadaf Ebrahimi printer.__print_lock__.release() 148*f5c631daSSadaf Ebrahimi 149*f5c631daSSadaf Ebrahimi os.remove(temp_file_name) 150*f5c631daSSadaf Ebrahimi 151*f5c631daSSadaf Ebrahimi# Returns the total number of files incorrectly formatted. 152*f5c631daSSadaf Ebrahimidef ClangFormatFiles(files, clang_format, in_place = False, jobs = 1, 153*f5c631daSSadaf Ebrahimi progress_prefix = ''): 154*f5c631daSSadaf Ebrahimi if not ClangFormatIsAvailable(clang_format): 155*f5c631daSSadaf Ebrahimi error_message = "`{}` version {}.{} not found. Please ensure it " \ 156*f5c631daSSadaf Ebrahimi "is installed, in your PATH and the correct version." \ 157*f5c631daSSadaf Ebrahimi .format(clang_format, 158*f5c631daSSadaf Ebrahimi CLANG_FORMAT_VERSION_MAJOR, 159*f5c631daSSadaf Ebrahimi CLANG_FORMAT_VERSION_MINOR) 160*f5c631daSSadaf Ebrahimi print(printer.COLOUR_RED + error_message + printer.NO_COLOUR) 161*f5c631daSSadaf Ebrahimi return -1 162*f5c631daSSadaf Ebrahimi 163*f5c631daSSadaf Ebrahimi queue = TestQueue(prefix = progress_prefix) 164*f5c631daSSadaf Ebrahimi for f in files: 165*f5c631daSSadaf Ebrahimi queue.AddTest(f, filename = f, clang_format = clang_format, in_place = in_place) 166*f5c631daSSadaf Ebrahimi 167*f5c631daSSadaf Ebrahimi rc = queue.Run(jobs, True, RunTest) 168*f5c631daSSadaf Ebrahimi 169*f5c631daSSadaf Ebrahimi printer.PrintOverwritableLine( 170*f5c631daSSadaf Ebrahimi progress_prefix + '%d files are incorrectly formatted.' % rc, 171*f5c631daSSadaf Ebrahimi type = printer.LINE_TYPE_LINTER) 172*f5c631daSSadaf Ebrahimi printer.EnsureNewLine() 173*f5c631daSSadaf Ebrahimi 174*f5c631daSSadaf Ebrahimi return rc 175*f5c631daSSadaf Ebrahimi 176*f5c631daSSadaf Ebrahimiif __name__ == '__main__': 177*f5c631daSSadaf Ebrahimi # Parse the arguments. 178*f5c631daSSadaf Ebrahimi args = BuildOptions() 179*f5c631daSSadaf Ebrahimi files = args.files or util.get_source_files() 180*f5c631daSSadaf Ebrahimi 181*f5c631daSSadaf Ebrahimi rc = ClangFormatFiles(files, clang_format = args.clang_format, 182*f5c631daSSadaf Ebrahimi in_place = args.in_place, jobs = args.jobs) 183*f5c631daSSadaf Ebrahimi 184*f5c631daSSadaf Ebrahimi sys.exit(rc) 185