☰
RT-Thread NG 构建系统深度解析:面向对象重构、SCons 集成与工具链扩展实战
2026/10/7 9:25:31 网站建设 项目流程
  • 操作系统
  • 嵌入式
  • 物联网
  • 嵌入式OS
  • RTOS

【免费下载链接】rt-thread

RT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/

项目地址:https://gitcode.com/gh_mirrors/rt/rt-thread
点击查看免费下载

RT-Thread NG(Next Generation)构建系统是 RT-Thread 对既有 SCons 构建体系的面向对象重构方案,位于 tools/ng 目录。它以BuildContext为核心,将配置解析、工程分组、工具链与项目生成器统一抽象为可插拔模块,同时通过适配层保证与既有 tools/building.py 完全向后兼容。本文将以仓库中的 tools/ng/README.md 为骨架,结合各模块源码逐层拆解 NG 系统的架构设计、最小化接入步骤、环境方法 API、自定义工具链与生成器的高级玩法,以及迁移与性能优化要点,帮助你在不修改现有 SConscript 的前提下平滑升级构建链路。

NG 构建系统是什么

RT-Thread 传统的构建系统以 tools/building.py 中的全局函数(DefineGroup、GetDepend、MergeGroup等)为入口,大量依赖模块级全局变量(如Env、Projects、Rtt_Root)在 SConscript 与构建脚本之间传递状态。随着 BSP 数量与组件体系不断膨胀,这种过程式设计在职责划分、依赖管理与扩展性上逐渐暴露瓶颈。

NG 系统正是对这一体系的面向对象重构,其核心目标在 README 中明确为:

  • ✅完全向后兼容:现有的 SConscript 无需修改;
  • ✅面向对象设计:清晰的类层次结构和职责分离;
  • ✅SCons 最佳实践:充分利用 SCons 的Environment对象;
  • ✅可扩展架构:易于添加新的工具链和项目生成器;
  • ✅类型安全:更好的类型提示和错误处理。

从实现细节看,NG 系统是**可选启用(opt-in)**的设计:building.py中通过try/except ImportError探测ng包,模块不存在时自动回退到旧实现,因此接入风险被控制在极小范围。

架构设计:核心模块与类图

模块划分

ng/ ├── __init__.py # 包初始化(导出核心类,__version__ = "1.0.0") ├── core.py # 核心类:BuildContext ├── environment.py # 环境扩展:RTEnv类,注入到SCons Environment ├── config.py # 配置管理:解析rtconfig.h ├── project.py # 项目管理:ProjectGroup和Registry ├── toolchain.py # 工具链抽象:GCC、Keil、IAR等 ├── generator.py # 项目生成器:VS Code、CMake等 ├── utils.py # 工具函数:路径、版本等 ├── adapter.py # 适配器:与building.py集成 ├── building_ng.py # 示例:最小化修改的building.py └── integration_example.py # 集成变更点注释示例

各模块职责单一:

  • tools/ng/core.py 中的BuildContext是中央构建上下文,负责协调所有组件。它在初始化时记录root_directory(RT-Thread 根目录)与bsp_directory(当前 BSP 目录,即os.getcwd()),并组装出四个管理器实例:ConfigManager、ProjectRegistry、ToolchainManager、GeneratorRegistry,外加PathService与一个格式为[%(levelname)s] %(message)s的日志器(logger 名为rtthread.build)。它还通过类变量_current_context维护“当前上下文”的单例引用,供全局函数与注入方法随时取用。
  • tools/ng/environment.py 中的RTEnv通过env.AddMethod(...)把 RT-Thread 特有方法注入 SConsEnvironment对象。
  • tools/ng/config.py 提供ConfigParser与ConfigManager,负责解析 BSP 下的rtconfig.h。
  • tools/ng/project.py 以数据类ProjectGroup封装一个组件的全部构建信息,ProjectRegistry负责注册、查询与合并所有组。
  • tools/ng/toolchain.py 定义抽象基类Toolchain,并内置GccToolchain、ArmccToolchain、IarToolchain三种实现。
  • tools/ng/generator.py 定义抽象基类ProjectGenerator,内置VscodeGenerator与CMakeGenerator。
  • tools/ng/utils.py 提供PathService(跨平台路径规范化)、PlatformInfo、FileUtils、VersionUtils等通用工具。
  • tools/ng/adapter.py 是连接新旧两套体系的桥梁,提供init_build_context、inject_environment_methods、load_rtconfig以及旧式全局函数DefineGroup/GetDepend/MergeGroups/GenerateProject的兼容实现。

类图与协作关系

README 中用 Mermaid 描述了核心类的关系:

对照源码可以进一步还原协作细节:

  • BuildContext.prepare_environment(env)会设置env['RTT_ROOT']与env['BSP_ROOT'],并把仓库根目录的tools目录插入sys.path(见 tools/ng/core.py)。
  • BuildContext.load_configuration('rtconfig.h')在 BSP 目录下查找配置文件,存在则交给config_manager.load_from_file解析并缓存全部选项,缺失则仅记录 warning(见 tools/ng/core.py)。
  • ProjectRegistry.merge_groups(env)汇总所有已注册组产生的构建对象,get_project_info()则聚合所有组的源文件、头文件路径、宏定义、库与库路径,供项目生成器消费(见 tools/ng/project.py)。

配置解析的底层实现

ConfigParser是 NG 系统的“配置真相来源”。它以正则逐行解析rtconfig.h:

  • #define NAME(无值)→ 布尔True;
  • #define NAME 1→ 布尔True;#define NAME 0→ 整数0;
  • #define NAME 0x200→ 整数(int(value, 0)支持十六进制/八进制);
  • #define NAME "str"→ 字符串;
  • #undef NAME→ 删除对应选项。

每个选项被封装为带元数据的ConfigOption(含type、line_number),并提供as_bool()/as_int()/as_str()转换方法(见 tools/ng/config.py)。

依赖检查采用AND 语义:get_dependency(['A', 'B'])要求全部宏均被定义为真值(整数非 0、布尔为 True、字符串非空),并且结果会以排序后的逗号拼接串为 key 进行缓存——这正是 README“性能优化”中“配置缓存”的落地实现(见 tools/ng/config.py)。

ConfigManager.validate()还内置了两条合法性校验:RT_NAME_MAX不得小于 4,RT_THREAD_PRIORITY_MAX必须为 8、32 或 256 之一(见 tools/ng/config.py),可作为 CI 环节的配置自检钩子。

使用方法:三步完成最小化集成

1. 最小化集成(推荐)

在 tools/building.py 中只需添加少量代码即可启用 NG 系统。README 给出的骨架如下:

# 在building.py的开头添加 try: from ng.adapter import ( init_build_context, inject_environment_methods, load_rtconfig as ng_load_rtconfig ) USE_NG = True except ImportError: USE_NG = False # 在PrepareBuilding函数中添加 def PrepareBuilding(env, root_directory, has_libcpu=False, remove_components=[]): # ... 原有代码 ... # 集成新系统 if USE_NG: context = init_build_context(root_directory) inject_environment_methods(env) ng_load_rtconfig('rtconfig.h') # ... 继续原有代码 ...

仓库中提供了两个可直接对照的集成范本:

  • tools/ng/building_ng.py 展示了“包装式”集成:先from building import *导入旧函数,再以_original_PrepareBuilding、_original_DefineGroup、_original_GetDepend、_original_DoBuilding保存原实现,随后覆盖为新版本——新版本优先走 NG 路径,异常时回退旧实现。DoBuilding中GetOption('target')触发新生成器,失败则回退调用旧GenTargetProject。
  • tools/ng/integration_example.py 则按行号标注了在building.py中需要插入/替换的 6 处变更点(导入、PrepareBuilding初始化、rtconfig.h解析后加载、DefineGroup、GetDepend、MergeGroup增强),合计约 35 行代码,并在末尾给出 SConscript 中“双保险”写法示例。

接入时的关键注意点:init_build_context应当在PrepareBuilding早期调用(它内部会os.path.abspath规范化根目录);inject_environment_methods需要在 SConsEnvironment就绪后调用,它会同时把 env 写入BuildContext;load_rtconfig('rtconfig.h')则应在 BSP 的配置文件解析完成之后调用。

2. 使用新的环境方法

集成后,SConsEnvironment对象自动获得新方法,SConscript 的写法变为:

# 在SConscript中使用新方法 Import('env') # 使用环境方法(推荐) src = env.GlobFiles('*.c') group = env.DefineGroup('MyComponent', src, depend=['RT_USING_XXX']) # 也可以使用传统方式(保持兼容) from building import * group = DefineGroup('MyComponent', src, depend=['RT_USING_XXX'])

对照 tools/ng/environment.py,RTEnv.inject_methods实际注入了 10 个方法:DefineGroup、GetDepend、SrcRemove、GetCurrentDir、BuildPackage、GlobFiles、GetBuildOptions、GetContext、GetRTTRoot、GetBSPRoot。其中GlobFiles是对 SConsGlob(pattern, strings=True)的增强封装,返回排序后的字符串列表,出错时仅告警并返回空列表,避免构建脚本因单个目录扫描失败而崩溃。

3. 新的项目生成器

NG 系统提供了改进的项目生成器,命令行用法与旧系统一致:

# 生成VS Code项目 scons --target=vscode # 生成CMake项目 scons --target=cmake

底层由 tools/ng/generator.py 的GeneratorRegistry分发(默认注册vscode、其别名vsc以及cmake),adapter.GenerateProject会从注册表收集project_info,构造GeneratorConfig后调用对应生成器(见 tools/ng/adapter.py)。

  • VscodeGenerator会生成.vscode下的四份配置:c_cpp_properties.json(自动从工具链探测compilerPath、把全部头文件路径写入includePath、把宏定义转为-D风格列表)、tasks.json(build/clean/rebuild三个 SCons 任务)、launch.json(基于 OpenOCD 的 Cortex Debug 配置)与settings.json(文件关联设置)。
  • CMakeGenerator会生成CMakeLists.txt,其中按project_info展开工具链(GCC 前缀)、include_directories、add_definitions、源文件列表与链接库。

与旧系统 tools/building.py 的GenTargetProject(仅支持 mdk/iar/vs/vsc 等模板拷贝)相比,NG 生成器完全由数据驱动、可编程、可单元测试。

API 参考:注入到 SCons Environment 的方法

所有方法都被注入到 SConsEnvironment对象中,以下是完整 API 说明(参数与返回均与 README 保持一致,并补充源码层面的行为细节)。

env.DefineGroup(name, src, depend, **kwargs)

定义一个组件组,返回构建对象列表。

参数:

  • name:组名称;
  • src:源文件列表(字符串或列表,内部会自动把单个字符串包装为列表);
  • depend:依赖条件(字符串或列表,不满足时直接返回空列表,对应文件不会参与构建);
  • **kwargs额外参数:
    • CPPPATH:头文件路径(作用于全局,会被AppendUnique到环境);
    • CPPDEFINES:宏定义;
    • CFLAGS/CXXFLAGS:编译选项;
    • LOCAL_CFLAGS/LOCAL_CPPPATH/LOCAL_CPPDEFINES/LOCAL_CXXFLAGS:仅对当前组有效的选项(会触发env.Clone(),不影响其他组);
    • LIBS/LIBPATH:库配置。

返回:构建对象列表。

示例:

src = ['driver.c', 'hal.c'] group = env.DefineGroup('Driver', src, depend=['RT_USING_DEVICE'], CPPPATH=[env.GetCurrentDir()], LOCAL_CFLAGS='-O3' )

从 tools/ng/environment.py 可以看到,DefineGroup先把 kwargs 映射到ProjectGroup数据类的各个字段,随后调用group.build(env)生成对象并注册到BuildContext。ProjectGroup.build的实现(见 tools/ng/project.py)会先检查_has_local_options(),有本地选项则env.Clone()并AppendUnique应用,再应用全局选项,最后对每个源文件执行build_env.Object(src)。

env.GetDepend(depend)

检查依赖是否满足,返回True/False。参数depend为依赖名称或列表。无BuildContext时回退为检查 env 中的变量(字符串按单个、列表按全真处理),有上下文时委托给ConfigManager.get_dependency:

if env.GetDepend('RT_USING_SERIAL'): src += ['serial.c'] if env.GetDepend(['RT_USING_SERIAL', 'RT_SERIAL_USING_DMA']): src += ['serial_dma.c']

env.SrcRemove(src, remove)

从源文件列表中移除文件(就地修改,不返回新列表)。remove支持精确匹配与fnmatch通配符匹配两种模式(见 tools/ng/environment.py):

src = env.GlobFiles('*.c') env.SrcRemove(src, ['test.c', 'debug.c']) # 也支持通配符:env.SrcRemove(src, ['*_test.c'])

env.BuildPackage(package_path)

从package.json构建软件包,返回构建对象列表。package_path缺省时在当前目录查找;传入目录则取其下package.json。其实现通过临时os.chdir复用旧版 tools/package.py 的BuildPackage函数,并在finally中恢复工作目录,保证后续 SConscript 的路径计算不受影响(见 tools/ng/environment.py)。

objs = env.BuildPackage('package.json')

env.GetContext()

获取当前构建上下文,返回BuildContext实例或None。未初始化上下文时返回None,因此使用时建议先判空:

context = env.GetContext() if context: context.logger.info("Building component...")

高级特性:工具链、生成器与构建钩子

1. 自定义工具链

创建自定义工具链只需继承抽象基类Toolchain,实现get_name/detect/configure_environment/get_compile_flags四个抽象方法:

from ng.toolchain import Toolchain class MyToolchain(Toolchain): def get_name(self): return "mycc" def detect(self): # 检测工具链 return shutil.which("mycc") is not None def configure_environment(self, env): env['CC'] = 'mycc' env['CFLAGS'] = '-O2 -Wall' # 注册工具链 context = env.GetContext() context.toolchain_manager.register_toolchain('mycc', MyToolchain())

内置三种实现可作为参考模板(见 tools/ng/toolchain.py):

  • GccToolchain:支持arm-none-eabi-、riscv32/riscv64-unknown-elf-前缀,通过shutil.which(prefix + 'gcc')探测并执行gcc --version获取版本,配置CC/CXX/AS/AR/LINK/SIZE/OBJDUMP/OBJCPY全套变量;get_compile_flags内置 cortex-m0/m0+/m3/m4/m7/m23/m33/a7/a9 的 CPU 标志映射,并追加-mfpu、-mfloat-abi、-ffunction-sections、-fdata-sections与-Wl,--gc-sections。
  • ArmccToolchain(Keil):探测armcc并回退检查C:\Keil_v5\ARM\ARMCC\bin等常见安装路径,配置LIBPREFIX/LIBSUFFIX为.lib,--cpu采用Cortex-Mx风格,追加--c99 --gnu标志。
  • IarToolchain:探测iccarm,版本识别标注为8.x,库后缀为.a,追加-e --dlib_config DLib_Config_Normal.h。

ToolchainManager在初始化时自动尝试探测上述工具链并注册(注册名形如gcc-arm-none-eabi-、armcc、iar),select_toolchain(name)支持按gcc/armcc/keil/iar懒创建并再次探测。

2. 自定义项目生成器

创建自定义生成器继承ProjectGenerator,实现get_name/generate/clean,通过注册表注册为类(注意注册的是类而非实例,由create_generator实例化):

from ng.generator import ProjectGenerator class MyGenerator(ProjectGenerator): def get_name(self): return "myide" def generate(self, context, project_info): # 生成项目文件 self._ensure_output_dir() # ... 生成逻辑 ... return True # 注册生成器 context.generator_registry.register('myide', MyGenerator)

基类还提供两个便利方法:_ensure_output_dir()(创建输出目录)与_copy_template(template_name, output_name)(从仓库 tools/targets 拷贝模板文件),其中模板目录由os.path.dirname(__file__) + '/../targets'解析(见 tools/ng/generator.py)。

3. 构建钩子

通过BuildContext可以在构建流程中挂接日志、读取配置、汇总项目信息:

context = env.GetContext() # 添加日志 context.logger.info("Starting build...") # 访问配置 if context.config_manager.get_option('RT_THREAD_PRIORITY_MAX'): print("Max priority:", context.config_manager.get_value('RT_THREAD_PRIORITY_MAX')) # 获取项目信息 info = context.project_registry.get_project_info() print(f"Total sources: {len(info['all_sources'])}")

get_project_info()返回结构包含groups(各组明细)、all_sources、all_includes(去重排序)、all_defines(合并后的宏字典)、all_libs、all_lib_paths(见 tools/ng/project.py),是编写自定义分析脚本与 IDE 元数据导出的理想入口。

迁移指南与最佳实践

从旧版本迁移

  1. 无需修改:现有的 SConscript 文件无需任何修改即可工作——building.py中旧版DefineGroup/GetDepend的增强版本会自动把调用转发到env.DefineGroup/env.GetDepend;
  2. 可选升级:可以逐步将DefineGroup调用改为env.DefineGroup,新写法可获得依赖缓存、本地选项隔离等能力;
  3. 新功能:可以开始使用新特性如env.BuildPackage、env.GetContext。

由于 NG 是 opt-in 设计(ImportError时USE_NG=False),即使ng包缺失,既有构建流程也能原样运行,这为团队提供了“按 BSP 逐步灰度”的平滑迁移路径。

最佳实践

  1. 使用环境方法:优先使用env.DefineGroup而不是全局函数;
  2. 类型提示:在 Python 3.5+ 中使用类型提示(模块内部大量使用typing.List/Dict/Optional与dataclass,tools/ng/init.py 版本号为1.0.0);
  3. 错误处理:使用context.logger记录错误和警告(logger.debug/info/warning/error齐备);
  4. 路径处理:使用PathService处理跨平台路径——它统一了相对/绝对路径换算、Windows 分隔符转正斜杠、公共前缀计算,避免在 Windows/Linux 双平台维护两套路径逻辑(见 tools/ng/utils.py)。

性能优化

NG 系统的性能优化体现在三个层面:

  1. 配置缓存:依赖检查结果以排序后的依赖名为 key 缓存于ConfigManager.cache,load_from_file时清空缓存,同一依赖组合在一次构建中只计算一次(见 tools/ng/config.py);
  2. 延迟加载:工具链和生成器按需加载——ToolchainManager构造时仅做探测注册,实际选用发生在select_toolchain;生成器由create_generator在GenerateProject时才实例化;
  3. 并行支持:项目生成可以并行执行——各ProjectGenerator.generate之间通过project_info数据解耦,天然适合多进程/多线程调度。

测试、路线图与许可

README 中给出的测试运行方式为:

cd tools/ng python -m pytest tests/

需要说明的是,截至当前仓库状态,tools/ng 目录下尚未包含tests/测试套件,README 路线图也明确把“完整的测试覆盖”“性能基准测试”列为待办项。因此该命令的执行前提是测试目录已就绪;在此之前,可通过python -c "import ng; print(ng.__version__)"在tools/ng上级目录验证包可导入性。

README 规划的路线图还包括:插件系统、更多项目生成器(Eclipse、Qt Creator 等)、构建缓存系统、分布式构建支持。NG 系统遵循 RT-Thread 的 Apache License 2.0 许可证。

总结

RT-Thread NG 构建系统以BuildContext为中心的面向对象模型、RTEnv的环境方法注入、ConfigManager的声明式依赖解析,以及Toolchain/ProjectGenerator的可插拔抽象,构成了一个既稳(向后兼容)又活(易扩展)的下一代构建底座。对普通组件开发者而言,最直接的价值是env.DefineGroup带来的本地选项隔离与依赖缓存;对平台移植与工具链维护者而言,新增一套工具链或 IDE 生成器只需实现一个抽象类并注册;对构建系统研究者而言,tools/ng 的模块拆分与 tools/ng/building_ng.py、tools/ng/integration_example.py 的渐进式改造范式,本身就是一份优秀的工程样板。后续随着测试套件、插件体系与分布式构建的落地,NG 有望成为 RT-Thread 构建链路的统一内核。

  • 操作系统
  • 嵌入式
  • 物联网
  • 嵌入式OS
  • RTOS

【免费下载链接】rt-thread

RT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/

项目地址:https://gitcode.com/gh_mirrors/rt/rt-thread
点击查看免费下载

相关推荐

上一篇:Wav2Vec2-Base-960h多语言支持:扩展非英语语音识别的可能性
下一篇:如何快速部署Qwen2.5-1.5B:5分钟完成本地模型安装与测试

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询