-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Expand file tree
/
Copy pathautoformat
More file actions
executable file
·121 lines (91 loc) · 3.39 KB
/
Copy pathautoformat
File metadata and controls
executable file
·121 lines (91 loc) · 3.39 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
#!/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'):
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 file_hash(path: str) -> str:
h = hashlib.md5()
with open(path, 'rb') as f:
h.update(f.read())
return h.hexdigest()
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 = file_hash(file_path)
with cache_lock:
if cache.get(rel_path) == current_hash:
return True, '', ''
cmd = ['clang-format', '-style=file', '-i', file_path]
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
if result.returncode != 0:
return False, file_path, result.stderr
new_hash = file_hash(file_path)
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)