xref: /aosp_15_r20/external/deqp/scripts/src_util/run_clang_format.py (revision 35238bce31c2a825756842865a792f8cf7f89930)
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