You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
948 lines
20 KiB
948 lines
20 KiB
|
5 months ago
|
# 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项目的构建提供坚实的基础。
|