RT-Thread使用基于SCons的构建系统,提供了统一的跨平台构建体验。构建系统支持:
进入BSP目录
cd bsp/stm32/stm32f103-blue-pill
配置系统(可选)
menuconfig # 图形化配置
编译项目
scons # 默认编译
scons -j8 # 多线程编译
生成IDE项目
scons --target=mdk5 # 生成Keil MDK5项目
scons --target=iar # 生成IAR项目
scons --target=vsc # 生成VS Code项目
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静态分析 | 代码质量检查 |
每个BSP都有一个rtconfig.py
文件,定义了工具链配置:
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'
GCC工具链
CROSS_TOOL = 'gcc'
PREFIX = 'arm-none-eabi-'
Keil MDK
CROSS_TOOL = 'keil'
PLATFORM = 'armcc' # ARM Compiler 5
# 或
PLATFORM = 'armclang' # ARM Compiler 6
IAR
CROSS_TOOL = 'iar'
PLATFORM = 'iccarm'
RISC-V GCC
CROSS_TOOL = 'gcc'
PREFIX = 'riscv64-unknown-elf-'
构建系统支持通过环境变量覆盖配置:
# 设置工具链路径
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
使用scons --target=vsc
生成VS Code项目,会创建以下配置文件:
.vscode/c_cpp_properties.json - IntelliSense配置
{
"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 - 构建任务配置
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "shell",
"command": "scons",
"problemMatcher": "$gcc",
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
使用scons --target=cmake
生成CMakeLists.txt:
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
)
RT-Thread支持使用package.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"]
}
}
}
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')
基于宏定义的条件编译
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'])
复杂依赖表达式
# 依赖可以是列表(AND关系)
depend = ['RT_USING_LWIP', 'RT_USING_NETDEV']
# 或者使用GetDepend进行复杂判断
if GetDepend('RT_USING_LWIP') and not GetDepend('RT_USING_SAL'):
print('配置错误:LWIP需要SAL支持')
为特定模块设置独立的编译选项:
# 全局编译选项
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
)
自动扫描并构建子目录:
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')
添加构建前后的自定义动作:
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
创建自定义分发包:
# 在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)
Clang静态分析
scons --clang-analyzer
生成compile_commands.json
scons --target=cmake # CMake项目会包含compile_commands.json
# 或使用
scons --compile-commands
生成Cscope数据库
scons --cscope
在相应目录的SConscript中添加:
src = Glob('*.c') # 自动包含所有.c文件
# 或
src = ['file1.c', 'file2.c'] # 手动指定
src = Glob('*.c')
SrcRemove(src, ['test.c', 'debug.c'])
src = ['common.c']
if rtconfig.PLATFORM == 'gcc':
src += ['gcc_specific.c']
elif rtconfig.PLATFORM == 'armcc':
src += ['keil_specific.c']
使用详细输出模式:
scons --verbose
查看预处理结果:
scons --target=mdk5 --verbose # 查看生成的项目配置
检查依赖关系:
# 在SConscript中添加调试输出
print('GetDepend result:', GetDepend('RT_USING_XXX'))
使用多线程编译:
scons -j$(nproc) # Linux/macOS
scons -j8 # Windows
使用ccache(GCC):
# 在rtconfig.py中
CC = 'ccache ' + PREFIX + 'gcc'
优化依赖关系,避免不必要的重编译
作为源码包含
# libraries/foo/SConscript
src = Glob('src/*.c')
CPPPATH = [GetCurrentDir() + '/include']
group = DefineGroup('foo', src, depend = ['RT_USING_FOO'],
CPPPATH = CPPPATH)
作为预编译库
# 添加库文件
LIBS = ['foo']
LIBPATH = [GetCurrentDir() + '/lib']
group = DefineGroup('foo', [], depend = ['RT_USING_FOO'],
LIBS = LIBS, LIBPATH = LIBPATH)
在rtconfig.py中指定:
# GCC工具链
LINK_SCRIPT = 'board/link.lds'
# Keil MDK
LINK_SCRIPT = 'board/link.sct'
# IAR
LINK_SCRIPT = 'board/link.icf'
RT-Thread的构建系统提供了强大而灵活的项目管理能力。通过合理使用各种构建选项和功能,可以高效地进行嵌入式软件开发。建议开发者深入理解构建系统的工作原理,以便更好地利用其功能。