first commit for chrg
This commit is contained in:
@@ -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**: 检查依赖的核心函数
|
||||
|
||||
## 构建系统架构图
|
||||
|
||||

|
||||
|
||||
## 主要特性
|
||||
|
||||
✅ **多工具链支持**
|
||||
- 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的构建系统,提供了统一的跨平台构建体验。构建系统支持:
|
||||
|
||||
- 多种编译器和IDE(GCC、Keil、IAR、VS Code等)
|
||||
- 模块化的组件管理
|
||||
- 灵活的配置系统
|
||||
- 自动化的依赖处理
|
||||
- 软件包管理功能
|
||||
|
||||
### 系统架构图
|
||||
|
||||

|
||||
|
||||
## 快速开始
|
||||
|
||||
### 基本编译流程
|
||||
|
||||
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的构建系统提供了强大而灵活的项目管理能力。通过合理使用各种构建选项和功能,可以高效地进行嵌入式软件开发。建议开发者深入理解构建系统的工作原理,以便更好地利用其功能。
|
||||
@@ -0,0 +1,841 @@
|
||||
# RT-Thread 构建系统技术原理
|
||||
|
||||
## 目录
|
||||
|
||||
1. [系统架构设计](#系统架构设计)
|
||||
2. [核心模块分析](#核心模块分析)
|
||||
3. [构建流程详解](#构建流程详解)
|
||||
4. [依赖管理机制](#依赖管理机制)
|
||||
5. [工具链适配层](#工具链适配层)
|
||||
6. [项目生成器架构](#项目生成器架构)
|
||||
7. [配置系统实现](#配置系统实现)
|
||||
8. [扩展机制](#扩展机制)
|
||||
|
||||
## 系统架构设计
|
||||
|
||||
### 整体架构图
|
||||
|
||||

|
||||
|
||||
### 设计原则
|
||||
|
||||
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)
|
||||
```
|
||||
|
||||
## 构建流程详解
|
||||
|
||||
### 完整构建流程图
|
||||
|
||||

|
||||
|
||||
### 依赖解析流程
|
||||
|
||||
```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. 为项目定制构建流程
|
||||
|
||||
构建系统的核心价值在于将复杂的嵌入式构建过程标准化和自动化,让开发者能够专注于功能开发而不是构建配置。
|
||||
Reference in New Issue
Block a user