1#!/usr/bin/env python3 2 3# Clang Format Helper 4# ------------------- 5# 6# Copyright (c) 2023 The Khronos Group Inc. 7# Copyright (c) 2023 Google LLC 8# 9# Licensed under the Apache License, Version 2.0 (the "License"); 10# you may not use this file except in compliance with the License. 11# You may obtain a copy of the License at 12# 13# http://www.apache.org/licenses/LICENSE-2.0 14# 15# Unless required by applicable law or agreed to in writing, software 16# distributed under the License is distributed on an "AS IS" BASIS, 17# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18# See the License for the specific language governing permissions and 19# limitations under the License. 20# 21# This script retrieves the list of files to format from git and feeds it to 22# clang-format. It requires the following environment variable: 23# 24# CTS_CLANG_PATH=/path/to/clang-format 25# 26# which should point to clang-format from llvm 16.0.4 27# 28# Usage: python3 run-clang-format.py 29# 30# -v: verbose output 31 32import argparse 33import multiprocessing 34import os 35import re 36import sys 37import time 38 39from common import getFilesModifiedSinceLastCommit, runCommandAsync, waitAsyncCommand 40 41def getClangFormat(): 42 clang_format = os.environ.get('CTS_CLANG_FORMAT') 43 if clang_format is not None: 44 return clang_format 45 46 print('CTS_CLANG_FORMAT is not defined. Set this environment variable to path to clang_format') 47 if not sys.platform.startswith('linux'): 48 sys.exit(1) 49 50 print('Falling back to scripts/src_util/clang-format') 51 clang_format = os.path.join(os.path.dirname(__file__), "clang-format") 52 return clang_format 53 54def runClangFormat(files, thread_count, verbose): 55 clang_format = getClangFormat() 56 57 processes = [] 58 59 total = len(files) 60 so_far = 0 61 62 last_report_time = time.time() 63 64 for file in files: 65 so_far = so_far + 1 66 if verbose: 67 print('Formatting {}'.format(file)) 68 elif last_report_time + 1 < time.time(): 69 print('\rFormatting {}/{}'.format(so_far, total), end = '') 70 last_report_time = last_report_time + 1 71 72 # Make sure a maximum of thread_count processes are in flight 73 if len(processes) > thread_count: 74 waitAsyncCommand(*processes[0]) 75 processes = processes[1:] 76 77 command = [clang_format, file, '-i'] 78 processes.append((runCommandAsync(command), command)) 79 80 for process in processes: 81 waitAsyncCommand(*process) 82 83 print('\rFormatted {} '.format(total)) 84 85def runClangFormatOnModifiedFiles(verbose): 86 files = getFilesModifiedSinceLastCommit() 87 88 # Only format files that can be formatted by clang-format. 89 pattern = (r'.*\.(cpp|hpp|c|h|m|mm|hh|inc|js|java|json)') 90 files = [file for file in files if re.match('^%s$' % pattern, file, re.IGNORECASE)] 91 files = [f for f in files if 'vulkancts/scripts/src' not in f.replace('\\', '/')] 92 files = [f for f in files if not re.match('.*external/openglcts/modules/runner/.*Mustpass.*\.hpp', f.replace('\\', '/'))] 93 94 thread_count = min(8, multiprocessing.cpu_count()) 95 runClangFormat(files, thread_count, verbose) 96 return True 97 98if __name__ == "__main__": 99 parser = argparse.ArgumentParser( 100 description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter 101 ) 102 parser.add_argument("-v", action="store_true", default=False, help="verbose") 103 args = parser.parse_args() 104 runClangFormatOnModifiedFiles(args.v) 105