Files
rt-thread/tools/docs/构建系统使用指南.md
2025-08-07 09:32:45 +08:00

618 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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的构建系统提供了强大而灵活的项目管理能力。通过合理使用各种构建选项和功能可以高效地进行嵌入式软件开发。建议开发者深入理解构建系统的工作原理以便更好地利用其功能。