first commit for chrg

This commit is contained in:
wmano
2025-08-16 22:58:22 +08:00
commit 52a3ed5862
2306 changed files with 1021208 additions and 0 deletions
+136
View File
@@ -0,0 +1,136 @@
# 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**: 检查依赖的核心函数
## 构建系统架构图
![arch](./readme_arch.drawio.png)
## 主要特性
**多工具链支持**
- 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版本。
@@ -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项目的构建提供坚实的基础。
Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

@@ -0,0 +1,128 @@
# RT-Thread package.json 构建支持
## 概述
RT-Thread支持使用package.json来定义组件的构建配置,作为传统SConscript的简化替代方案。
## 现有支持
### package.json格式
```json
{
"name": "hello",
"description": "Hello World component for RT-Thread",
"type": "rt-thread-component",
"dependencies": ["RT_USING_HELLO"],
"defines": [],
"sources": [{
"name": "src",
"dependencies": [],
"includes": ["."],
"files": ["hello.c"]
}]
}
```
### 字段说明
- **name**: 组件名称(必需)
- **type**: 必须为"rt-thread-component"(必需)
- **description**: 组件描述
- **dependencies**: 全局依赖,数组形式的宏定义
- **defines**: 全局宏定义
- **sources**: 源文件组数组,每组可包含:
- **name**: 源组名称
- **dependencies**: 源组特定依赖
- **includes**: 头文件搜索路径
- **files**: 源文件列表(支持通配符)
## 使用方式
### 1. 在SConscript中使用
方式一:使用PackageSConscript(推荐)
```python
from building import *
objs = PackageSConscript('package.json')
Return('objs')
```
方式二:直接调用BuildPackage
```python
Import('env')
from package import BuildPackage
objs = BuildPackage('package.json')
Return('objs')
```
### 2. 目录结构示例
```
mycomponent/
├── SConscript
├── package.json
├── mycomponent.c
├── mycomponent.h
└── src/
└── helper.c
```
### 3. 完整示例
package.json:
```json
{
"name": "mycomponent",
"description": "My RT-Thread component",
"type": "rt-thread-component",
"dependencies": ["RT_USING_MYCOMPONENT"],
"defines": ["MY_VERSION=1"],
"sources": [
{
"name": "main",
"dependencies": [],
"includes": ["."],
"files": ["mycomponent.c"]
},
{
"name": "helper",
"dependencies": ["RT_USING_MYCOMPONENT_HELPER"],
"includes": ["src"],
"files": ["src/*.c"]
}
]
}
```
## 工作原理
1. **依赖检查**:首先检查全局dependencies,如果不满足则跳过整个组件
2. **源组处理**:遍历sources数组,每个源组独立检查dependencies
3. **路径处理**includes相对路径基于package.json所在目录
4. **文件匹配**:使用SCons的Glob函数处理文件通配符
5. **构建调用**:最终调用DefineGroup创建构建组
## 与DefineGroup的对比
| 特性 | package.json | DefineGroup |
|------|--------------|-------------|
| 配置方式 | JSON文件 | Python代码 |
| 依赖管理 | 结构化 | 函数参数 |
| 源文件组织 | 分组管理 | 单一列表 |
| 条件编译 | 源组级别 | 整体级别 |
| 灵活性 | 中等 | 高 |
| 易用性 | 高 | 中等 |
## 最佳实践
1. **简单组件优先使用package.json**:配置清晰,易于维护
2. **复杂逻辑使用SConscript**:需要动态逻辑时使用传统方式
3. **混合使用**:可以在同一项目中混用两种方式
## 注意事项
1. package.json必须是有效的JSON格式
2. type字段必须为"rt-thread-component"
3. 文件路径相对于package.json所在目录
4. 依赖不满足时会静默跳过,不会报错
5. 与RT-Thread构建系统完全集成,不支持独立构建
Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

@@ -0,0 +1,618 @@
# RT-Thread 构建系统使用指南
## 目录
1. [概述](#概述)
2. [快速开始](#快速开始)
3. [命令行选项详解](#命令行选项详解)
4. [工具链配置](#工具链配置)
5. [项目生成](#项目生成)
6. [软件包管理](#软件包管理)
7. [高级功能](#高级功能)
8. [常见问题](#常见问题)
## 概述
RT-Thread使用基于SCons的构建系统,提供了统一的跨平台构建体验。构建系统支持:
- 多种编译器和IDEGCC、Keil、IAR、VS Code等)
- 模块化的组件管理
- 灵活的配置系统
- 自动化的依赖处理
- 软件包管理功能
### 系统架构图
![arch](./guide_arch.drawio.png)
## 快速开始
### 基本编译流程
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. 使用ccacheGCC):
```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的构建系统提供了强大而灵活的项目管理能力。通过合理使用各种构建选项和功能,可以高效地进行嵌入式软件开发。建议开发者深入理解构建系统的工作原理,以便更好地利用其功能。
@@ -0,0 +1,841 @@
# RT-Thread 构建系统技术原理
## 目录
1. [系统架构设计](#系统架构设计)
2. [核心模块分析](#核心模块分析)
3. [构建流程详解](#构建流程详解)
4. [依赖管理机制](#依赖管理机制)
5. [工具链适配层](#工具链适配层)
6. [项目生成器架构](#项目生成器架构)
7. [配置系统实现](#配置系统实现)
8. [扩展机制](#扩展机制)
## 系统架构设计
### 整体架构图
![arch](./tech_arch.drawio.png)
### 设计原则
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)
```
## 构建流程详解
### 完整构建流程图
![process](./process.drawio.png)
### 依赖解析流程
```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. 为项目定制构建流程
构建系统的核心价值在于将复杂的嵌入式构建过程标准化和自动化,让开发者能够专注于功能开发而不是构建配置。