-
Notifications
You must be signed in to change notification settings - Fork 5.1k
/
Copy pathcompile_commands.py
191 lines (156 loc) · 7.04 KB
/
compile_commands.py
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
#
# File : compile_commands.py
# This file is part of RT-Thread RTOS
# COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Change Logs:
# Date Author Notes
# 2025-03-02 ZhaoCake Create compile_commands.json without bear.
import os
import json
import re
from SCons.Script import *
def collect_compile_info(env):
"""收集编译命令和文件信息"""
print("=> Starting compile command collection")
compile_commands = []
collected_files = set()
def get_command_string(source, target, env, for_signature):
"""从SCons获取实际的编译命令"""
if env.get('CCCOMSTR'):
return env.get('CCCOM')
return '${CCCOM}'
def on_compile(target, source, env):
"""编译动作的回调函数"""
print(f" Processing compilation for {len(source)} source files")
for src in source:
src_path = str(src)
if src_path in collected_files:
continue
collected_files.add(src_path)
directory = os.path.abspath(os.path.dirname(src_path))
# 构建编译命令
command = env.subst(get_command_string(source, target, env, True))
# 解析include路径
includes = []
for path in env.get('CPPPATH', []):
includes.append('-I' + str(path))
# 添加编译命令记录
entry = {
'directory': directory,
'command': f"{command} {' '.join(includes)}",
'file': os.path.abspath(src_path),
'output': str(target[0]) if target else ''
}
compile_commands.append(entry)
print(f" Added compile command for: {os.path.basename(src_path)}")
return on_compile, compile_commands
def generate_compile_commands(env):
"""生成compile_commands.json"""
print("=> Enabling compile commands generation...")
# 获取输出路径并存储到环境变量
output_path = GetOption('compile-commands-out') or 'compile_commands.json'
env['COMPILE_COMMANDS_OUT'] = output_path
print(f" Compile commands will be written to: {os.path.abspath(output_path)}")
# 注册编译回调并保存到环境变量
callback, compile_commands = collect_compile_info(env)
env['COMPILE_COMMANDS'] = compile_commands
env.AddPreAction('*.o', callback)
print(" Registered compile command collector")
# 定义后处理动作
def write_compile_commands(target, source, env):
print("\n=> [DEBUG] Entering write_compile_commands callback")
print(f" Target: {target}")
print(f" Source: {source}")
output_path = env.get('COMPILE_COMMANDS_OUT', 'compile_commands.json')
compile_commands = env.get('COMPILE_COMMANDS', [])
try:
if not compile_commands:
print("Warning: No compile commands collected, skipping file generation")
return
print(f"\n=> Writing compile_commands.json ({len(compile_commands)} entries)")
with open(output_path, 'w') as f:
json.dump(compile_commands, f, indent=2)
print(f"=> Successfully generated: {os.path.abspath(output_path)}")
except PermissionError:
print(f"\nError: Permission denied when writing to {output_path}")
print("Please check file permissions and try again")
except Exception as e:
print(f"\nError writing compile_commands.json: {str(e)}")
import traceback
traceback.print_exc()
# 使用None作为目标以确保总是执行
print("=> Adding post-build action for compile_commands generation")
env.AddPostAction(None, write_compile_commands)
def parse_compile_paths(json_path, rt_thread_root=None):
"""解析compile_commands.json并提取RT-Thread相关的包含路径
Args:
json_path: compile_commands.json的路径
rt_thread_root: RT-Thread根目录路径,默认使用环境变量RTT_ROOT
Returns:
dict: 包含以下键的字典:
'sources': RT-Thread源文件的相对路径列表
'includes': RT-Thread头文件目录的相对路径列表
"""
if rt_thread_root is None:
rt_thread_root = os.getenv('RTT_ROOT')
if not rt_thread_root:
raise ValueError("RT-Thread根目录未指定")
rt_thread_root = os.path.abspath(rt_thread_root)
result = {
'sources': set(),
'includes': set()
}
try:
with open(json_path, 'r') as f:
compile_commands = json.load(f)
for entry in compile_commands:
# 处理源文件
src_file = entry.get('file', '')
if src_file.startswith(rt_thread_root):
rel_path = os.path.relpath(src_file, rt_thread_root)
result['sources'].add(os.path.dirname(rel_path))
# 处理包含路径
command = entry.get('command', '')
include_paths = [p[2:] for p in command.split() if p.startswith('-I')]
for inc_path in include_paths:
if inc_path.startswith(rt_thread_root):
rel_path = os.path.relpath(inc_path, rt_thread_root)
result['includes'].add(rel_path)
# 转换为排序列表
result['sources'] = sorted(list(result['sources']))
result['includes'] = sorted(list(result['includes']))
return result
except Exception as e:
print(f"Error parsing compile_commands.json: {str(e)}")
return None
def get_minimal_dist_paths(json_path=None, rt_thread_root=None):
"""获取最小化发布所需的路径
Args:
json_path: compile_commands.json的路径,默认为当前目录下的compile_commands.json
rt_thread_root: RT-Thread根目录路径
Returns:
list: 需要包含在发布包中的相对路径列表
"""
if json_path is None:
json_path = 'compile_commands.json'
paths = parse_compile_paths(json_path, rt_thread_root)
if not paths:
return []
# 合并源码和头文件路径
all_paths = set(paths['sources']) | set(paths['includes'])
return sorted(list(all_paths))