Repository navigation
Expand file tree
/
Copy pathclang_format_sources.py
More file actions
executable file
·219 lines (176 loc) · 7.08 KB
/
Copy pathclang_format_sources.py
File metadata and controls
executable file
·219 lines (176 loc) · 7.08 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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#!/usr/bin/env python3
"""
clang_format_sources.py — format C/C++ sources with the repo .clang-format.
Default roots: autonomy/ and autolink/. Run from the autonomy repository root.
"""
import argparse
import subprocess
import sys
import os
import shutil
from pathlib import Path
def get_project_root():
"""Return the autonomy repository root (parent of tools/)."""
return Path(__file__).resolve().parent.parent
def find_clang_format():
"""查找 clang-format 工具"""
# 首先尝试标准 PATH 中的 clang-format(包括版本化名称)
for name in ("clang-format", "clang-format-17", "clang-format-16",
"clang-format-15", "clang-format-14", "clang-format-13"):
path = shutil.which(name)
if path:
return path
# 尝试通过 xcode-select 获取的路径查找 Apple Command Line Tools
try:
xcode_path_result = subprocess.run(
["xcode-select", "-p"],
capture_output=True,
text=True,
check=False
)
if xcode_path_result.returncode == 0:
xcode_path = xcode_path_result.stdout.strip()
# 尝试在 xcode-select 返回的路径下查找 clang-format
clang_format_path = os.path.join(xcode_path, "usr", "bin", "clang-format")
if os.path.exists(clang_format_path) and os.access(clang_format_path, os.X_OK):
return clang_format_path
except (subprocess.SubprocessError, FileNotFoundError):
pass
return None
def list_source_files(root_dir, directories):
"""
列出指定目录中的 C/C++ 源文件
Args:
root_dir: 项目根目录
directories: 要搜索的目录列表(相对于 root_dir)
Returns:
文件路径列表(相对于 root_dir)
"""
files = []
root_path = Path(root_dir).resolve()
# C/C++ 文件扩展名
extensions = (".c", ".cc", ".cpp", ".cxx", ".h", ".hpp", ".hxx", ".h++")
for dir_name in directories:
# 确保目录名是相对于 root_dir 的
dir_path = root_path / dir_name
if not dir_path.exists():
print(f"Warning: Directory {dir_path} does not exist, skipping...",
file=sys.stderr)
continue
if not dir_path.is_dir():
print(f"Warning: {dir_path} is not a directory, skipping...",
file=sys.stderr)
continue
# 递归查找所有 C/C++ 源文件
for ext in extensions:
for file_path in dir_path.rglob(f"*{ext}"):
if file_path.is_file():
# 转换为相对路径(相对于 root_dir)
files.append(str(file_path.relative_to(root_path)))
# 去重并排序
files = sorted(set(files))
return files
def format_files(directories=None, dry_run=False, check=False):
"""
格式化项目中的 C/C++ 源文件
Args:
directories: 要格式化的目录列表,默认为 ["autonomy", "autolink"]
dry_run: 如果为 True,只列出将要格式化的文件,不实际格式化
check: 如果为 True,检查文件是否已格式化,不实际修改文件
Returns:
退出码(0 表示成功)
"""
root = get_project_root()
# 检查 .clang-format 文件是否存在
clang_format_config = root / ".clang-format"
if not clang_format_config.exists():
print(f"Error: .clang-format not found at {clang_format_config}",
file=sys.stderr)
return 1
# 默认格式化 autonomy 和 autolink 目录
if directories is None:
directories = ["autonomy", "autolink"]
files = list_source_files(root, directories)
if not files:
print("No C/C++ files found to format.")
return 0
if dry_run:
print(f"Found {len(files)} files that would be formatted:")
for f in files:
print(f" {f}")
return 0
# 实际格式化需要 clang-format
clang_format = find_clang_format()
if not clang_format:
print("Error: clang-format not found in PATH", file=sys.stderr)
print("Please install clang-format:", file=sys.stderr)
print(" macOS: brew install clang-format", file=sys.stderr)
print(" Linux: sudo apt-get install clang-format", file=sys.stderr)
return 1
print(f"Using clang-format: {clang_format}")
print(f"Formatting {len(files)} files...")
if check:
# 检查模式:使用 --dry-run 和 --Werror 来检查格式
cmd = [clang_format, "--dry-run", "--Werror", "--style=file"] + files
try:
subprocess.check_call(cmd, cwd=root)
print(f"All {len(files)} files are properly formatted.")
return 0
except subprocess.CalledProcessError as e:
print(f"Error: Some files are not properly formatted.", file=sys.stderr)
return e.returncode
else:
# 实际格式化模式
cmd = [clang_format, "-i", "--style=file"] + files
try:
subprocess.check_call(cmd, cwd=root)
print(f"Successfully formatted {len(files)} files.")
return 0
except subprocess.CalledProcessError as e:
print(f"Error: clang-format failed with exit code {e.returncode}",
file=sys.stderr)
return e.returncode
def main():
parser = argparse.ArgumentParser(
description="Format C/C++ source files using clang-format",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Format all files in autonomy and autolink directories
python3 tools/python/clang_format_sources.py
# List files that would be formatted (dry run)
python3 tools/python/clang_format_sources.py --dry-run
# Check if files are properly formatted
python3 tools/python/clang_format_sources.py --check
# Format only specific directories
python3 tools/python/clang_format_sources.py autonomy
python3 tools/python/clang_format_sources.py autonomy autolink
"""
)
parser.add_argument(
"directories",
nargs="*",
help="Directories to format (default: autonomy autolink)"
)
parser.add_argument(
"--dry-run",
action="store_true",
help="List files that would be formatted without actually formatting them"
)
parser.add_argument(
"--check",
action="store_true",
help="Check if files are properly formatted without modifying them"
)
args = parser.parse_args()
# --dry-run 和 --check 不能同时使用
if args.dry_run and args.check:
print("Error: --dry-run and --check cannot be used together",
file=sys.stderr)
return 1
# 如果指定了目录,使用指定的目录;否则使用 None(将使用默认值)
directories = args.directories if args.directories else None
return format_files(directories=directories, dry_run=args.dry_run,
check=args.check)
if __name__ == "__main__":
sys.exit(main())