Scons入门到入土 - (嵌入式提升篇)
本文记录SCons的使用流程和使用方法。
Written by: Zhai Xiufeng
- 概述
SCons 是一个开源的、跨平台的自动化构建工具,类似于 Make,但使用 Python 脚本作为配置文件。它主要用于软件项目的编译、链接和构建。特别适用于自动化工程构建。
- Scons基本语句和用法
以下是 SCons 的主要函数、语句和用法,按功能分类,附带说明和示例。
2.1 环境创建与配置
SCons 的构建基于 Environment 对象,用于设置编译器、标志、路径等。
Environment(**kwargs)
创建一个新的构建环境。
用法:初始化编译器、标志、路径等。
参数:
CC:指定 C 编译器(如 'gcc'、'clang')。
CXX:指定 C++ 编译器。
CCFLAGS:编译器标志(如 '-Wall')。
CPPPATH:头文件搜索路径。
LIBPATH:库文件搜索路径。
LIBS:链接的库(如 ['m', 'pthread'])。
示例:
env = Environment(CC='gcc', CCFLAGS='-Wall', CPPPATH=['include'], LIBS=['m'])
env.Clone(**kwargs)
克隆现有环境并修改参数,避免影响原始环境。
用法:为不同目标创建独立的配置。
参数:
CC:指定 C 编译器(如 'gcc'、'clang')。
CXX:指定 C++ 编译器。
CCFLAGS:编译器标志(如 '-Wall')。
CPPPATH:头文件搜索路径。
LIBPATH:库文件搜索路径。
LIBS:链接的库(如 ['m', 'pthread'])。
示例:
env = Environment(CCFLAGS='-O2')
debug_env = env.Clone(CCFLAGS='-g') # 创建带调试标志的环境
DefaultEnvironment(**kwargs)
获取或修改默认环境(全局环境)。
用法:设置全局默认配置。
参数:
CC:指定 C 编译器(如 'gcc'、'clang')。
CXX:指定 C++ 编译器。
CCFLAGS:编译器标志(如 '-Wall')。
CPPPATH:头文件搜索路径。
LIBPATH:库文件搜索路径。
LIBS:链接的库(如 ['m', 'pthread'])。
示例:
DefaultEnvironment(CCFLAGS='-Wall')
2.2 文件匹配与源文件处理
SCons 提供函数来匹配和处理源文件。
Glob(pattern)
匹配指定模式的文件(如 *.c)。
用法:自动收集源文件。
示例:
c_files = Glob('src/*.c') # 匹配 src 目录下所有 .c 文件
env.Program('my_program', c_files)
Split(string)
将字符串拆分为文件列表。
用法:手动指定多个源文件。
示例:
sources = Split('main.c util.c')
env.Program('my_program', sources)
File(filename)
创建文件节点,显式指定单个文件。
用法:精确控制文件。
示例:
main_file = File('main.c')
env.Program('my_program', main_file)
Dir(dirname)
创建目录节点。
用法:指定目录路径。
示例:
src_dir = Dir('src')
2.3 构建目标(Builders)
SCons 使用 Builder 函数生成目标文件(如可执行文件、库)。
Program(target, source, **kwargs)
编译生成可执行文件。
参数:
target:输出文件名。
source:源文件列表。 示例:
env.Program(target='my_program', source=Glob('*.c'))
Object(target, source)
编译生成目标文件(.o 文件)。
用法:单独编译源文件。
示例:
env.Object('main.o', 'main.c')
Library(target, source)
生成静态库(.a 文件)。
示例:
env.Library('mylib', Glob('src/*.c'))
SharedLibrary(target, source)
生成动态库(.so 或 .dll 文件)。
示例:
env.SharedLibrary('mylib', Glob('src/*.c'))
Install(target_dir, source)
安装文件到指定目录。
用法:将生成的文件复制到目标路径。
示例:
env.Install('bin', 'my_program')
Alias(alias, targets)
为构建目标创建别名,方便命令行调用。
用法:简化复杂目标的构建。
示例:
env.Alias('build', 'my_program')
# 运行 `SCons build` 等价于构建 my_program
2.4 依赖管理
SCons 自动管理依赖,但也可以显式指定。
Depends(target, dependency)
显式指定目标的依赖关系。
用法:强制依赖某些文件。
示例:
env.Depends('my_program', 'config.h')
Ignore(target, dependency)
忽略某些依赖。
用法:排除不必要的依赖检查。
示例:
env.Ignore('my_program', 'old_config.h')
SideEffect(filename, target)
指定构建过程中的副产物文件。
用法:管理中间文件。
示例:
env.SideEffect('temp.o', 'my_program')
2.5 子目录与模块化
SCons 支持通过 SConscript 文件管理子目录。
SConscript(files, [exports, variant_dir])
调用子构建脚本。
参数:
files:子脚本文件(如 'subdir/SConscript')。
exports:导出变量到子脚本。
variant_dir:指定构建输出目录。
示例:
# SConstruct文件
env = Environment()
SConscript('src/SConscript', exports='env')
# src/SConscript文件
Import('env') # 导入导出的环境
env.Program('my_program', Glob('*.c'))
VariantDir(variant_dir, src_dir)
指定构建输出目录,保持源目录干净。
示例:
VariantDir('build', 'src')
SConscript('build/SConscript')
2.6 自定义工具与命令
SCons 允许自定义构建命令和工具。
Command(target, source, action)
执行自定义命令。
用法:运行任意命令(如脚本或工具)。
示例:
env.Command('output.txt', 'input.txt', 'cp $SOURCE $TARGET')
AddMethod(env, function, [name])
向环境添加自定义方法。
用法:扩展 SCons 功能。
示例:
def my_builder(env, target, source):
env.Command(target, source, 'echo Building $SOURCE > $TARGET')
env.AddMethod(my_builder, 'MyBuilder')
env.MyBuilder('out.txt', 'in.txt')
2.7 配置与检测
SCons 支持检测编译器、库和工具。
Configure(env, **kwargs)
创建配置上下文,用于检测环境。
用法:检查编译器、库或头文件是否存在。
示例:
conf = Configure(env)
if conf.CheckLib('m'):
print("Math library found")
env = conf.Finish()
CheckHeader(context, header, language)
检查头文件是否存在。
示例:
conf = Configure(env)
if conf.CheckHeader('math.h', language='C'):
print("math.h found")
env = conf.Finish()
2.8 构建控制
控制构建行为和流程。
Default(target)
指定默认构建目标。
用法:运行 SCons 时构建这些目标。
示例:
env.Program('my_program', 'main.c')
Default('my_program')
AlwaysBuild(target)
强制目标始终构建。
示例:
env.AlwaysBuild('my_program')
NoClean(target)
防止目标在 SCons -c 时被清理。
示例:
env.NoClean('my_program')
2.9 环境变量与标志
设置编译器和链接器选项。
env.Append(**kwargs)
向环境变量追加值(如标志、路径)。
示例:
env.Append(CCFLAGS='-g', CPPPATH=['include'])
env.Replace(**kwargs)
替换环境变量的值。
示例:
env.Replace(CC='clang')
env.Prepend(**kwargs)
在环境变量前添加值。
示例:
env.Prepend(CCFLAGS='-O3')
2.10 调试与信息输出
帮助调试构建过程。
env.Message(msg)
输出自定义消息。
示例:
env.Message("Building my_program...")
env.Progress(func)
显示构建进度。
示例:
env.Progress(lambda x: print(f"Processing {x}"))
- 工程示例
3.1 SConstruct文件
import os
# 描述:导入 Python 的 os 模块,提供文件和目录操作的功能。
# 意图:为路径操作、环境变量获取和目录检查等功能提供支持,例如构造路径或验证目录存在。
import sys
# 描述:导入 Python 的 sys 模块,用于操作 Python 运行时环境。
# 意图:允许修改模块搜索路径(sys.path),以便加载 RT-Thread 的工具模块。
import rtconfig
# 描述:导入 rtconfig 模块,包含 RT-Thread 项目的编译工具链和标志配置。
# 意图:提供编译器、链接器和标志的配置信息(如 rtconfig.CC、rtconfig.CFLAGS),用于设置构建环境。
if os.getenv('RTT_ROOT'):
# 描述:检查环境变量 RTT_ROOT 是否存在,使用 os.getenv() 获取其值。
# 意图:确定 RT-Thread 根目录的路径,优先使用用户设置的环境变量以提高灵活性。
RTT_ROOT = os.getenv('RTT_ROOT')
# 描述:将环境变量 RTT_ROOT 的值赋给变量 RTT_ROOT。
# 意图:记录 RT-Thread 根目录路径,确保后续路径构造使用正确的根目录。
else:
RTT_ROOT = os.path.normpath(os.getcwd() + '/../../..')
# 描述:计算 RT-Thread 根目录路径,通过 os.getcwd() 获取当前目录并向上回溯三级,再用 os.path.normpath 规范化路径。
# 意图:为没有设置 RTT_ROOT 环境变量的情况提供默认根目录路径,确保脚本在不同环境下可运行。
sys.path = sys.path + [os.path.join(RTT_ROOT, 'tools')]
# 描述:将 RT-Thread 的 tools 目录路径追加到 Python 的模块搜索路径 sys.path 中,使用 os.path.join 构造路径。
# 意图:确保 Python 能找到 RT-Thread 的工具模块(如 building.py),支持后续导入构建辅助函数。
try:
from building import *
# 描述:尝试从 tools 目录的 building 模块导入所有内容,包含 RT-Thread 的构建辅助函数。
# 意图:加载 PrepareBuilding 和 DoBuilding 等函数,为项目的编译和链接提供支持。
except:
print('Cannot find RT-Thread root directory, please check RTT_ROOT')
# 描述:如果导入 building 模块失败,打印错误信息,提示无法找到 RT-Thread 根目录。
# 意图:帮助用户调试问题,明确错误原因是 RTT_ROOT 路径不正确。
print(RTT_ROOT)
# 描述:打印当前 RTT_ROOT 变量的值。
# 意图:提供 RTT_ROOT 路径的实际值,方便用户检查路径是否正确。
exit(-1)
# 描述:调用 exit(-1) 退出程序,返回错误码 -1。
# 意图:终止构建过程,表明由于找不到 RT-Thread 根目录,脚本无法继续执行。
TARGET = 'rt-thread.' + rtconfig.TARGET_EXT
# 描述:构造目标文件名,拼接字符串 'rt-thread.' 和 rtconfig.TARGET_EXT(目标文件扩展名,如 'out')。
# 意图:定义最终生成的目标文件名称,例如 'rt-thread.out',用于后续编译和链接。
DefaultEnvironment(tools=[])
# 描述:创建 SCons 的默认环境,并通过 tools=[] 禁用所有默认工具。
# 意图:避免 SCons 加载默认工具链(如 gcc),为自定义工具链配置留出空间。
env = Environment(tools=['mingw'],
# 描述:创建 SCons 构建环境,指定 tools=['mingw'] 使用 MinGW 工具链。
# 意图:初始化一个自定义的编译环境,适配 Windows 上的 GCC 编译器或类似工具链。
AS=rtconfig.AS, ASFLAGS=rtconfig.AFLAGS,
# 描述:设置汇编器(AS)和汇编标志(ASFLAGS),从 rtconfig 模块获取相应值。
# 意图:配置汇编工具和标志,确保汇编代码按 RT-Thread 的要求编译。
CC=rtconfig.CC, CFLAGS=rtconfig.CFLAGS,
# 描述:设置 C 编译器(CC)和 C 编译标志(CFLAGS),从 rtconfig 模块获取值。
# 意图:定义 C 代码的编译工具和标志,确保与 RT-Thread 的配置一致。
AR=rtconfig.AR, ARFLAGS='-rc',
# 描述:设置归档工具(AR)为 rtconfig.AR,归档标志(ARFLAGS)固定为 '-rc'。
# 意图:配置静态库生成工具,确保生成 .a 文件时使用正确的归档参数。
CXX=rtconfig.CXX, CXXFLAGS=rtconfig.CXXFLAGS,
# 描述:设置 C++ 编译器(CXX)和 C++ 编译标志(CXXFLAGS),从 rtconfig 模块获取值。
# 意图:支持 C++ 代码的编译,适配 RT-Thread 项目中的 C++ 部分(如果有)。
LINK=rtconfig.LINK, LINKFLAGS=rtconfig.LFLAGS)
# 描述:设置链接器(LINK)和链接标志(LINKFLAGS),从 rtconfig 模块获取值。
# 意图:配置链接工具和参数,确保生成目标文件时使用正确的链接脚本和选项。
env.PrependENVPath('PATH', rtconfig.EXEC_PATH)
# 描述:将 rtconfig.EXEC_PATH(工具链的可执行文件路径)添加到环境变量 PATH 的开头。
# 意图:确保 SCons 能找到编译器、链接器等工具,优先使用 RT-Thread 指定的工具链路径。
if rtconfig.PLATFORM in ['iccarm']:
# 描述:检查 rtconfig.PLATFORM 是否为 'iccarm',判断是否使用 IAR 编译器。
# 意图:为 IAR 编译器平台提供特定的配置,适配其独特的编译和链接要求。
env.Replace(CCCOM=['$CC $CFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -o $TARGET $SOURCES'])
# 描述:替换 C 编译命令(CCCOM),使用 IAR 特定的命令模板,包含编译器、标志和输出选项。
# 意图:确保 IAR 编译器按照正确的参数编译 C 代码,生成目标文件。
env.Replace(ARFLAGS=[''])
# 描述:清空归档标志(ARFLAGS),将其设置为空列表。
# 意图:适配 IAR 编译器的归档工具,移除默认的 '-rc' 标志以避免冲突。
env.Replace(LINKCOM=env["LINKCOM"] + ' --map rt-thread.map')
# 描述:修改链接命令(LINKCOM),在原有命令后追加 '--map rt-thread.map' 参数。
# 意图:为 IAR 链接器生成映射文件 rt-thread.map,记录符号表和内存布局。
Export('RTT_ROOT')
# 描述:使用 SCons 的 Export 函数将 RTT_ROOT 变量导出到子脚本。
# 意图:允许子 SConscript 脚本通过 Import('RTT_ROOT') 访问 RT-Thread 根目录路径。
Export('rtconfig')
# 描述:使用 Export 函数将 rtconfig 模块导出到子脚本。
# 意图:使子脚本能够访问编译器和标志配置,确保一致的构建环境。
SDK_ROOT = os.path.abspath('./')
# 描述:使用 os.path.abspath 获取当前工作目录的绝对路径,存储在 SDK_ROOT 变量中。
# 意图:记录项目根目录的绝对路径,方便后续构造库或驱动的路径。
if os.path.exists(SDK_ROOT + '/libraries'):
# 描述:检查 SDK_ROOT/libraries 目录是否存在。
# 意图:确定 libraries 目录的位置,优先使用项目内的 libraries 路径。
libraries_path_prefix = SDK_ROOT + '/libraries'
# 描述:如果 libraries 目录存在,将其路径赋给 libraries_path_prefix。
# 意图:设置 libraries 目录的路径,用于查找 STM32 HAL 库和驱动。
else:
libraries_path_prefix = os.path.dirname(SDK_ROOT) + '/libraries'
# 描述:如果 libraries 目录不存在,构造父目录下的 libraries 路径并赋给 libraries_path_prefix。
# 意图:提供备用路径,确保即使 libraries 不在项目内也能找到库文件。
SDK_LIB = libraries_path_prefix
# 描述:将 libraries_path_prefix 的值赋给 SDK_LIB 变量。
# 意图:统一库路径的变量名,便于后续导出和使用。
Export('SDK_LIB')
# 描述:使用 Export 函数将 SDK_LIB 变量导出到子脚本。
# 意图:允许子 SConscript 脚本访问库路径,方便加载 STM32 库和驱动。
objs = PrepareBuilding(env, RTT_ROOT, has_libcpu=False)
# 描述:调用 PrepareBuilding 函数,准备构建环境,返回构建对象列表,传入 env、RTT_ROOT 和 has_libcpu=False 参数。
# 意图:初始化 RT-Thread 项目的编译环境,收集核心源文件和各级SConscript的构建对象,禁用 libcpu 相关代码。
stm32_library = 'STM32F4xx_HAL'
# 描述:定义变量 stm32_library,赋值为 'STM32F4xx_HAL',表示 STM32F4xx 硬件抽象层库。
# 意图:指定项目使用的 STM32 库名称,供后续路径构造和配置使用。
rtconfig.BSP_LIBRARY_TYPE = stm32_library
# 描述:将 stm32_library 的值赋给 rtconfig.BSP_LIBRARY_TYPE。
# 意图:记录板级支持包(BSP)使用的库类型,确保 RT-Thread 配置与 STM32 库一致。
objs.extend(SConscript(os.path.join(libraries_path_prefix, stm32_library, 'SConscript')))
# 描述:调用 STM32F4xx_HAL 库的 SConscript 脚本,获取其构建对象并追加到 objs 列表。
# 意图:将 STM32 HAL 库的编译结果(.o 文件)纳入构建过程,支持硬件抽象层功能。
objs.extend(SConscript(os.path.join(libraries_path_prefix, 'HAL_Drivers', 'SConscript')))
# 描述:调用 HAL_Drivers 目录的 SConscript 脚本,获取驱动相关的构建对象并追加到 objs 列表。
# 意图:将 STM32 驱动代码的编译结果纳入构建过程,提供硬件驱动支持。
DoBuilding(TARGET, objs)
# 描述:调用 DoBuilding 函数,传入目标文件名 TARGET 和构建对象列表 objs,执行最终编译和链接。
# 意图:生成最终的目标文件(如 rt-thread.elf),完成项目的构建过程。
3.2 SConstruct文件
import os
# 描述:导入 Python 的 os 模块,提供文件和目录操作功能。
# 意图:为后续的目录遍历和文件检查提供必要的工具,例如列出子目录或构造路径。
Import('remove_components')
# 描述:使用 SCons 的 Import 函数从父脚本导入 remove_components 变量。
# 意图:获取父脚本中定义的排除模块列表,以便动态控制哪些子模块不被编译。
from building import *
# 描述:从 building 模块导入所有内容,通常包含 RT-Thread 的构建辅助函数。
# 意图:为脚本提供 RT-Thread 特定的构建工具,如 PrepareBuilding 或 DoBuilding,尽管本脚本未直接使用。
objs = []
# 描述:初始化一个空的列表 objs,用于存储子模块的构建对象。
# 意图:创建一个容器,收集所有子模块的编译结果(如 .o 文件),供父脚本使用。
cwd = GetCurrentDir()
# 描述:调用 SCons 的 GetCurrentDir 函数,获取当前 SConscript 脚本所在目录的路径。
# 意图:记录当前工作目录,以便构造子目录路径,用于后续遍历和文件检查。
list = os.listdir(cwd)
# 描述:使用 os.listdir 函数获取当前目录下的所有文件和子目录名称列表。
# 意图:生成一个包含潜在子模块的列表,为后续遍历子目录提供基础数据。
for item in list:
# 描述:使用 for 循环遍历 list 中的每个条目,item 表示文件或目录的名称。
# 意图:逐一检查当前目录下的每个条目,识别哪些是需要编译的子模块。
if item in remove_components:
continue
# 描述:检查当前条目 item 是否在 remove_components 列表中,若是则跳过(continue)。
# 意图:排除不需要编译的模块,实现动态配置,允许根据项目需求禁用特定模块。
if os.path.isfile(os.path.join(cwd, item, 'SConscript')):
# 描述:使用 os.path.isfile 检查子目录中是否存在 SConscript 文件,路径由 os.path.join 构造。
# 意图:确认当前子目录是否是一个有效的模块(包含 SConscript 文件),以决定是否需要编译。
objs = objs + SConscript(os.path.join(item, 'SConscript'))
# 描述:调用子目录中的 SConscript 脚本,并将其返回的构建对象追加到 objs 列表。
# 意图:执行子模块的构建逻辑,收集其编译结果(如 .o 文件),汇总到 objs 用于最终链接。
Return('objs')
# 描述:使用 SCons 的 Return 函数将 objs 列表返回给调用该脚本的父脚本。
# 意图:将所有子模块的构建对象传递给父脚本,以便进行进一步的编译或链接(如生成可执行文件)。
3.3 自建SCons工程验证
3.3.1 SCons工程和C代码工程
1. 工程验证环境搭建
搭建Python环境
搭建GCC编译环境
搭建SCONS编译环境
2. 写个C语言demo
H文件

C文件

3. 添加SConstruct文件和SConscript文件
SConstruct文件
import os
from SCons.Script import ARGUMENTS, Environment
# 定义 MinGW 的编译器路径
# 用于后续构建中明确指定 gcc/g++ 所在的位置
MINGW_PATH = r'D:\tools\WinGcc\mingw64\bin'
# 创建一个构建环境,指定使用 MinGW 工具链
# 避免 SCons 默认选择 MSVC 编译器造成参数不兼容
env = Environment(
tools=['mingw'],
)
# 将 MinGW 路径添加到环境变量 PATH 中
# 确保 SCons 在执行 gcc/g++ 时能正确找到工具路径
env.PrependENVPath('PATH', MINGW_PATH)
# 手动设置编译器与链接器路径及可执行文件后缀
# 避免使用 SCons 自动探测的工具路径,确保调用的是我们指定的 MinGW 版本
env.Replace(
CC = os.path.join(MINGW_PATH, 'gcc.exe'), # 设置 C 编译器为 MinGW 的 gcc
CXX = os.path.join(MINGW_PATH, 'g++.exe'), # 设置 C++ 编译器为 MinGW 的 g++
LINK = os.path.join(MINGW_PATH, 'gcc.exe'), # 设置链接器为 gcc(适用于 C 项目)
PROGSUFFIX = '.exe' # 指定生成程序使用 .exe 后缀(Windows 下的标准格式)
)
# 获取构建模式参数,默认为 debug
# 允许通过命令行传参切换 debug 或 release 模式(如:SCons build=release)
variant = ARGUMENTS.get('build', 'debug')
# 设置 debug 模式下的编译和链接参数
# 开启调试信息,不进行优化,并显示所有警告
if variant == 'debug':
env.Append(CCFLAGS=['-g', '-O0', '-Wall'], LINKFLAGS=['-g'])
# 设置 release 模式下的参数
# 启用优化并保留警告提示,适合发布版本
else:
env.Append(CCFLAGS=['-O2', '-Wall'])
# 构建输出目录,按构建模式区分(如 build/debug)
# 实现不同构建模式的中间文件和输出文件隔离
out_dir = f'build/{variant}'
# 加载子构建脚本,返回中间编译产物(object 文件列表)
# 将构建任务分离到子目录中,保持结构清晰
objs = SConscript('project/SConscript',
exports='env', # 向子脚本传递构建环境变量
variant_dir=out_dir, # 指定输出目录
duplicate=0) # 不复制源文件,只生成构建结果
# 链接生成最终的可执行文件,命名为 output.exe
# 使用返回的 object 文件完成链接步骤,输出至对应目录
env.Program(f'{out_dir}/output.exe', objs)
SConscript
# 导入从主构建脚本(SConstruct)传进来的构建环境变量 env
# 保证子构建脚本与主构建脚本使用相同的编译器和参数设置
Import('env')
# 使用通配符获取当前目录下所有 .c 源文件,保存在 sources 变量中
# 自动收集所有 C 源文件,避免手动一个个列出,方便管理
sources = Glob('*.c')
# 使用传入的构建环境将所有源文件编译为 .o 对象文件,存入 objs
# 预编译阶段生成目标文件,供后续链接生成可执行文件使用
objs = env.Object(sources)
# 将编译生成的对象文件列表返回给调用者(SConstruct)
# 主构建脚本需要用这些对象文件来进行最终的链接操作
Return('objs')
- 编译
打开CMD 输入SCons命令

- 执行写的示例

3.3.2 附录测试demo
链接: https://pan.baidu.com/s/1znPwPG6EJ8d2H7BY_P0_dA?pwd=4uhd 提取码: 4uhd
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)