-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Expand file tree
/
Copy pathautoformat
More file actions
executable file
·134 lines (102 loc) · 3.75 KB
/
Copy pathautoformat
File metadata and controls
executable file
·134 lines (102 loc) · 3.75 KB
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
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/usr/bin/env python
# License: GPLv3 Copyright: 2026, Kovid Goyal <kovid at kovidgoyal.net>
import concurrent.futures
import hashlib
import json
import os
import subprocess
import sys
import tempfile
import threading
base = os.path.dirname(os.path.abspath(__file__))
ruff = subprocess.Popen(['ruff', 'format'], cwd=base, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
go = subprocess.Popen('gofmt -s -l -w tools kittens'.split(), cwd=base, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
ruff_output = b''
go_output = b''
def wait_ruff() -> None:
global ruff_output
ruff_output = ruff.communicate()[0]
def wait_go() -> None:
global go_output
go_output = go.communicate()[0]
threading.Thread(target=wait_ruff).start()
threading.Thread(target=wait_go).start()
clang_files = []
for x in os.listdir(base):
if x in ('dist', 'build', 'bypy', '3rdparty') or x.startswith('.'):
continue
for root, dirnames, files in os.walk(os.path.join(base, x)):
for file in files:
if file.startswith('wayland-') and os.path.basename(root) == 'glfw':
continue
ext = os.path.splitext(file)[1]
if ext in ('.c', '.h', '.m', '.slang'):
clang_files.append(os.path.join(root, file))
CACHE_DIR = os.path.join(base, '.cache', 'autoformat')
CACHE_FILE = os.path.join(CACHE_DIR, 'clang_format.json')
def data_hash(src: bytes) -> str:
return hashlib.md5(src).hexdigest()
def file_hash(path: str) -> tuple[str, bytes]:
with open(path, 'rb') as f:
src = f.read()
return data_hash(src), src
def load_cache() -> dict[str, str]:
try:
with open(CACHE_FILE) as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
return {}
def save_cache(cache: dict[str, str]) -> None:
os.makedirs(CACHE_DIR, exist_ok=True)
fd, tmp = tempfile.mkstemp(dir=CACHE_DIR)
try:
with os.fdopen(fd, 'w') as f:
json.dump(cache, f, indent=2)
os.replace(tmp, CACHE_FILE)
except Exception:
os.unlink(tmp)
raise
cache: dict[str, str] = load_cache()
cache_lock = threading.Lock()
def run_clang_format(file_path: str) -> tuple[bool, str, str]:
rel_path = os.path.relpath(file_path, base)
current_hash, src = file_hash(file_path)
with cache_lock:
if cache.get(rel_path) == current_hash:
return True, '', ''
fn = file_path
sf = '.clang-format'
if file_path.endswith('.slang'):
fn += '.cs'
sf = '.clang-format-for-slang'
cmd = ['clang-format', '--style=file:' + sf, '--assume-filename=' + fn]
result = subprocess.run(cmd, capture_output=True, input=src)
if result.returncode != 0:
return False, file_path, result.stderr.decode()
if result.stdout:
new_hash = data_hash(result.stdout)
with open(file_path, 'wb') as f:
f.write(result.stdout)
else:
new_hash = current_hash
with cache_lock:
cache[rel_path] = new_hash
return True, '', ''
clang_failed = False
with concurrent.futures.ThreadPoolExecutor(max_workers=os.cpu_count()) as executor:
futures = {executor.submit(run_clang_format, f): f for f in clang_files}
for future in concurrent.futures.wait(futures)[0]:
success, file_path, error_msg = future.result()
if not success:
print(f'[FAILED] {file_path}\n{error_msg}', file=sys.stderr)
clang_failed = True
save_cache(cache)
ruff.wait()
go.wait()
if ruff.wait() != 0:
sys.stderr.buffer.write(ruff_output)
raise SystemExit('Formatting of Python code failed')
if go.wait() != 0:
sys.stderr.buffer.write(go_output)
raise SystemExit('Formatting of Go code failed')
raise SystemExit('Formatting of C files failed' if clang_failed else 0)