构建系统使用指南.md 14 KB

RT-Thread 构建系统使用指南

目录

  1. 概述
  2. 快速开始
  3. 命令行选项详解
  4. 工具链配置
  5. 项目生成
  6. 软件包管理
  7. 高级功能
  8. 常见问题

概述

RT-Thread使用基于SCons的构建系统,提供了统一的跨平台构建体验。构建系统支持:

  • 多种编译器和IDE(GCC、Keil、IAR、VS Code等)
  • 模块化的组件管理
  • 灵活的配置系统
  • 自动化的依赖处理
  • 软件包管理功能

系统架构图

arch

快速开始

基本编译流程

  1. 进入BSP目录

    cd bsp/stm32/stm32f103-blue-pill
    
  2. 配置系统(可选)

    menuconfig        # 图形化配置
    
  3. 编译项目

    scons            # 默认编译
    scons -j8        # 多线程编译
    
  4. 生成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静态分析 代码质量检查

工具链配置

rtconfig.py配置文件

每个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'

支持的工具链

  1. GCC工具链

    CROSS_TOOL = 'gcc'
    PREFIX = 'arm-none-eabi-'
    
  2. Keil MDK

    CROSS_TOOL = 'keil'
    PLATFORM = 'armcc'      # ARM Compiler 5
    # 或
    PLATFORM = 'armclang'   # ARM Compiler 6
    
  3. IAR

    CROSS_TOOL = 'iar'
    PLATFORM = 'iccarm'
    
  4. 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

项目生成

VS Code项目配置

使用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
            }
        }
    ]
}

CMake项目生成

使用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
)

软件包管理

使用package.json定义组件

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"]
        }
    }
}

在SConscript中使用BuildPackage

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. 条件编译和依赖管理

基于宏定义的条件编译

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支持')

2. 本地编译选项

为特定模块设置独立的编译选项:

# 全局编译选项
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. 递归构建子目录

自动扫描并构建子目录:

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. 自定义构建动作

添加构建前后的自定义动作:

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. 分发包定制

创建自定义分发包:

# 在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静态分析

scons --clang-analyzer

生成compile_commands.json

scons --target=cmake  # CMake项目会包含compile_commands.json
# 或使用
scons --compile-commands

生成Cscope数据库

scons --cscope

常见问题

Q1: 如何添加新的源文件?

在相应目录的SConscript中添加:

src = Glob('*.c')  # 自动包含所有.c文件
# 或
src = ['file1.c', 'file2.c']  # 手动指定

Q2: 如何排除特定文件?

src = Glob('*.c')
SrcRemove(src, ['test.c', 'debug.c'])

Q3: 如何处理不同配置下的源文件?

src = ['common.c']

if rtconfig.PLATFORM == 'gcc':
    src += ['gcc_specific.c']
elif rtconfig.PLATFORM == 'armcc':
    src += ['keil_specific.c']

Q4: 如何调试构建问题?

  1. 使用详细输出模式:

    scons --verbose
    
  2. 查看预处理结果:

    scons --target=mdk5 --verbose  # 查看生成的项目配置
    
  3. 检查依赖关系:

    # 在SConscript中添加调试输出
    print('GetDepend result:', GetDepend('RT_USING_XXX'))
    

Q5: 如何加快编译速度?

  1. 使用多线程编译:

    scons -j$(nproc)  # Linux/macOS
    scons -j8         # Windows
    
  2. 使用ccache(GCC):

    # 在rtconfig.py中
    CC = 'ccache ' + PREFIX + 'gcc'
    
  3. 优化依赖关系,避免不必要的重编译

Q6: 如何处理第三方库?

  1. 作为源码包含

    # libraries/foo/SConscript
    src = Glob('src/*.c')
    CPPPATH = [GetCurrentDir() + '/include']
       
    group = DefineGroup('foo', src, depend = ['RT_USING_FOO'], 
                       CPPPATH = CPPPATH)
    
  2. 作为预编译库

    # 添加库文件
    LIBS = ['foo']
    LIBPATH = [GetCurrentDir() + '/lib']
       
    group = DefineGroup('foo', [], depend = ['RT_USING_FOO'],
                       LIBS = LIBS, LIBPATH = LIBPATH)
    

Q7: 如何自定义链接脚本?

在rtconfig.py中指定:

# 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的构建系统提供了强大而灵活的项目管理能力。通过合理使用各种构建选项和功能,可以高效地进行嵌入式软件开发。建议开发者深入理解构建系统的工作原理,以便更好地利用其功能。