diff options
| author | bernard <[email protected]> | 2025-08-01 21:33:50 +0800 |
|---|---|---|
| committer | R b b666 <[email protected]> | 2025-08-07 09:32:45 +0800 |
| commit | a65efe648c7750bbd970c7f8d72fb164cde13835 (patch) | |
| tree | 83b22b91c5faafd5ffd52cbe5d84bae55d900e55 /tools/docs | |
| parent | 5347500f3306a283d9c4c2be5b9454bf31dd514f (diff) | |
[Tools] Add documents for tools script; Add NG for tools
Diffstat (limited to 'tools/docs')
| -rw-r--r-- | tools/docs/README.md | 162 | ||||
| -rw-r--r-- | tools/docs/SConscript编写指南.md | 948 | ||||
| -rw-r--r-- | tools/docs/构建系统使用指南.md | 643 | ||||
| -rw-r--r-- | tools/docs/构建系统技术原理.md | 912 |
4 files changed, 2665 insertions, 0 deletions
diff --git a/tools/docs/README.md b/tools/docs/README.md new file mode 100644 index 0000000000..295f6a4c03 --- /dev/null +++ b/tools/docs/README.md @@ -0,0 +1,162 @@ +# RT-Thread 构建系统文档 + +欢迎使用RT-Thread构建系统文档。本文档集详细介绍了RT-Thread基于SCons的构建系统的使用方法和技术原理。 + +## 文档目录 + +### 📚 用户指南 + +1. **[构建系统使用指南](构建系统使用指南.md)** + - 快速开始 + - 命令行选项详解 + - 工具链配置 + - 项目生成 + - 软件包管理 + - 高级功能 + - 常见问题解答 + +2. **[SConscript编写指南](SConscript编写指南.md)** + - 基础语法 + - 常用模式 + - 高级技巧 + - 最佳实践 + - 示例集合 + +### 🔧 技术文档 + +3. **[构建系统技术原理](构建系统技术原理.md)** + - 系统架构设计 + - 核心模块分析 + - 构建流程详解 + - 依赖管理机制 + - 工具链适配层 + - 项目生成器架构 + - 扩展机制 + +## 快速导航 + +### 常用命令 + +```bash +# 基础编译 +scons # 默认编译 +scons -j8 # 8线程并行编译 +scons -c # 清理编译产物 + +# 配置管理 +menuconfig # 图形化配置 +scons --pyconfig # Python脚本配置 + +# 项目生成 +scons --target=mdk5 # 生成Keil MDK5项目 +scons --target=iar # 生成IAR项目 +scons --target=vsc # 生成VS Code项目 +scons --target=cmake # 生成CMake项目 + +# 软件包管理 +pkgs --update # 更新软件包 +pkgs --list # 列出已安装包 +``` + +### 核心概念 + +- **SConstruct**: BSP根目录的主构建脚本 +- **SConscript**: 各个组件/目录的构建脚本 +- **rtconfig.py**: 工具链和平台配置 +- **rtconfig.h**: RT-Thread功能配置 +- **DefineGroup**: 定义组件的核心函数 +- **GetDepend**: 检查依赖的核心函数 + +## 构建系统架构图 + +``` +┌─────────────────────────────────────┐ +│ 用户命令 (scons) │ +└──────────────┬──────────────────────┘ + │ +┌──────────────▼──────────────────────┐ +│ SConstruct (主脚本) │ +│ ┌─────────────────────┐ │ +│ │ PrepareBuilding() │ │ +│ │ 环境初始化 │ │ +│ └──────────┬──────────┘ │ +└────────────────┼───────────────────┘ + │ +┌────────────────▼────────────────────┐ +│ building.py │ +│ ┌──────────┬──────────┐ │ +│ │ 组件收集 │ 依赖处理 │ │ +│ └──────────┴──────────┘ │ +└─────────────────────────────────────┘ + │ + ┌────────┴────────┐ + │ │ +┌───────▼──────┐ ┌────────▼────────┐ +│ SConscript │ │ rtconfig.h │ +│ 组件脚本 │ │ 功能配置 │ +└──────────────┘ └─────────────────┘ +``` + +## 主要特性 + +✅ **多工具链支持** +- GCC (ARM/RISC-V/x86) +- Keil MDK (ARMCC/ARMClang) +- IAR +- Visual Studio + +✅ **灵活的配置系统** +- Kconfig图形配置 +- 条件编译支持 +- 本地编译选项 + +✅ **丰富的项目生成器** +- IDE项目文件生成 +- CMake支持 +- Makefile生成 +- VS Code配置 + +✅ **模块化设计** +- 组件独立构建 +- 清晰的依赖管理 +- 可扩展架构 + +## 开发工作流 + +```mermaid +graph LR + A[配置系统] --> B[编写代码] + B --> C[构建项目] + C --> D[调试运行] + D --> E{是否完成?} + E -->|否| B + E -->|是| F[发布] + + A1[menuconfig] -.-> A + C1[scons] -.-> C + C2[IDE项目] -.-> C +``` + +## 相关链接 + +- [RT-Thread官网](https://www.rt-thread.org) +- [RT-Thread GitHub](https://github.com/RT-Thread/rt-thread) +- [SCons官方文档](https://scons.org/documentation.html) + +## 贡献指南 + +如果您发现文档中的错误或有改进建议,欢迎: + +1. 在GitHub上提交Issue +2. 提交Pull Request +3. 在RT-Thread社区论坛反馈 + +## 版本信息 + +- 文档版本:1.0.0 +- 更新日期:2024-01 +- 适用版本:RT-Thread 4.1.0+ + +--- + +**注意**:本文档基于RT-Thread最新版本编写,部分功能可能需要特定版本支持。使用前请确认您的RT-Thread版本。
\ No newline at end of file diff --git a/tools/docs/SConscript编写指南.md b/tools/docs/SConscript编写指南.md new file mode 100644 index 0000000000..f7d7490b4b --- /dev/null +++ b/tools/docs/SConscript编写指南.md @@ -0,0 +1,948 @@ +# RT-Thread SConscript 编写指南 + +## 目录 + +1. [概述](#概述) +2. [基础语法](#基础语法) +3. [常用模式](#常用模式) +4. [高级技巧](#高级技巧) +5. [最佳实践](#最佳实践) +6. [示例集合](#示例集合) +7. [常见问题](#常见问题) + +## 概述 + +SConscript是RT-Thread构建系统中的模块构建脚本,每个组件或目录都可以有自己的SConscript文件。本指南将详细介绍如何编写高质量的SConscript文件。 + +### SConscript在构建系统中的位置 + +``` +项目根目录/ +├── SConstruct # 主构建脚本 +├── applications/ +│ └── SConscript # 应用层构建脚本 +├── drivers/ +│ └── SConscript # 驱动层构建脚本 +└── components/ + ├── SConscript # 组件主脚本 + └── finsh/ + └── SConscript # 子组件脚本 +``` + +## 基础语法 + +### 1. 基本结构 + +```python +# 导入构建模块 +from building import * + +# 获取当前目录 +cwd = GetCurrentDir() + +# 定义源文件 +src = ['main.c', 'app.c'] + +# 定义头文件路径 +CPPPATH = [cwd] + +# 定义组 +group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH) + +# 返回组对象 +Return('group') +``` + +### 2. 导入building模块 + +```python +from building import * +``` + +这行代码导入了RT-Thread构建系统的所有函数,包括: +- `GetCurrentDir()` - 获取当前目录 +- `DefineGroup()` - 定义组件组 +- `GetDepend()` - 检查依赖 +- `Glob()` - 文件通配符匹配 +- `SrcRemove()` - 移除源文件 +- `DoBuilding()` - 执行构建 + +### 3. 源文件定义 + +#### 手动指定文件列表 +```python +src = ['file1.c', 'file2.c', 'file3.c'] +``` + +#### 使用通配符 +```python +src = Glob('*.c') # 当前目录所有.c文件 +src = Glob('src/*.c') # src子目录所有.c文件 +src = Glob('**/*.c') # 递归所有子目录的.c文件 +``` + +#### 混合使用 +```python +src = ['main.c'] + Glob('drivers/*.c') +``` + +### 4. 条件编译 + +#### 基于宏定义 +```python +src = ['common.c'] + +if GetDepend('RT_USING_SERIAL'): + src += ['serial.c'] + +if GetDepend('RT_USING_I2C'): + src += ['i2c.c'] +``` + +#### 基于平台 +```python +import rtconfig + +if rtconfig.PLATFORM == 'gcc': + src += ['gcc_specific.c'] +elif rtconfig.PLATFORM == 'armcc': + src += ['keil_specific.c'] +``` + +### 5. 移除文件 + +```python +src = Glob('*.c') +SrcRemove(src, ['test.c', 'debug.c']) # 移除不需要的文件 +``` + +## 常用模式 + +### 1. 简单组件模式 + +最基础的SConscript模式: + +```python +from building import * + +src = Glob('*.c') +CPPPATH = [GetCurrentDir()] + +group = DefineGroup('MyComponent', src, depend = ['RT_USING_MYCOMPONENT'], + CPPPATH = CPPPATH) + +Return('group') +``` + +### 2. 条件编译模式 + +根据配置选项包含不同的源文件: + +```python +from building import * + +src = ['core.c'] +CPPPATH = [GetCurrentDir()] + +# 功能模块条件编译 +if GetDepend('MYCOMPONENT_USING_FEATURE_A'): + src += ['feature_a.c'] + +if GetDepend('MYCOMPONENT_USING_FEATURE_B'): + src += ['feature_b.c'] + +# 平台相关代码 +if rtconfig.PLATFORM == 'gcc': + src += ['port_gcc.c'] +elif rtconfig.PLATFORM == 'armcc': + src += ['port_keil.c'] +elif rtconfig.PLATFORM == 'iccarm': + src += ['port_iar.c'] + +group = DefineGroup('MyComponent', src, depend = ['RT_USING_MYCOMPONENT'], + CPPPATH = CPPPATH) + +Return('group') +``` + +### 3. 多目录组织模式 + +处理复杂的目录结构: + +```python +from building import * + +cwd = GetCurrentDir() + +# 源文件来自多个目录 +src = Glob('src/*.c') +src += Glob('port/*.c') +src += Glob('hal/*.c') + +# 多个头文件路径 +CPPPATH = [ + cwd, + cwd + '/include', + cwd + '/internal', + cwd + '/port' +] + +# 根据配置添加特定目录 +if GetDepend('RT_USING_LWIP'): + src += Glob('lwip/*.c') + CPPPATH += [cwd + '/lwip'] + +group = DefineGroup('Network', src, depend = ['RT_USING_NETWORK'], + CPPPATH = CPPPATH) + +Return('group') +``` + +### 4. 递归子目录模式 + +自动处理所有子目录的SConscript: + +```python +import os +from building import * + +objs = [] +cwd = GetCurrentDir() + +# 获取所有子目录 +list = os.listdir(cwd) + +# 需要跳过的目录 +skip_dirs = ['test', 'doc', 'examples', '.git'] + +for d in list: + path = os.path.join(cwd, d) + # 检查是否是目录且包含SConscript + if os.path.isdir(path) and d not in skip_dirs: + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) + +Return('objs') +``` + +### 5. 库文件链接模式 + +链接预编译的库文件: + +```python +from building import * +import os + +cwd = GetCurrentDir() + +# 只包含必要的接口文件 +src = ['lib_interface.c'] + +CPPPATH = [cwd + '/include'] + +# 库文件配置 +LIBS = [] +LIBPATH = [] + +# 根据架构选择库文件 +import rtconfig +if rtconfig.ARCH == 'arm': + if rtconfig.CPU == 'cortex-m4': + LIBS += ['mylib_cm4'] + LIBPATH += [cwd + '/lib/cortex-m4'] + elif rtconfig.CPU == 'cortex-m3': + LIBS += ['mylib_cm3'] + LIBPATH += [cwd + '/lib/cortex-m3'] + +group = DefineGroup('MyLib', src, depend = ['RT_USING_MYLIB'], + CPPPATH = CPPPATH, LIBS = LIBS, LIBPATH = LIBPATH) + +Return('group') +``` + +## 高级技巧 + +### 1. 本地编译选项 + +为特定模块设置独立的编译选项: + +```python +from building import * + +src = Glob('*.c') +CPPPATH = [GetCurrentDir()] + +# 全局编译选项(影响依赖此组件的其他组件) +CPPDEFINES = ['GLOBAL_DEFINE'] + +# 本地编译选项(仅影响当前组件) +LOCAL_CFLAGS = '' +LOCAL_CPPDEFINES = ['LOCAL_DEFINE'] +LOCAL_CPPPATH = ['./private'] + +# 根据编译器设置优化选项 +import rtconfig +if rtconfig.PLATFORM == 'gcc': + LOCAL_CFLAGS += ' -O3 -funroll-loops' +elif rtconfig.PLATFORM == 'armcc': + LOCAL_CFLAGS += ' -O3 --loop_optimization_level=2' + +group = DefineGroup('HighPerf', src, depend = ['RT_USING_HIGHPERF'], + CPPPATH = CPPPATH, + CPPDEFINES = CPPDEFINES, + LOCAL_CFLAGS = LOCAL_CFLAGS, + LOCAL_CPPDEFINES = LOCAL_CPPDEFINES, + LOCAL_CPPPATH = LOCAL_CPPPATH +) + +Return('group') +``` + +### 2. 动态源文件生成 + +在构建时生成源文件: + +```python +from building import * +import time + +def generate_version_file(): + """生成版本信息文件""" + version_c = ''' +/* Auto-generated file, do not edit! */ +#include "version.h" + +const char *build_time = "%s"; +const char *version = "%s"; +''' % (time.strftime('%Y-%m-%d %H:%M:%S'), '1.0.0') + + with open('version_gen.c', 'w') as f: + f.write(version_c) + +# 生成文件 +generate_version_file() + +src = ['main.c', 'version_gen.c'] +CPPPATH = [GetCurrentDir()] + +group = DefineGroup('App', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') +``` + +### 3. 复杂依赖处理 + +处理复杂的依赖关系: + +```python +from building import * + +src = [] +CPPPATH = [GetCurrentDir()] +CPPDEFINES = [] + +# 基础功能 +if GetDepend('RT_USING_DEVICE'): + src += ['device_core.c'] + + # 串口驱动(依赖设备框架) + if GetDepend('RT_USING_SERIAL'): + src += ['serial.c'] + + # 串口DMA(依赖串口驱动) + if GetDepend('RT_SERIAL_USING_DMA'): + src += ['serial_dma.c'] + CPPDEFINES += ['SERIAL_USING_DMA'] + + # SPI驱动(依赖设备框架) + if GetDepend('RT_USING_SPI'): + src += ['spi.c'] + + # SPI DMA(依赖SPI驱动) + if GetDepend('RT_SPI_USING_DMA'): + src += ['spi_dma.c'] + +# 错误检查 +if GetDepend('RT_SERIAL_USING_DMA') and not GetDepend('RT_USING_SERIAL'): + print('Error: RT_SERIAL_USING_DMA requires RT_USING_SERIAL!') + exit(1) + +group = DefineGroup('Drivers', src, depend = ['RT_USING_DEVICE'], + CPPPATH = CPPPATH, CPPDEFINES = CPPDEFINES) + +Return('group') +``` + +### 4. 平台特定实现 + +根据不同平台包含不同实现: + +```python +from building import * +import rtconfig + +cwd = GetCurrentDir() +src = ['common.c'] +CPPPATH = [cwd, cwd + '/include'] + +# 架构相关目录映射 +arch_map = { + 'arm': { + 'cortex-m3': 'arm/cortex-m3', + 'cortex-m4': 'arm/cortex-m4', + 'cortex-m7': 'arm/cortex-m7', + 'cortex-a': 'arm/cortex-a' + }, + 'risc-v': { + 'rv32': 'riscv/rv32', + 'rv64': 'riscv/rv64' + }, + 'x86': { + 'i386': 'x86/i386', + 'x86_64': 'x86/x86_64' + } +} + +# 根据架构和CPU选择实现 +if hasattr(rtconfig, 'ARCH') and hasattr(rtconfig, 'CPU'): + arch = rtconfig.ARCH + cpu = rtconfig.CPU + + if arch in arch_map and cpu in arch_map[arch]: + port_dir = arch_map[arch][cpu] + port_src = Glob(port_dir + '/*.c') + port_src += Glob(port_dir + '/*.S') + src += port_src + CPPPATH += [cwd + '/' + port_dir] + else: + print('Warning: No port for %s - %s' % (arch, cpu)) + +group = DefineGroup('MyDriver', src, depend = ['RT_USING_MYDRIVER'], + CPPPATH = CPPPATH) + +Return('group') +``` + +### 5. 第三方库集成 + +集成第三方库的模板: + +```python +from building import * +import os + +cwd = GetCurrentDir() + +# 第三方库路径 +lib_path = cwd + '/3rdparty/libfoo' + +# 检查库是否存在 +if not os.path.exists(lib_path): + print('Error: libfoo not found at', lib_path) + print('Please run: git submodule update --init') + Return('group') + +# 库源文件 +src = Glob(lib_path + '/src/*.c') + +# 移除不需要的文件 +SrcRemove(src, [ + lib_path + '/src/test.c', + lib_path + '/src/example.c' +]) + +# 头文件路径 +CPPPATH = [ + lib_path + '/include', + cwd + '/port' # 移植层头文件 +] + +# 添加移植层 +src += Glob('port/*.c') + +# 配置宏定义 +CPPDEFINES = ['LIBFOO_RTOS_RTTHREAD'] + +# 根据配置启用功能 +if GetDepend('LIBFOO_ENABLE_FLOAT'): + CPPDEFINES += ['LIBFOO_USE_FLOAT'] + +if GetDepend('LIBFOO_ENABLE_STDIO'): + CPPDEFINES += ['LIBFOO_USE_STDIO'] + +group = DefineGroup('libfoo', src, depend = ['RT_USING_LIBFOO'], + CPPPATH = CPPPATH, CPPDEFINES = CPPDEFINES) + +Return('group') +``` + +### 6. 条件导出符号 + +根据配置导出不同的API: + +```python +from building import * + +src = ['core.c'] +CPPPATH = [GetCurrentDir()] + +# API版本控制 +if GetDepend('RT_USING_MODULE_API_V2'): + src += ['api_v2.c'] + CPPDEFINES = ['MODULE_API_VERSION=2'] +else: + src += ['api_v1.c'] + CPPDEFINES = ['MODULE_API_VERSION=1'] + +# 根据配置级别导出不同功能 +if GetDepend('MODULE_EXPERT_MODE'): + src += ['expert_api.c'] + CPPDEFINES += ['EXPORT_EXPERT_API'] + +group = DefineGroup('Module', src, depend = ['RT_USING_MODULE'], + CPPPATH = CPPPATH, CPPDEFINES = CPPDEFINES) + +Return('group') +``` + +## 最佳实践 + +### 1. 文件组织原则 + +``` +component/ +├── SConscript # 主构建脚本 +├── Kconfig # 配置选项 +├── README.md # 组件说明 +├── include/ # 公开头文件 +│ └── component.h +├── src/ # 源文件 +│ ├── core.c +│ └── utils.c +├── port/ # 平台相关代码 +│ ├── cortex-m/ +│ └── risc-v/ +└── examples/ # 示例代码 + └── example.c +``` + +### 2. 依赖管理最佳实践 + +```python +from building import * + +# 1. 明确声明依赖 +REQUIRED_DEPS = ['RT_USING_DEVICE', 'RT_USING_HEAP'] + +# 2. 检查必要依赖 +for dep in REQUIRED_DEPS: + if not GetDepend(dep): + print('Error: %s requires %s' % ('MyComponent', dep)) + Return('group') + +# 3. 可选依赖 +src = ['core.c'] + +# 可选功能 +OPTIONAL_FEATURES = { + 'RT_MYCOMPONENT_USING_DMA': 'dma.c', + 'RT_MYCOMPONENT_USING_INTERRUPT': 'interrupt.c', + 'RT_MYCOMPONENT_USING_STATS': 'statistics.c' +} + +for macro, file in OPTIONAL_FEATURES.items(): + if GetDepend(macro): + src += [file] + +group = DefineGroup('MyComponent', src, depend = ['RT_USING_MYCOMPONENT']) +Return('group') +``` + +### 3. 错误处理 + +```python +from building import * +import os + +# 检查关键文件 +critical_files = ['config.h', 'version.h'] +for f in critical_files: + if not os.path.exists(f): + print('Error: Missing required file:', f) + # 返回空组,不中断整体构建 + group = DefineGroup('MyComponent', [], depend = ['']) + Return('group') + +# 检查工具链 +import rtconfig +supported_toolchains = ['gcc', 'armcc', 'iar'] +if rtconfig.PLATFORM not in supported_toolchains: + print('Warning: Toolchain %s not tested' % rtconfig.PLATFORM) + +# 正常构建流程 +src = Glob('*.c') +group = DefineGroup('MyComponent', src, depend = ['RT_USING_MYCOMPONENT']) +Return('group') +``` + +### 4. 性能优化 + +```python +from building import * +import os + +# 1. 缓存文件列表(避免重复扫描) +_file_cache = {} + +def cached_glob(pattern): + if pattern not in _file_cache: + _file_cache[pattern] = Glob(pattern) + return _file_cache[pattern] + +# 2. 延迟加载(仅在需要时扫描) +src = [] +if GetDepend('RT_USING_MYCOMPONENT'): + src = cached_glob('*.c') + + if GetDepend('RT_MYCOMPONENT_USING_EXTRA'): + src += cached_glob('extra/*.c') + +# 3. 避免深度递归(使用显式路径) +# 不好的做法 +# src = Glob('**/*.c') # 递归所有子目录 + +# 好的做法 +src = Glob('*.c') +src += Glob('core/*.c') +src += Glob('hal/*.c') + +group = DefineGroup('MyComponent', src, depend = ['RT_USING_MYCOMPONENT']) +Return('group') +``` + +### 5. 文档化 + +```python +""" +SConscript for MyComponent + +This component provides [功能描述] + +Configuration: + RT_USING_MYCOMPONENT - Enable this component + RT_MYCOMPONENT_USING_DMA - Enable DMA support + RT_MYCOMPONENT_BUFFER_SIZE - Buffer size (default: 256) + +Dependencies: + - RT_USING_DEVICE (required) + - RT_USING_DMA (optional, for DMA support) +""" + +from building import * + +# ... 构建脚本内容 ... +``` + +## 示例集合 + +### 示例1:设备驱动SConscript + +```python +from building import * + +cwd = GetCurrentDir() + +# 驱动源文件 +src = [] +CPPPATH = [cwd + '/../inc'] + +# I2C驱动 +if GetDepend('RT_USING_I2C'): + src += ['drv_i2c.c'] + +# SPI驱动 +if GetDepend('RT_USING_SPI'): + src += ['drv_spi.c'] + + # QSPI支持 + if GetDepend('RT_USING_QSPI'): + src += ['drv_qspi.c'] + +# USB驱动 +if GetDepend('RT_USING_USB'): + src += ['drv_usb.c'] + if GetDepend('RT_USING_USB_HOST'): + src += ['drv_usbh.c'] + if GetDepend('RT_USING_USB_DEVICE'): + src += ['drv_usbd.c'] + +# SDIO驱动 +if GetDepend('RT_USING_SDIO'): + src += ['drv_sdio.c'] + +group = DefineGroup('Drivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') +``` + +### 示例2:网络组件SConscript + +```python +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd] + +# 网络接口层 +if GetDepend('RT_USING_NETDEV'): + src += Glob('netdev/*.c') + CPPPATH += [cwd + '/netdev'] + +# SAL套接字抽象层 +if GetDepend('RT_USING_SAL'): + src += Glob('sal/src/*.c') + src += Glob('sal/socket/*.c') + CPPPATH += [cwd + '/sal/include'] + + # AT指令支持 + if GetDepend('SAL_USING_AT'): + src += Glob('sal/impl/af_inet_at.c') + + # LwIP支持 + if GetDepend('SAL_USING_LWIP'): + src += Glob('sal/impl/af_inet_lwip.c') + +# AT指令框架 +if GetDepend('RT_USING_AT'): + src += Glob('at/src/*.c') + CPPPATH += [cwd + '/at/include'] + + # AT Socket + if GetDepend('AT_USING_SOCKET'): + src += Glob('at/at_socket/*.c') + +group = DefineGroup('Network', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') +``` + +### 示例3:文件系统SConscript + +```python +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd + '/include'] + +# DFS框架 +if GetDepend('RT_USING_DFS'): + src += Glob('src/*.c') + + # ELM FatFS + if GetDepend('RT_USING_DFS_ELMFAT'): + src += Glob('filesystems/elmfat/*.c') + # FatFS版本选择 + if GetDepend('RT_DFS_ELM_USE_LFN'): + src += ['filesystems/elmfat/ffunicode.c'] + + # ROMFS + if GetDepend('RT_USING_DFS_ROMFS'): + src += ['filesystems/romfs/dfs_romfs.c'] + + # DevFS + if GetDepend('RT_USING_DFS_DEVFS'): + src += ['filesystems/devfs/devfs.c'] + + # NFS + if GetDepend('RT_USING_DFS_NFS'): + src += Glob('filesystems/nfs/*.c') + CPPPATH += [cwd + '/filesystems/nfs'] + +group = DefineGroup('Filesystem', src, depend = ['RT_USING_DFS'], + CPPPATH = CPPPATH) + +Return('group') +``` + +### 示例4:使用package.json的SConscript + +```python +from building import * +import os +import json + +cwd = GetCurrentDir() + +# 尝试使用package.json +package_file = os.path.join(cwd, 'package.json') +if os.path.exists(package_file): + # 使用自动构建 + objs = BuildPackage(package_file) +else: + # 手动构建 + src = Glob('src/*.c') + CPPPATH = [cwd + '/include'] + + # 读取配置 + config_file = os.path.join(cwd, 'config.json') + if os.path.exists(config_file): + with open(config_file, 'r') as f: + config = json.load(f) + + # 根据配置添加源文件 + for feature in config.get('features', []): + if GetDepend('RT_USING_' + feature.upper()): + src += Glob('src/%s/*.c' % feature) + + objs = DefineGroup('MyPackage', src, depend = ['RT_USING_MYPACKAGE'], + CPPPATH = CPPPATH) + +Return('objs') +``` + +## 常见问题 + +### Q1: 如何调试SConscript? + +```python +from building import * + +# 1. 打印调试信息 +print('Current directory:', GetCurrentDir()) +print('GetDepend RT_USING_XXX:', GetDepend('RT_USING_XXX')) + +# 2. 打印源文件列表 +src = Glob('*.c') +print('Source files:', src) + +# 3. 条件调试输出 +if GetOption('verbose'): + print('Detailed debug info...') + +# 4. 检查环境变量 +import os +print('RTT_ROOT:', os.getenv('RTT_ROOT')) +``` + +### Q2: 如何处理可选的依赖? + +```python +from building import * + +src = ['core.c'] + +# 可选依赖处理 +optional_deps = { + 'RT_USING_SERIAL': ['serial.c', 'serial_ops.c'], + 'RT_USING_CAN': ['can.c', 'can_ops.c'], + 'RT_USING_I2C': ['i2c.c', 'i2c_ops.c'] +} + +for dep, files in optional_deps.items(): + if GetDepend(dep): + src += files + +# 检查组合依赖 +if GetDepend('RT_USING_SERIAL') and GetDepend('RT_USING_DMA'): + src += ['serial_dma.c'] + +group = DefineGroup('Drivers', src, depend = ['RT_USING_DEVICE']) +Return('group') +``` + +### Q3: 如何支持多个工具链? + +```python +from building import * +import rtconfig + +src = ['common.c'] + +# 工具链特定文件 +toolchain_files = { + 'gcc': ['gcc_startup.S', 'gcc_specific.c'], + 'armcc': ['keil_startup.s', 'keil_specific.c'], + 'iccarm': ['iar_startup.s', 'iar_specific.c'] +} + +if rtconfig.PLATFORM in toolchain_files: + src += toolchain_files[rtconfig.PLATFORM] +else: + print('Warning: Unknown toolchain', rtconfig.PLATFORM) + +# 工具链特定编译选项 +LOCAL_CFLAGS = '' +if rtconfig.PLATFORM == 'gcc': + LOCAL_CFLAGS = '-Wno-unused-function' +elif rtconfig.PLATFORM == 'armcc': + LOCAL_CFLAGS = '--diag_suppress=177' + +group = DefineGroup('MyComponent', src, depend = [''], + LOCAL_CFLAGS = LOCAL_CFLAGS) +Return('group') +``` + +### Q4: 如何处理生成的代码? + +```python +from building import * +import subprocess + +def generate_code(): + """生成代码""" + # 运行代码生成器 + cmd = ['python', 'codegen.py', '-o', 'generated.c'] + subprocess.check_call(cmd) + +# 确保生成代码 +if GetDepend('RT_USING_CODEGEN'): + generate_code() + src = ['generated.c'] +else: + src = ['default.c'] + +group = DefineGroup('Generated', src, depend = ['']) +Return('group') +``` + +### Q5: 如何组织大型项目? + +```python +# 主SConscript +from building import * + +objs = [] + +# 子模块列表 +modules = [ + 'core', + 'drivers', + 'network', + 'filesystem', + 'gui' +] + +# 根据配置包含模块 +for module in modules: + # 检查模块是否启用 + if GetDepend('RT_USING_' + module.upper()): + # 构建子模块 + objs += SConscript(module + '/SConscript') + +Return('objs') +``` + +## 总结 + +编写高质量的SConscript需要: + +1. **清晰的结构**:合理组织源文件和目录 +2. **正确的依赖**:准确声明和检查依赖关系 +3. **平台兼容**:处理不同工具链和平台的差异 +4. **性能考虑**:避免不必要的文件扫描 +5. **错误处理**:优雅处理各种异常情况 +6. **文档完善**:添加必要的注释和说明 + +通过遵循本指南的建议和最佳实践,可以编写出易维护、可扩展的构建脚本,为RT-Thread项目的构建提供坚实的基础。
\ No newline at end of file diff --git a/tools/docs/构建系统使用指南.md b/tools/docs/构建系统使用指南.md new file mode 100644 index 0000000000..1e54f1d4d9 --- /dev/null +++ b/tools/docs/构建系统使用指南.md @@ -0,0 +1,643 @@ +# RT-Thread 构建系统使用指南 + +## 目录 + +1. [概述](#概述) +2. [快速开始](#快速开始) +3. [命令行选项详解](#命令行选项详解) +4. [工具链配置](#工具链配置) +5. [项目生成](#项目生成) +6. [软件包管理](#软件包管理) +7. [高级功能](#高级功能) +8. [常见问题](#常见问题) + +## 概述 + +RT-Thread使用基于SCons的构建系统,提供了统一的跨平台构建体验。构建系统支持: + +- 多种编译器和IDE(GCC、Keil、IAR、VS Code等) +- 模块化的组件管理 +- 灵活的配置系统 +- 自动化的依赖处理 +- 软件包管理功能 + +### 系统架构图 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 用户命令 │ +│ (scons, scons --target=xxx) │ +└─────────────────────┬───────────────────────────────────────┘ + │ +┌─────────────────────▼───────────────────────────────────────┐ +│ SConstruct │ +│ (BSP根目录构建脚本) │ +└─────────────────────┬───────────────────────────────────────┘ + │ +┌─────────────────────▼───────────────────────────────────────┐ +│ building.py │ +│ (核心构建引擎和函数库) │ +├─────────────────────┬───────────────────────────────────────┤ +│ PrepareBuilding() │ DefineGroup() │ DoBuilding() │ +│ 环境初始化 │ 组件定义 │ 执行构建 │ +└─────────────────────┴─────────┬─────────────────────────────┘ + │ + ┌───────────────────────┼───────────────────────┐ + │ │ │ +┌───────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐ +│ SConscript │ │ rtconfig.py │ │ rtconfig.h │ +│ (组件脚本) │ │ (工具链配置) │ │ (功能配置) │ +└────────────────┘ └─────────────────┘ └─────────────────┘ +``` + +## 快速开始 + +### 基本编译流程 + +1. **进入BSP目录** + ```bash + cd bsp/stm32/stm32f103-blue-pill + ``` + +2. **配置系统**(可选) + ```bash + menuconfig # 图形化配置 + ``` + +3. **编译项目** + ```bash + scons # 默认编译 + scons -j8 # 多线程编译 + ``` + +4. **生成IDE项目** + ```bash + scons --target=mdk5 # 生成Keil MDK5项目 + scons --target=iar # 生成IAR项目 + scons --target=vsc # 生成VS Code项目 + ``` + +### 清理和重建 + +```bash +scons -c # 清理编译产物 +scons -c --target=mdk5 # 清理MDK5项目文件 +scons --dist # 生成分发包 +``` + +## 命令行选项详解 + +### 基础编译选项 + +| 选项 | 说明 | 示例 | +|------|------|------| +| `-j N` | 多线程编译,N为线程数 | `scons -j8` | +| `-c` | 清理编译产物 | `scons -c` | +| `-s` | 静默模式,不显示命令 | `scons -s` | +| `--verbose` | 详细输出模式 | `scons --verbose` | + +### 项目生成选项 + +| 选项 | 说明 | 生成的文件 | +|------|------|------------| +| `--target=mdk4` | Keil MDK4项目 | project.uvproj | +| `--target=mdk5` | Keil MDK5项目 | project.uvprojx | +| `--target=iar` | IAR工作区 | project.eww | +| `--target=vs2012` | Visual Studio项目 | project.vcxproj | +| `--target=vsc` | VS Code配置 | .vscode/目录 | +| `--target=eclipse` | Eclipse CDT项目 | .project, .cproject | +| `--target=cmake` | CMake项目 | CMakeLists.txt | +| `--target=makefile` | 通用Makefile | Makefile | + +### 配置管理选项 + +| 选项 | 说明 | 使用场景 | +|------|------|----------| +| `--menuconfig` | 启动图形配置界面 | 修改功能配置 | +| `--pyconfig` | 通过Python脚本配置 | 自动化配置 | +| `--pyconfig-silent` | 静默Python配置 | CI/CD环境 | +| `--genconfig` | 从rtconfig.h生成.config | 配置迁移 | +| `--useconfig=xxx` | 使用指定配置文件 | 切换配置 | + +### 工具链选项 + +| 选项 | 说明 | 示例 | +|------|------|------| +| `--exec-path=PATH` | 指定工具链路径 | `--exec-path=/opt/gcc-arm/bin` | +| `--exec-prefix=PREFIX` | 指定工具链前缀 | `--exec-prefix=arm-none-eabi-` | +| `--strict` | 严格编译模式 | 开启-Werror | + +### 分发和调试选项 + +| 选项 | 说明 | 用途 | +|------|------|------| +| `--dist` | 生成分发包 | 项目发布 | +| `--dist-strip` | 生成精简分发包 | 最小化项目 | +| `--dist-ide` | 生成RT-Thread Studio项目 | Studio开发 | +| `--cscope` | 生成cscope数据库 | 代码导航 | +| `--clang-analyzer` | 运行Clang静态分析 | 代码质量检查 | + +## 工具链配置 + +### rtconfig.py配置文件 + +每个BSP都有一个`rtconfig.py`文件,定义了工具链配置: + +```python +import os + +# 工具链定义 +CROSS_TOOL = 'gcc' # 工具链类型: gcc/keil/iar +PLATFORM = 'armcc' # 平台标识 + +# 编译器路径 +if os.getenv('RTT_EXEC_PATH'): + EXEC_PATH = os.getenv('RTT_EXEC_PATH') +else: + EXEC_PATH = r'C:/Keil_v5/ARM/ARMCC/bin' + +# 编译器前缀(GCC工具链) +PREFIX = 'arm-none-eabi-' + +# 编译器定义 +CC = PREFIX + 'gcc' +CXX = PREFIX + 'g++' +AS = PREFIX + 'gcc' +AR = PREFIX + 'ar' +LINK = PREFIX + 'gcc' +SIZE = PREFIX + 'size' +OBJDUMP = PREFIX + 'objdump' +OBJCPY = PREFIX + 'objcopy' + +# 设备相关参数 +DEVICE = ' -mcpu=cortex-m3 -mthumb -ffunction-sections -fdata-sections' + +# 编译标志 +CFLAGS = DEVICE + ' -Dgcc' +AFLAGS = ' -c' + DEVICE + ' -x assembler-with-cpp -Wa,-mimplicit-it=thumb ' +LFLAGS = DEVICE + ' -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,Reset_Handler -T link.lds' + +# 路径定义 +CPATH = '' +LPATH = '' + +# 链接脚本 +LINK_SCRIPT = 'link.lds' + +# 后处理命令 +POST_ACTION = OBJCPY + ' -O binary $TARGET rtthread.bin\n' + SIZE + ' $TARGET \n' +``` + +### 支持的工具链 + +1. **GCC工具链** + ```python + CROSS_TOOL = 'gcc' + PREFIX = 'arm-none-eabi-' + ``` + +2. **Keil MDK** + ```python + CROSS_TOOL = 'keil' + PLATFORM = 'armcc' # ARM Compiler 5 + # 或 + PLATFORM = 'armclang' # ARM Compiler 6 + ``` + +3. **IAR** + ```python + CROSS_TOOL = 'iar' + PLATFORM = 'iccarm' + ``` + +4. **RISC-V GCC** + ```python + CROSS_TOOL = 'gcc' + PREFIX = 'riscv64-unknown-elf-' + ``` + +### 环境变量支持 + +构建系统支持通过环境变量覆盖配置: + +```bash +# 设置工具链路径 +export RTT_EXEC_PATH=/opt/gcc-arm-none-eabi-10-2020-q4-major/bin + +# 设置工具链前缀 +export RTT_CC_PREFIX=arm-none-eabi- + +# 设置工具链类型 +export RTT_CC=gcc +``` + +## 项目生成 + +### VS Code项目配置 + +使用`scons --target=vsc`生成VS Code项目,会创建以下配置文件: + +**.vscode/c_cpp_properties.json** - IntelliSense配置 +```json +{ + "configurations": [ + { + "name": "RT-Thread", + "includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/../../components/finsh", + "${workspaceFolder}/../../include" + ], + "defines": [ + "RT_USING_FINSH", + "RT_USING_SERIAL", + "__GNUC__" + ], + "compilerPath": "/opt/gcc-arm/bin/arm-none-eabi-gcc", + "cStandard": "c99", + "cppStandard": "c++11" + } + ] +} +``` + +**.vscode/tasks.json** - 构建任务配置 +```json +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "type": "shell", + "command": "scons", + "problemMatcher": "$gcc", + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} +``` + +### CMake项目生成 + +使用`scons --target=cmake`生成CMakeLists.txt: + +```cmake +cmake_minimum_required(VERSION 3.10) + +# 工具链设置 +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR cortex-m3) +set(CMAKE_C_COMPILER arm-none-eabi-gcc) +set(CMAKE_ASM_COMPILER arm-none-eabi-gcc) + +project(rtthread C ASM) + +# 编译选项 +add_compile_options( + -mcpu=cortex-m3 + -mthumb + -ffunction-sections + -fdata-sections + -Wall + -O0 + -g +) + +# 头文件路径 +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../../include +) + +# 源文件 +set(SOURCES + applications/main.c + ../../src/clock.c + ../../src/components.c +) + +# 生成可执行文件 +add_executable(${PROJECT_NAME}.elf ${SOURCES}) + +# 链接选项 +target_link_options(${PROJECT_NAME}.elf PRIVATE + -T${CMAKE_CURRENT_SOURCE_DIR}/link.lds + -Wl,-Map=${PROJECT_NAME}.map,--cref + -Wl,--gc-sections +) +``` + +## 软件包管理 + +### 使用package.json定义组件 + +RT-Thread支持使用`package.json`文件定义软件包: + +```json +{ + "name": "my-driver", + "version": "1.0.0", + "type": "rt-thread-component", + "license": "Apache-2.0", + "dependencies": { + "RT_USING_DEVICE": "latest" + }, + "sources": { + "common": { + "source_files": ["src/*.c"], + "header_files": ["inc/*.h"], + "header_path": ["inc"] + }, + "cortex-m": { + "condition": "defined(ARCH_ARM_CORTEX_M)", + "source_files": ["port/cortex-m/*.c"] + } + } +} +``` + +### 在SConscript中使用BuildPackage + +```python +from building import * +import os + +# 使用package.json构建 +objs = BuildPackage('package.json') + +# 或者手动指定包路径 +pkg_path = os.path.join(GetCurrentDir(), 'package.json') +objs = BuildPackage(pkg_path) + +Return('objs') +``` + +## 高级功能 + +### 1. 条件编译和依赖管理 + +**基于宏定义的条件编译** +```python +src = ['common.c'] + +if GetDepend('RT_USING_SERIAL'): + src += ['serial.c'] + +if GetDepend(['RT_USING_SPI', 'RT_USING_SFUD']): + src += ['spi_flash.c'] + +group = DefineGroup('Drivers', src, depend = ['RT_USING_DEVICE']) +``` + +**复杂依赖表达式** +```python +# 依赖可以是列表(AND关系) +depend = ['RT_USING_LWIP', 'RT_USING_NETDEV'] + +# 或者使用GetDepend进行复杂判断 +if GetDepend('RT_USING_LWIP') and not GetDepend('RT_USING_SAL'): + print('配置错误:LWIP需要SAL支持') +``` + +### 2. 本地编译选项 + +为特定模块设置独立的编译选项: + +```python +# 全局编译选项 +CPPPATH = [GetCurrentDir()] +CPPDEFINES = ['MODULE_VERSION=1'] + +# 本地编译选项(仅对当前group有效) +LOCAL_CFLAGS = '-O3 -funroll-loops' +LOCAL_CPPPATH = ['./private'] +LOCAL_CPPDEFINES = {'BUFFER_SIZE': 1024} + +group = DefineGroup('Module', src, depend = [''], + CPPPATH = CPPPATH, + CPPDEFINES = CPPDEFINES, + LOCAL_CFLAGS = LOCAL_CFLAGS, + LOCAL_CPPPATH = LOCAL_CPPPATH, + LOCAL_CPPDEFINES = LOCAL_CPPDEFINES +) +``` + +### 3. 递归构建子目录 + +自动扫描并构建子目录: + +```python +import os +from building import * + +objs = [] +cwd = GetCurrentDir() +dirs = os.listdir(cwd) + +# 黑名单目录 +skip_dirs = ['test', 'doc', 'example'] + +for d in dirs: + if d in skip_dirs: + continue + + path = os.path.join(cwd, d) + if os.path.isdir(path): + sconscript = os.path.join(path, 'SConscript') + if os.path.isfile(sconscript): + objs += SConscript(sconscript) + +Return('objs') +``` + +### 4. 自定义构建动作 + +添加构建前后的自定义动作: + +```python +from building import * + +def pre_build_action(target, source, env): + print('开始构建:', target[0]) + # 执行预处理操作 + +def post_build_action(target, source, env): + print('构建完成:', target[0]) + # 生成额外文件,如hex文件 + import subprocess + subprocess.call(['arm-none-eabi-objcopy', '-O', 'ihex', + str(target[0]), str(target[0]) + '.hex']) + +# 注册构建动作 +if GetOption('target') == None: + rtconfig.POST_ACTION = post_build_action +``` + +### 5. 分发包定制 + +创建自定义分发包: + +```python +# 在BSP的SConstruct中添加 +def dist_handle(BSP_ROOT, dist_dir): + import shutil + + # 复制必要文件 + src_files = ['applications', 'board', 'rtconfig.py', 'SConstruct'] + for src in src_files: + src_path = os.path.join(BSP_ROOT, src) + dst_path = os.path.join(dist_dir, src) + if os.path.isdir(src_path): + shutil.copytree(src_path, dst_path) + else: + shutil.copy2(src_path, dst_path) + + # 创建README + with open(os.path.join(dist_dir, 'README.md'), 'w') as f: + f.write('# RT-Thread BSP 分发包\n') + f.write('构建时间: ' + time.strftime('%Y-%m-%d %H:%M:%S\n')) + +# 注册分发处理函数 +AddOption('--dist-handle', + dest = 'dist-handle', + action = 'store_true', + default = False, + help = 'Enable dist handle') + +if GetOption('dist-handle'): + dist_handle(BSP_ROOT, dist_dir) +``` + +### 6. 代码分析集成 + +**Clang静态分析** +```bash +scons --clang-analyzer +``` + +**生成compile_commands.json** +```bash +scons --target=cmake # CMake项目会包含compile_commands.json +# 或使用 +scons --compile-commands +``` + +**生成Cscope数据库** +```bash +scons --cscope +``` + +## 常见问题 + +### Q1: 如何添加新的源文件? + +在相应目录的SConscript中添加: +```python +src = Glob('*.c') # 自动包含所有.c文件 +# 或 +src = ['file1.c', 'file2.c'] # 手动指定 +``` + +### Q2: 如何排除特定文件? + +```python +src = Glob('*.c') +SrcRemove(src, ['test.c', 'debug.c']) +``` + +### Q3: 如何处理不同配置下的源文件? + +```python +src = ['common.c'] + +if rtconfig.PLATFORM == 'gcc': + src += ['gcc_specific.c'] +elif rtconfig.PLATFORM == 'armcc': + src += ['keil_specific.c'] +``` + +### Q4: 如何调试构建问题? + +1. 使用详细输出模式: + ```bash + scons --verbose + ``` + +2. 查看预处理结果: + ```bash + scons --target=mdk5 --verbose # 查看生成的项目配置 + ``` + +3. 检查依赖关系: + ```python + # 在SConscript中添加调试输出 + print('GetDepend result:', GetDepend('RT_USING_XXX')) + ``` + +### Q5: 如何加快编译速度? + +1. 使用多线程编译: + ```bash + scons -j$(nproc) # Linux/macOS + scons -j8 # Windows + ``` + +2. 使用ccache(GCC): + ```python + # 在rtconfig.py中 + CC = 'ccache ' + PREFIX + 'gcc' + ``` + +3. 优化依赖关系,避免不必要的重编译 + +### Q6: 如何处理第三方库? + +1. **作为源码包含** + ```python + # libraries/foo/SConscript + src = Glob('src/*.c') + CPPPATH = [GetCurrentDir() + '/include'] + + group = DefineGroup('foo', src, depend = ['RT_USING_FOO'], + CPPPATH = CPPPATH) + ``` + +2. **作为预编译库** + ```python + # 添加库文件 + LIBS = ['foo'] + LIBPATH = [GetCurrentDir() + '/lib'] + + group = DefineGroup('foo', [], depend = ['RT_USING_FOO'], + LIBS = LIBS, LIBPATH = LIBPATH) + ``` + +### Q7: 如何自定义链接脚本? + +在rtconfig.py中指定: +```python +# GCC工具链 +LINK_SCRIPT = 'board/link.lds' + +# Keil MDK +LINK_SCRIPT = 'board/link.sct' + +# IAR +LINK_SCRIPT = 'board/link.icf' +``` + +## 最佳实践 + +1. **模块化设计**:每个功能模块使用独立的SConscript +2. **依赖管理**:正确设置depend参数,避免编译不需要的代码 +3. **路径处理**:使用GetCurrentDir()获取当前路径,避免硬编码 +4. **条件编译**:合理使用GetDepend进行条件判断 +5. **编译选项**:全局选项放在rtconfig.py,局部选项使用LOCAL_xxx +6. **文档维护**:在SConscript中添加必要的注释说明 + +## 总结 + +RT-Thread的构建系统提供了强大而灵活的项目管理能力。通过合理使用各种构建选项和功能,可以高效地进行嵌入式软件开发。建议开发者深入理解构建系统的工作原理,以便更好地利用其功能。
\ No newline at end of file diff --git a/tools/docs/构建系统技术原理.md b/tools/docs/构建系统技术原理.md new file mode 100644 index 0000000000..d0a9ac0aaa --- /dev/null +++ b/tools/docs/构建系统技术原理.md @@ -0,0 +1,912 @@ +# RT-Thread 构建系统技术原理 + +## 目录 + +1. [系统架构设计](#系统架构设计) +2. [核心模块分析](#核心模块分析) +3. [构建流程详解](#构建流程详解) +4. [依赖管理机制](#依赖管理机制) +5. [工具链适配层](#工具链适配层) +6. [项目生成器架构](#项目生成器架构) +7. [配置系统实现](#配置系统实现) +8. [扩展机制](#扩展机制) + +## 系统架构设计 + +### 整体架构图 + +``` +┌────────────────────────────────────────────────────────────────────┐ +│ 用户接口层 │ +│ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ ┌──────────┐│ +│ │ 命令行接口 │ │ menuconfig │ │ 环境变量 │ │ 配置文件 ││ +│ │ (scons) │ │ (Kconfig) │ │ (RTT_xxx) │ │ (.config)││ +│ └──────┬──────┘ └──────┬───────┘ └──────┬──────┘ └─────┬────┘│ +└─────────┼─────────────────┼──────────────────┼───────────────┼─────┘ + │ │ │ │ +┌─────────▼─────────────────▼──────────────────▼───────────────▼─────┐ +│ 构建引擎层 │ +│ ┌─────────────────────────────────────────────────────────────┐ │ +│ │ building.py │ │ +│ ├─────────────┬───────────────┬─────────────┬────────────────┤ │ +│ │ 环境准备 │ 组件收集 │ 依赖处理 │ 构建执行 │ │ +│ │ Prepare │ DefineGroup │ GetDepend │ DoBuilding │ │ +│ └─────────────┴───────────────┴─────────────┴────────────────┘ │ +└────────────────────────────────────────────────────────────────────┘ + │ │ │ │ +┌─────────▼─────────────────▼──────────────────▼───────────────▼─────┐ +│ 工具支撑层 │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐ ┌──────┐│ +│ │ utils.py │ │options.py│ │package.py│ │mkdist.py│ │预处理││ +│ │ 工具函数 │ │命令选项 │ │包管理 │ │分发打包 │ │器 ││ +│ └──────────┘ └──────────┘ └──────────┘ └─────────┘ └──────┘│ +└────────────────────────────────────────────────────────────────────┘ + │ │ │ │ +┌─────────▼─────────────────▼──────────────────▼───────────────▼─────┐ +│ 目标生成器层 │ +│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────────┐ │ +│ │keil.py │ │iar.py │ │gcc.py │ │vsc.py │ │cmake.py等 │ │ +│ └────────┘ └────────┘ └────────┘ └────────┘ └────────────┘ │ +└────────────────────────────────────────────────────────────────────┘ +``` + +### 设计原则 + +1. **模块化设计**:每个功能模块独立,通过明确的接口交互 +2. **可扩展性**:易于添加新的工具链支持和目标生成器 +3. **跨平台兼容**:统一的抽象层处理平台差异 +4. **配置驱动**:通过配置文件控制构建行为 + +## 核心模块分析 + +### 1. building.py - 构建引擎核心 + +#### 1.1 全局变量管理 + +```python +BuildOptions = {} # 存储从rtconfig.h解析的宏定义 +Projects = [] # 存储所有的组件对象 +Rtt_Root = '' # RT-Thread根目录 +Env = None # SCons环境对象 +``` + +#### 1.2 PrepareBuilding 函数实现 + +```python +def PrepareBuilding(env, root_directory, has_libcpu=False, remove_components = []): + """ + 准备构建环境 + + 参数: + env: SCons环境对象 + root_directory: RT-Thread根目录 + has_libcpu: 是否包含libcpu + remove_components: 需要移除的组件列表 + """ + # 1. 添加命令行选项 + AddOptions() + + # 2. 设置全局环境变量 + global Env, Rtt_Root + Env = env + Rtt_Root = os.path.abspath(root_directory) + + # 3. 配置日志系统 + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger('rt-scons') + Env['log'] = logger + + # 4. 工具链检测和配置 + if not utils.CmdExists(os.path.join(rtconfig.EXEC_PATH, rtconfig.CC)): + # 尝试自动检测工具链 + try: + envm = utils.ImportModule('env_utility') + exec_path = envm.GetSDKPath(rtconfig.CC) + if exec_path: + rtconfig.EXEC_PATH = exec_path + except: + pass + + # 5. 解析rtconfig.h配置 + PreProcessor = create_preprocessor_instance() + with open('rtconfig.h', 'r') as f: + PreProcessor.process_contents(f.read()) + BuildOptions = PreProcessor.cpp_namespace + + # 6. 处理目标平台 + if GetOption('target'): + # 根据目标设置工具链 + rtconfig.CROSS_TOOL, rtconfig.PLATFORM = tgt_dict[tgt_name] + + return objs +``` + +#### 1.3 DefineGroup 函数实现 + +```python +def DefineGroup(name, src, depend, **parameters): + """ + 定义一个组件组 + + 参数: + name: 组名称 + src: 源文件列表 + depend: 依赖条件 + **parameters: 编译参数(CPPPATH, CPPDEFINES, LIBS等) + + 返回: + 组对象列表 + """ + # 1. 检查依赖条件 + if not GetDepend(depend): + return [] + + # 2. 处理源文件 + if isinstance(src, list): + # 过滤掉被移除的文件 + src = [s for s in src if s not in removed_src] + + # 3. 创建组对象 + group = {} + group['name'] = name + group['src'] = src + + # 4. 处理编译参数 + # 全局参数 + if 'CPPPATH' in parameters: + group['CPPPATH'] = parameters['CPPPATH'] + + # 本地参数(仅对当前组有效) + if 'LOCAL_CPPPATH' in parameters: + paths = parameters['LOCAL_CPPPATH'] + group['LOCAL_CPPPATH'] = [os.path.abspath(p) for p in paths] + + # 5. 注册到全局项目列表 + Projects.append(group) + + # 6. 返回SCons对象 + if src: + objs = Env.Object(src) + else: + objs = [] + + return objs +``` + +### 2. 依赖管理机制 + +#### 2.1 GetDepend 实现 + +```python +def GetDepend(depend): + """ + 检查依赖条件是否满足 + + 参数: + depend: 字符串或字符串列表 + + 返回: + True: 依赖满足 + False: 依赖不满足 + """ + # 1. 处理空依赖 + if not depend: + return True + + # 2. 处理字符串依赖 + if isinstance(depend, str): + return _CheckSingleDepend(depend) + + # 3. 处理列表依赖(AND关系) + if isinstance(depend, list): + for d in depend: + if not _CheckSingleDepend(d): + return False + return True + + return False + +def _CheckSingleDepend(depend): + """检查单个依赖""" + # 1. 检查是否在BuildOptions中定义 + if depend in BuildOptions: + # 2. 检查值是否为真 + return BuildOptions[depend] != '0' + return False +``` + +#### 2.2 依赖表达式支持 + +```python +# 支持的依赖表达式 +depend = 'RT_USING_SERIAL' # 单个依赖 +depend = ['RT_USING_LWIP', 'SAL'] # AND关系 +depend = '' # 无条件包含 + +# 高级用法 - 在SConscript中 +if GetDepend('RT_USING_LWIP'): + if GetDepend('RT_USING_LWIP_TCP'): + src += ['tcp.c'] + if GetDepend('RT_USING_LWIP_UDP'): + src += ['udp.c'] +``` + +### 3. 配置解析系统 + +#### 3.1 预处理器实现 + +```python +class PreProcessor: + """ + C预处理器实现,用于解析rtconfig.h + """ + def __init__(self): + self.cpp_namespace = {} + self.defines = {} + + def process_contents(self, contents): + """处理文件内容""" + lines = contents.split('\n') + + for line in lines: + # 处理 #define 指令 + if line.startswith('#define'): + self._process_define(line) + # 处理 #ifdef 等条件编译 + elif line.startswith('#ifdef'): + self._process_ifdef(line) + + def _process_define(self, line): + """处理宏定义""" + # #define RT_NAME_MAX 8 + parts = line.split(None, 2) + if len(parts) >= 2: + name = parts[1] + value = parts[2] if len(parts) > 2 else '1' + self.cpp_namespace[name] = value +``` + +#### 3.2 配置文件格式 + +**rtconfig.h 示例** +```c +/* RT-Thread 配置文件 */ +#ifndef RT_CONFIG_H__ +#define RT_CONFIG_H__ + +/* 内核配置 */ +#define RT_THREAD_PRIORITY_32 +#define RT_THREAD_PRIORITY_MAX 32 +#define RT_TICK_PER_SECOND 100 +#define RT_USING_TIMER_SOFT + +/* 组件配置 */ +#define RT_USING_DEVICE +#define RT_USING_SERIAL +#define RT_SERIAL_RB_BUFSZ 64 + +/* 条件配置 */ +#ifdef RT_USING_SERIAL + #define RT_SERIAL_USING_DMA +#endif + +#endif /* RT_CONFIG_H__ */ +``` + +### 4. 工具链适配层 + +#### 4.1 工具链抽象接口 + +```python +class ToolchainBase: + """工具链基类""" + def __init__(self): + self.name = '' + self.prefix = '' + self.suffix = '' + + def get_cc(self): + """获取C编译器""" + raise NotImplementedError + + def get_cflags(self): + """获取C编译选项""" + raise NotImplementedError + + def get_linkflags(self): + """获取链接选项""" + raise NotImplementedError +``` + +#### 4.2 GCC工具链实现 + +```python +class GccToolchain(ToolchainBase): + def __init__(self, prefix=''): + self.name = 'gcc' + self.prefix = prefix + self.suffix = '' + + def get_cc(self): + return self.prefix + 'gcc' + + def get_cflags(self): + flags = [] + # 基础选项 + flags += ['-Wall', '-g'] + # 优化选项 + if GetOption('optimization') == 'size': + flags += ['-Os'] + else: + flags += ['-O0'] + # 架构选项 + flags += ['-mcpu=cortex-m3', '-mthumb'] + return ' '.join(flags) +``` + +#### 4.3 Keil MDK适配 + +```python +class KeilToolchain(ToolchainBase): + def __init__(self): + self.name = 'keil' + + def setup_environment(self, env): + """设置Keil特定的环境变量""" + # 修改文件扩展名 + env['OBJSUFFIX'] = '.o' + env['LIBPREFIX'] = '' + env['LIBSUFFIX'] = '.lib' + + # 设置编译命令 + env['CC'] = 'armcc' + env['AS'] = 'armasm' + env['AR'] = 'armar' + env['LINK'] = 'armlink' + + # 设置命令格式 + env['ARCOM'] = '$AR --create $TARGET $SOURCES' +``` + +### 5. 项目生成器架构 + +#### 5.1 生成器基类 + +```python +class ProjectGenerator: + """项目生成器基类""" + def __init__(self, env, project): + self.env = env + self.project = project + self.template_dir = '' + + def generate(self): + """生成项目文件""" + self._prepare() + self._generate_project_file() + self._generate_workspace_file() + self._copy_template_files() + self._post_process() + + def _collect_source_files(self): + """收集源文件""" + sources = [] + for group in self.project: + sources.extend(group['src']) + return sources + + def _collect_include_paths(self): + """收集头文件路径""" + paths = [] + for group in self.project: + if 'CPPPATH' in group: + paths.extend(group['CPPPATH']) + return list(set(paths)) # 去重 +``` + +#### 5.2 VS Code生成器实现 + +```python +class VSCodeGenerator(ProjectGenerator): + """VS Code项目生成器""" + + def _generate_project_file(self): + """生成VS Code配置文件""" + # 创建.vscode目录 + vscode_dir = os.path.join(self.env['BSP_ROOT'], '.vscode') + if not os.path.exists(vscode_dir): + os.makedirs(vscode_dir) + + # 生成c_cpp_properties.json + self._generate_cpp_properties() + + # 生成tasks.json + self._generate_tasks() + + # 生成launch.json + self._generate_launch() + + def _generate_cpp_properties(self): + """生成IntelliSense配置""" + config = { + "configurations": [{ + "name": "RT-Thread", + "includePath": self._collect_include_paths(), + "defines": self._collect_defines(), + "compilerPath": self._get_compiler_path(), + "cStandard": "c99", + "cppStandard": "c++11", + "intelliSenseMode": "gcc-arm" + }], + "version": 4 + } + + # 写入文件 + file_path = os.path.join('.vscode', 'c_cpp_properties.json') + with open(file_path, 'w') as f: + json.dump(config, f, indent=4) +``` + +#### 5.3 Keil MDK5生成器 + +```python +class MDK5Generator(ProjectGenerator): + """Keil MDK5项目生成器""" + + def _generate_project_file(self): + """生成uvprojx文件""" + # 加载XML模板 + tree = etree.parse(self.template_file) + root = tree.getroot() + + # 更新目标配置 + self._update_target_options(root) + + # 添加文件组 + groups_node = root.find('.//Groups') + for group in self.project: + self._add_file_group(groups_node, group) + + # 保存项目文件 + tree.write('project.uvprojx', encoding='utf-8', + xml_declaration=True) + + def _add_file_group(self, parent, group): + """添加文件组""" + group_elem = etree.SubElement(parent, 'Group') + + # 组名 + name_elem = etree.SubElement(group_elem, 'GroupName') + name_elem.text = group['name'] + + # 文件列表 + files_elem = etree.SubElement(group_elem, 'Files') + for src in group['src']: + self._add_file(files_elem, src) +``` + +### 6. 编译数据库生成 + +#### 6.1 compile_commands.json生成 + +```python +def generate_compile_commands(env, project): + """ + 生成compile_commands.json用于代码分析工具 + """ + commands = [] + + for group in project: + for src in group['src']: + if src.endswith('.c') or src.endswith('.cpp'): + cmd = { + "directory": env['BSP_ROOT'], + "file": os.path.abspath(src), + "command": _generate_compile_command(env, src, group) + } + commands.append(cmd) + + # 写入文件 + with open('compile_commands.json', 'w') as f: + json.dump(commands, f, indent=2) + +def _generate_compile_command(env, src, group): + """生成单个文件的编译命令""" + cmd = [] + + # 编译器 + cmd.append(env['CC']) + + # 编译选项 + cmd.extend(env['CFLAGS'].split()) + + # 头文件路径 + for path in group.get('CPPPATH', []): + cmd.append('-I' + path) + + # 宏定义 + for define in group.get('CPPDEFINES', []): + if isinstance(define, tuple): + cmd.append('-D{}={}'.format(define[0], define[1])) + else: + cmd.append('-D' + define) + + # 源文件 + cmd.append(src) + + return ' '.join(cmd) +``` + +### 7. 分发系统实现 + +#### 7.1 分发包生成流程 + +```python +def MkDist(program, BSP_ROOT, RTT_ROOT, Env, project): + """生成分发包""" + # 1. 创建分发目录 + dist_name = os.path.basename(BSP_ROOT) + dist_dir = os.path.join(BSP_ROOT, 'dist', dist_name) + + # 2. 复制RT-Thread内核 + print('=> copy RT-Thread kernel') + copytree(os.path.join(RTT_ROOT, 'src'), + os.path.join(dist_dir, 'rt-thread', 'src')) + copytree(os.path.join(RTT_ROOT, 'include'), + os.path.join(dist_dir, 'rt-thread', 'include')) + + # 3. 复制使用的组件 + print('=> copy components') + for group in project: + _copy_group_files(group, dist_dir) + + # 4. 生成Kconfig文件 + _generate_kconfig(dist_dir, project) + + # 5. 打包 + make_zip(dist_dir, dist_name + '.zip') +``` + +#### 7.2 精简分发包生成 + +```python +def MkDist_Strip(program, BSP_ROOT, RTT_ROOT, Env): + """ + 基于compile_commands.json生成精简分发包 + 只包含实际使用的文件 + """ + # 1. 解析compile_commands.json + with open('compile_commands.json', 'r') as f: + commands = json.load(f) + + # 2. 提取使用的文件 + used_files = set() + for cmd in commands: + # 源文件 + used_files.add(cmd['file']) + + # 解析包含的头文件 + includes = _parse_includes(cmd['file'], cmd['command']) + used_files.update(includes) + + # 3. 复制文件 + for file in used_files: + _copy_with_structure(file, dist_dir) +``` + +## 构建流程详解 + +### 完整构建流程图 + +``` +┌──────────────┐ +│ 用户输入 │ +│ scons │ +└──────┬───────┘ + │ +┌──────▼───────┐ +│ SConstruct │ ← 读取rtconfig.py +│ 主脚本 │ ← 调用PrepareBuilding +└──────┬───────┘ + │ +┌──────▼───────┐ +│ 环境初始化 │ +│ ·设置路径 │ +│ ·检测工具链 │ +│ ·解析配置 │ +└──────┬───────┘ + │ +┌──────▼───────┐ +│ 递归处理 │ +│ SConscript │ ← 调用DefineGroup +│ ·收集源文件 │ ← 检查依赖GetDepend +│ ·设置参数 │ +└──────┬───────┘ + │ +┌──────▼───────┐ +│ 构建执行 │ +│ DoBuilding │ +│ ·合并对象 │ +│ ·链接程序 │ +└──────┬───────┘ + │ +┌──────▼───────┐ +│ 后处理 │ +│ ·生成bin文件 │ +│ ·显示大小 │ +│ ·自定义动作 │ +└──────────────┘ +``` + +### 依赖解析流程 + +```python +def dependency_resolution_flow(): + """ + 依赖解析流程示例 + """ + # 1. 从rtconfig.h读取所有宏定义 + macros = parse_rtconfig_h() + # 例: {'RT_USING_SERIAL': '1', 'RT_USING_PIN': '1'} + + # 2. 处理单个组件 + for component in components: + # 3. 检查依赖条件 + if check_dependencies(component.depends, macros): + # 4. 包含组件 + include_component(component) + else: + # 5. 跳过组件 + skip_component(component) + + # 6. 递归处理子依赖 + resolve_sub_dependencies() +``` + +## 扩展机制 + +### 1. 添加新的工具链支持 + +```python +# 1. 在tgt_dict中添加映射 +tgt_dict['mycc'] = ('mycc', 'mycc') + +# 2. 创建tools/mycc.py +import os +from building import * + +def generate_project(env, project): + """生成项目文件""" + print("Generating MyCC project...") + + # 收集信息 + info = ProjectInfo(env, project) + + # 生成项目文件 + # ... + +# 3. 在rtconfig.py中配置 +CROSS_TOOL = 'mycc' +PLATFORM = 'mycc' +``` + +### 2. 添加自定义构建步骤 + +```python +# 在SConstruct或SConscript中 +def custom_builder(target, source, env): + """自定义构建器""" + # 执行自定义操作 + cmd = 'custom_tool -o {} {}'.format(target[0], source[0]) + os.system(cmd) + +# 注册构建器 +env['BUILDERS']['CustomBuild'] = Builder(action=custom_builder, + suffix='.out', + src_suffix='.in') + +# 使用构建器 +custom_out = env.CustomBuild('output.out', 'input.in') +``` + +### 3. 扩展配置解析器 + +```python +class ExtendedPreProcessor(PreProcessor): + """扩展的预处理器""" + + def __init__(self): + super().__init__() + self.custom_handlers = {} + + def register_handler(self, directive, handler): + """注册自定义指令处理器""" + self.custom_handlers[directive] = handler + + def process_line(self, line): + """处理单行""" + # 检查自定义指令 + for directive, handler in self.custom_handlers.items(): + if line.startswith(directive): + return handler(line) + + # 默认处理 + return super().process_line(line) +``` + +### 4. 插件系统实现 + +```python +class BuildPlugin: + """构建插件基类""" + + def __init__(self, name): + self.name = name + + def pre_build(self, env, project): + """构建前钩子""" + pass + + def post_build(self, env, project): + """构建后钩子""" + pass + + def configure(self, env): + """配置环境""" + pass + +# 插件管理器 +class PluginManager: + def __init__(self): + self.plugins = [] + + def register(self, plugin): + self.plugins.append(plugin) + + def run_pre_build(self, env, project): + for plugin in self.plugins: + plugin.pre_build(env, project) +``` + +## 性能优化 + +### 1. 构建缓存机制 + +```python +class BuildCache: + """构建缓存""" + + def __init__(self, cache_dir='.scache'): + self.cache_dir = cache_dir + self.cache_db = os.path.join(cache_dir, 'cache.db') + + def get_hash(self, file): + """计算文件哈希""" + import hashlib + with open(file, 'rb') as f: + return hashlib.md5(f.read()).hexdigest() + + def is_cached(self, source, target): + """检查是否已缓存""" + # 检查目标文件是否存在 + if not os.path.exists(target): + return False + + # 检查源文件是否更新 + source_hash = self.get_hash(source) + cached_hash = self.load_hash(source) + + return source_hash == cached_hash +``` + +### 2. 并行构建优化 + +```python +def optimize_parallel_build(env, project): + """优化并行构建""" + # 1. 分析依赖关系 + dep_graph = analyze_dependencies(project) + + # 2. 计算最优构建顺序 + build_order = topological_sort(dep_graph) + + # 3. 分组独立任务 + parallel_groups = [] + for level in build_order: + # 同一层级可以并行 + parallel_groups.append(level) + + # 4. 设置并行度 + import multiprocessing + num_jobs = multiprocessing.cpu_count() + env.SetOption('num_jobs', num_jobs) + + return parallel_groups +``` + +## 调试技巧 + +### 1. 构建日志分析 + +```python +# 启用详细日志 +def enable_build_logging(): + # 设置SCons日志 + env.SetOption('debug', 'explain') + + # 自定义日志 + class BuildLogger: + def __init__(self, logfile): + self.logfile = logfile + + def __call__(self, msg, *args): + with open(self.logfile, 'a') as f: + f.write(msg % args + '\n') + + logger = BuildLogger('build.log') + env['PRINT_CMD_LINE_FUNC'] = logger +``` + +### 2. 依赖关系可视化 + +```python +def visualize_dependencies(project): + """生成依赖关系图""" + import graphviz + + dot = graphviz.Digraph(comment='Dependencies') + + # 添加节点 + for group in project: + dot.node(group['name']) + + # 添加边 + for group in project: + for dep in group.get('depends', []): + if find_group(dep): + dot.edge(dep, group['name']) + + # 渲染 + dot.render('dependencies', format='png') +``` + +## 最佳实践 + +### 1. 模块化设计原则 + +- 每个功能模块独立的SConscript +- 明确的依赖关系声明 +- 避免循环依赖 +- 使用统一的命名规范 + +### 2. 性能优化建议 + +- 使用Glob谨慎,大目录下性能差 +- 合理设置并行编译数 +- 使用增量编译 +- 避免重复的文件扫描 + +### 3. 可维护性建议 + +- 添加充分的注释 +- 使用有意义的变量名 +- 遵循Python PEP8规范 +- 定期清理无用代码 + +### 4. 跨平台兼容性 + +- 使用os.path处理路径 +- 避免平台特定的命令 +- 测试多平台构建 +- 处理路径分隔符差异 + +## 总结 + +RT-Thread的构建系统是一个精心设计的模块化系统,通过清晰的架构和灵活的扩展机制,为嵌入式开发提供了强大的构建能力。理解其内部原理有助于: + +1. 更好地使用和优化构建流程 +2. 快速定位和解决构建问题 +3. 扩展支持新的工具链和平台 +4. 为项目定制构建流程 + +构建系统的核心价值在于将复杂的嵌入式构建过程标准化和自动化,让开发者能够专注于功能开发而不是构建配置。
\ No newline at end of file |
