1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
import argparse
import os
import fileinput
import pathlib
import re
match_expressions = []
valid_extensions = []
root_dir = ''
use_batching = True
def is_header_missing(f):
with open(f) as reader:
lines = reader.read().lstrip().splitlines()
if len(lines) > 0: return not lines[0].startswith("// ")
return True
def add_headers(files, header):
for line in fileinput.input(files, inplace=True):
if fileinput.isfirstline():
[ print(h) for h in header.splitlines() ]
print(line, end="")
def scan_tree(root):
files = []
header_files = []
with os.scandir(root) as dirs:
for entry in dirs:
if entry.is_dir():
scan_tree(os.path.join(root, entry.name))
continue
full_path = os.path.join(root, entry.name)
relative_root_path = os.path.relpath(full_path, start=root_dir)
if is_matching_filename(relative_root_path):
print("... formatting: {}".format(relative_root_path))
files.append(full_path)
if is_header_missing(full_path):
header_files.append(full_path)
args = ""
if files:
if use_batching:
os.system("clang-format -i " + " ".join(files))
else:
for file in files:
os.system("clang-format -i " + file)
if header_files:
add_headers(header_files, "// Copyright Epic Games, Inc. All Rights Reserved.\n\n")
def scan_zen(root):
with os.scandir(root) as dirs:
for entry in dirs:
if entry.is_dir() and entry.name.startswith("zen"):
scan_tree(os.path.join(root, entry.name))
def is_matching_filename(relative_root_path):
global match_expressions
global root_dir
global valid_extensions
if os.path.splitext(relative_root_path)[1].lower() not in valid_extensions:
return False
if not match_expressions:
return True
relative_root_path = relative_root_path.replace('\\', '/')
for regex in match_expressions:
if regex.fullmatch(relative_root_path):
return True
return False
def parse_match_expressions(wildcards, matches):
global match_expressions
global valid_extensions
valid_extensions = ['.cpp', '.h']
for wildcard in wildcards:
regex = wildcard.replace('*', '%FORMAT_STAR%').replace('\\', '/')
regex = re.escape(regex)
regex = '.*' + regex.replace('%FORMAT_STAR%', '.*') + '.*'
try:
match_expressions.append(re.compile(regex, re.IGNORECASE))
except Exception as ex:
print(f'Could not parse input filename expression \'{wildcard}\': {str(ex)}')
quit()
for regex in matches:
try:
match_expressions.append(re.compile(regex, re.IGNORECASE))
except Exception as ex:
print(f'Could not parse input --match expression \'{regex}\': {str(ex)}')
quit()
def _main():
global root_dir, use_batching
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help="Match text for filenames. If fullpath contains text it is a match, " +\
"* is a wildcard. Directory separators are matched by either / or \\. Case insensitive.")
parser.add_argument('--match', action='append', default=[], help="Match regular expression for filenames. " +\
"Relative path from the root zen directory must be a complete match. Directory separators are matched only by /. Case insensitive.")
parser.add_argument('--batch', dest='use_batching', action='store_true', help="Enable batching calls to clang-format.")
parser.add_argument('--no-batch', dest='use_batching', action='store_false', help="Disable batching calls to clang-format.")
parser.set_defaults(use_batching=True)
options = parser.parse_args()
parse_match_expressions(options.filenames, options.match)
root_dir = pathlib.Path(__file__).parent.parent.resolve()
use_batching = options.use_batching
while True:
if (os.path.isfile(".clang-format")):
scan_zen(".")
quit()
else:
cwd = os.getcwd()
if os.path.dirname(cwd) == cwd:
quit()
os.chdir("..")
if __name__ == '__main__':
_main()
|