123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- #
- # 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))
|