mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-03-23 17:48:27 +08:00
618 lines
14 KiB
Markdown
618 lines
14 KiB
Markdown
# 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的构建系统提供了强大而灵活的项目管理能力。通过合理使用各种构建选项和功能,可以高效地进行嵌入式软件开发。建议开发者深入理解构建系统的工作原理,以便更好地利用其功能。 |