-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathformat.py
More file actions
130 lines (110 loc) · 3.58 KB
/
Copy pathformat.py
File metadata and controls
130 lines (110 loc) · 3.58 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
import argparse
import subprocess
import sys
from pathlib import Path
from common import find_files
class DefaultParameters:
modified_files: bool = False
search_paths: list[str] = ["include", "tests", "benchmarks"]
file_patterns: list[str] = ["*.cpp", "*.hpp", "*.c", "*.h"]
exclude_paths: list[str] = ["tests/external"]
check: bool = False
clang_format_executable: str = "clang-format"
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument(
"-m",
"--modified-files",
default=DefaultParameters.modified_files,
action=argparse.BooleanOptionalAction,
help="run clang-format only on the files modified since last pushed commit",
)
parser.add_argument(
"-p",
"--search-paths",
type=str,
default=DefaultParameters.search_paths,
nargs="*",
help="list of search directory paths",
)
parser.add_argument(
"-f",
"--file-patterns",
type=str,
default=DefaultParameters.file_patterns,
nargs="*",
help="list of file patterns to include",
)
parser.add_argument(
"-e",
"--exclude-paths",
type=str,
default=DefaultParameters.exclude_paths,
nargs="*",
help="list of directory paths to exclude",
)
parser.add_argument(
"-c",
"--check",
default=DefaultParameters.check,
action=argparse.BooleanOptionalAction,
help="run format check",
)
parser.add_argument(
"-exe",
"--clang-format-executable",
type=str,
default=DefaultParameters.clang_format_executable,
help="path or name of the clang-format executable (default: clang-format)",
)
return vars(parser.parse_args())
def get_modified_files(files: set[Path]) -> set[Path]:
try:
result = subprocess.run(
"git diff --name-only @{u}".split(),
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
modified_files = {Path(file) for file in result.stdout.splitlines() if file}
return modified_files & files
except subprocess.CalledProcessError as e:
print(f"Error executing git command: {e.stderr}")
raise RuntimeError("Failed to retrieve the modified files.")
def run_clang_format(clang_format_exec: str, files: set[Path], check: bool) -> int:
n_files = len(files)
if check:
print(f"Files to check: {n_files}")
else:
print(f"Files to format: {n_files}")
return_code = 0
for i, file in enumerate(files):
print(f"[{i + 1}/{n_files}] {file}")
cmd = [clang_format_exec, str(file)]
if check:
cmd.extend(["--dry-run", "--Werror"])
else:
cmd.append("-i")
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
return_code = result.returncode
print(
f"[Format error]\n[stdout]\n{result.stdout}\n[stderr]\n{result.stderr}"
)
print("Done!")
return return_code
def main(
modified_files: bool,
search_paths: list[str],
file_patterns: list[str],
exclude_paths: list[str],
check: bool,
clang_format_executable: str,
):
files_to_format = find_files(search_paths, file_patterns, exclude_paths)
if modified_files:
files_to_format = get_modified_files(files_to_format)
sys.exit(run_clang_format(clang_format_executable, files_to_format, check))
if __name__ == "__main__":
main(**parse_args())