辅助脚本与库合并
本页介绍 fw-AW33N_BLE_SDK 仓库中 tools/ 目录下的辅助脚本体系:开发环境引导脚本 make_prompt.bat、静态库合并脚本 do_merge_libs.bat,以及随附的 Windows 工具集(make.exe、find.exe、merge-archives.exe 等)。这些脚本共同支撑了 SDK 在 Windows 环境下以 GNU Make 驱动的构建流程,以及将多个 .a 静态库归档合并为单一库的链接前处理环节。
Purpose and Scope
本页覆盖以下内容:
tools/make_prompt.bat:一键进入带工具链 PATH 的 cmd 开发环境;tools/utils/do_merge_libs.bat:把指定目录下的全部.a归档合并为一个输出归档;tools/utils/工具集:为 Windows 提供类 Unix 命令行工具,其中merge-archives.exe是库合并的实际执行者;- 库合并机制与
--no-rewrite参数的设计意图。
本页不覆盖 SDK 的芯片驱动、BLE 协议栈实现、Makefile 的具体构建规则与链接脚本(linker script)等内容,这些属于其他目录页的主题。本页聚焦于"脚本工具本身如何工作、为何这样设计"。
Overview
背景与动机
AW33N BLE SDK 的固件工程采用 GNU Make 作为构建驱动,源码树中包含大量 .a 静态库归档(如协议栈、驱动、DSP 库等),并经常需要把多个归档合并成一个整体库后再交给链接器。Windows 原生环境不提供 make、find、rm 等工具,也不提供类 Unix 的归档处理流程,因此 SDK 在 tools/ 下捆绑了一套可移植工具链与批处理脚本:
make_prompt.bat负责"环境引导"——把tools/utils加入PATH,并切换到 SDK 根目录,随后打开交互式cmd,让开发者在开箱即用的环境中执行make;do_merge_libs.bat负责"库合并"——枚举输入目录下的所有.a文件,调用merge-archives.exe以--no-rewrite模式合并输出,保证归档成员的原始名称与顺序语义被保留。
关键概念
| 概念 | 说明 |
|---|---|
.a 归档 | ar 格式的静态库归档文件,链接器按成员顺序扫描解析符号 |
--no-rewrite | 合并归档时保留原始成员名,不重写/重命名成员 |
| 延迟变量展开 | setlocal enabledelayedexpansion 配合 !VAR! 语法,使循环内累积的变量在运行时取值 |
| 可移植工具集 | 由 MSYS/MinGW 风格工具(如 make.exe、find.exe)与运行时 DLL(libiconv2.dll、libintl3.dll)构成 |
Architecture
下图展示辅助脚本与工具集在 SDK 构建流程中的位置与调用关系:
flowchart TD
subgraph sg_Boot["开发环境引导"]
Prompt["tools/make_prompt.bat"]
end
subgraph sg_Utils["tools/utils 工具集"]
MergeExe["merge-archives.exe"]
Make["make.exe"]
Find["find.exe"]
Other["rm / ls / true / uname / override-seg 等"]
end
subgraph sg_Merge["库合并脚本"]
DoMerge["tools/utils/do_merge_libs.bat"]
end
subgraph sg_Output["构建产物"]
LibA["输入目录 *.a 静态库"]
MergedLib["合并后的 .a 输出库"]
end
Prompt -->|"将 utils 目录加入 PATH"| sg_Utils
Make -->|"构建流程中调用"| DoMerge
DoMerge -->|"dir /b 枚举"| LibA
DoMerge -->|"%MERGE% --no-rewrite --output"| MergeExe
MergeExe --> MergedLib
架构说明:
make_prompt.bat是环境入口。它不直接参与编译,而是把tools/utils前置到PATH,使cmd中可以直接调用make、find、merge-archives等工具,随后cd ..回到 SDK 根目录并打开交互 shell。tools/utils是工具供给层。除 GNU 工具外,merge-archives.exe是库合并功能的真正执行者,fixbat.exe、override-seg.exe等则服务于构建链中的其他辅助需求(其内部实现未包含在已读脚本中,故本页不展开)。do_merge_libs.bat是合并流程的编排者。它把"枚举文件"与"执行合并"两个动作串联起来,输出供链接器使用的合并归档。
实现详解
make_prompt.bat:开发环境引导
tools/make_prompt.bat 全文只有 4 行,是整个 SDK 在 Windows 下构建体验的入口:
SET SCRIPT_PATH=%~dp0%
set PATH=%SCRIPT_PATH%\utils;%PATH%
cd ..
cmd
Source: make_prompt.bat
逐行解读:
SET SCRIPT_PATH=%~dp0%:%~dp0是批处理文件的驱动盘符+所在目录(以反斜杠结尾),例如C:\sdk\tools\。此处使用%闭合的写法(%~dp0%)在 Windows 批处理中实际等效于%~dp0,其目的是捕获脚本自身位置,从而不依赖"当前工作目录"这一易变状态——这是批处理脚本中常见的"自定位"技巧,保证无论从何处运行都能找到配套工具。set PATH=%SCRIPT_PATH%\utils;%PATH%:把tools\utils前置到PATH最前面。前置(而非追加)意味着若系统里已装有同名工具,也会优先使用 SDK 捆绑版本,避免版本差异导致的构建行为漂移。cd ..:从tools/上溯到 SDK 根目录(tools的上一级即仓库根),让后续make等命令天然在正确的工作目录下执行。cmd:启动一个继承上述环境变量的交互式 shell,开发者可直接键入make开始构建。
设计意图: SDK 构建强依赖 GNU 工具链,但 Windows 原生不提供。与其要求用户安装 MSYS2/Cygwin,不如把最小工具集随 SDK 分发,用一个脚本完成"环境装配",降低上手成本并保证工具版本可控。
tools/utils 工具集
tools/utils/ 目录捆绑了构建所需的可执行文件与运行时库(经 ListFiles 确认存在):
| 文件 | 作用(依据文件名与调用关系推断) |
|---|---|
make.exe | GNU make,SDK Makefile 的执行引擎 |
find.exe / ls.exe / rm.exe / mkdir_win.exe | 类 Unix 文件操作工具,供 Makefile 规则调用 |
true.exe / uname.exe | GNU 兼容工具,uname 常用于 Makefile 中的平台判断 |
merge-archives.exe | 归档合并工具,do_merge_libs.bat 的执行目标 |
override-seg.exe | 构建链中段(segment)覆盖相关工具 |
fixbat.exe | 批处理文件修正工具 |
libiconv2.dll / libintl3.dll | GNU iconv / gettext 运行时库,支撑上述 MSYS 系工具的运行 |
注:
fixbat.exe、override-seg.exe等工具为二进制程序,仓库中未包含其源码或文档,本页对它们仅作存在性说明,不推断其内部行为。
do_merge_libs.bat:库合并脚本
tools/utils/do_merge_libs.bat 是合并流程的核心编排脚本:
@echo off
setlocal enabledelayedexpansion
set INDIR=%1%
set MERGE=%2%
set AROUT=%3%
echo %INDIR%
echo %MERGE%
echo %AROUT%
set FILES=
for /f "tokens=*" %%i in ('dir /b %INDIR%\*.a') DO SET FILES=!FILES! %INDIR%\%%i
echo %FILES%
%MERGE% --no-rewrite --output %AROUT% %FILES%
Source: do_merge_libs.bat
关键机制:
- 参数约定:
%1=输入目录(INDIR)、%2=合并工具路径(MERGE)、%3=输出归档路径(AROUT)。参数化的设计让脚本可以被 Makefile 以不同目标反复调用,工具路径也可替换为其他兼容实现。 setlocal enabledelayedexpansion与!FILES!:这是本脚本最值得注意的细节。在for循环体内给变量赋值,若使用普通%FILES%语法,会在整条语句解析时就完成展开(此时 FILES 仍为空);必须启用延迟展开并用!FILES!才能在每次循环执行时取到当前值。这是批处理中"循环内累积字符串"的标准解法。for /f "tokens=*" %%i in ('dir /b %INDIR%\*.a'):对输入目录执行dir /b(仅文件名)并逐行迭代,tokens=*保证含空格的路径也能整体取到;每行把INDIR\文件名追加进FILES。%MERGE% --no-rewrite --output %AROUT% %FILES%:最终调用合并工具。--no-rewrite与--output是merge-archives.exe的 CLI 选项(调用形式即为其接口文档)。
merge-archives 与 --no-rewrite 的设计意图
merge-archives.exe 的选项通过 do_merge_libs.bat 第 17 行的调用约定得以确认:
%MERGE% --no-rewrite --output %AROUT% %FILES%
两个选项的意义:
--output <path>:指定合并结果的输出路径,即第三个参数AROUT。--no-rewrite:合并时不重写归档成员名。ar 归档按成员顺序保存对象文件,链接器对归档的解析是"顺序扫描 + 按需抽取";若合并工具重命名成员或重排成员顺序,可能改变链接器的符号解析结果,导致已解析符号被重新抽取、重复定义或未定义引用等问题。--no-rewrite保证合并后的归档在链接语义上等价于"原样拼接"多个归档,这是 BLE 固件链接稳定性(链接顺序敏感)的关键保障。
设计意图总结:库合并不是简单的"文件拼接",而是要在保留归档成员语义(名称、顺序、符号表)的前提下做物理合并。do_merge_libs.bat 把"枚举—组装—执行"拆成清晰三步,参数全部外置,既便于 Makefile 复用,也让合并行为(工具、输入、输出)对构建脚本完全透明。
核心流程
环境引导流程
sequenceDiagram
participant Dev as 开发者
participant Prompt as make_prompt.bat
participant Utils as tools/utils
participant Shell as cmd 交互环境
Dev->>Prompt: 双击 / 命令行运行
Prompt->>Prompt: SCRIPT_PATH=%~dp0%(脚本自定位)
Prompt->>Utils: PATH 前置 tools\utils
Prompt->>Prompt: cd .. 进入 SDK 根目录
Prompt->>Shell: 启动 cmd(继承新环境)
Shell-->>Dev: 可直接执行 make / merge 等命令
库合并流程
flowchart TD
Start([构建脚本调用 do_merge_libs.bat]) --> P1["读取参数<br/>INDIR / MERGE / AROUT"]
P1 --> P2["dir /b 枚举 INDIR 下全部 *.a"]
P2 --> P3{"存在 .a 文件?"}
P3 -->|"否"| E1["FILES 为空,仅生成空归档"]
P3 -->|"是"| P4["延迟展开拼接 FILES 列表"]
P4 --> P5["执行 MERGE --no-rewrite --output AROUT"]
P5 --> E2["得到合并后的 .a 输出库"]
E1 --> End([结束])
E2 --> End
两个流程说明:环境引导是"一次性装配"——脚本只负责把工具暴露出来并回到正确目录,后续命令由开发者在 shell 中自由执行;库合并是"幂等转换"——输入是目录快照,输出是单一归档,脚本自身不保留状态,任何时刻重跑都会得到一致的合并结果(前提是输入目录未变化)。
使用示例
启动开发环境
双击运行 tools/make_prompt.bat 后,在出现的 cmd 中即可直接使用捆绑工具:
REM 运行 make_prompt.bat 后,PATH 已包含 tools\utils
make -j8
find . -name "*.a"
Source: make_prompt.bat
调用库合并脚本
典型调用(三个位置参数:输入目录、合并工具、输出文件):
call do_merge_libs.bat build\libs tools\utils\merge-archives.exe build\libs\app_merged.a
脚本内部实际执行的命令等价于:
tools\utils\merge-archives.exe --no-rewrite --output build\libs\app_merged.a build\libs\lib_a.a build\libs\lib_b.a ...
Source: do_merge_libs.bat
配置选项
do_merge_libs.bat 位置参数
| 参数 | 类型 | 必需 | 说明 |
|---|---|---|---|
%1(INDIR) | 目录路径 | 是 | 存放待合并 .a 文件的输入目录 |
%2(MERGE) | 可执行文件路径 | 是 | 合并工具,通常为 tools\utils\merge-archives.exe |
%3(AROUT) | 文件路径 | 是 | 合并输出归档的完整路径 |
merge-archives.exe 命令行选项
| 选项 | 说明 |
|---|---|
--no-rewrite | 合并时保留归档成员的原始名称,不重写成员(保持链接语义) |
--output <path> | 指定输出归档路径 |
选项名称与语义来自 do_merge_libs.bat 的实际调用;该工具为二进制程序,仓库内无更多文档。
make_prompt.bat 环境变量
| 变量 | 说明 |
|---|---|
PATH | 前置追加 tools\utils,使捆绑工具优先生效 |
SCRIPT_PATH | 脚本自身所在目录(%~dp0 展开),用于定位 utils 子目录 |
失败模式与边界情况
输入目录为空或无 .a 文件
dir /b %INDIR%\*.a 匹配不到任何文件时,FILES 保持为空,合并命令退化为 merge-archives.exe --no-rewrite --output AROUT(无输入成员)。此时生成的归档为空归档,链接时不会提供任何符号。Makefile 侧应在调用前确认输入目录产物已生成完毕,否则会出现"合并成功但链接缺符号"的隐性失败。
路径含空格
for /f "tokens=*" 保证枚举到的文件名整体赋给 %%i,且每个文件都以 INDIR\%%i 形式带引号加入 FILES?——注意:脚本并未显式加引号。若 SDK 仓库路径或输入目录路径含空格,%MERGE% --no-rewrite --output %AROUT% %FILES% 这一行的参数切分可能出错。因此建议将 SDK 放置在无空格的路径(如 C:\sdk\)下运行,这是批处理脚本常见的隐性约束。
延迟展开陷阱
若有人把 !FILES! 改成 %FILES%,循环内赋值将永不生效(整块在解析期一次性展开),最终 FILES 为空——这是本脚本最容易在维护时被改坏的点。保留 setlocal enabledelayedexpansion 与 !FILES! 的配对是正确性的前提。
并发与一致性
do_merge_libs.bat 读取输入目录快照并生成单一输出文件,脚本本身无并发保护。若多个 make 目标并行(make -j)同时对同一 INDIR 执行合并、写入同一 AROUT,将产生竞争写。实践中应由 Makefile 通过目标依赖关系串行化合并步骤,确保"先完成全部子库构建,再执行合并"。
setlocal 作用域
脚本使用 setlocal 而未显式 endlocal,批处理结束时环境变量自动恢复,不会污染调用方 shell;但这也意味着脚本内部设置的 FILES 等变量无法在调用方复用,属于"一次性计算"语义。
性能与运维说明
- 合并操作为纯文件级拷贝/归档重组,耗时与归档总大小线性相关,通常远小于编译时间;无需额外优化。
merge-archives.exe为 Windows 本地可执行文件,不依赖 MSYS2/Cygwin 运行时(仅依赖同目录的libiconv2.dll/libintl3.dll),部署即拷即用。make_prompt.bat每次运行都重建环境,无缓存、无状态,适合反复进入开发环境。
扩展点
- 替换合并工具:
do_merge_libs.bat的%2参数接受任意兼容--no-rewrite/--output接口的工具,可平滑替换为其他归档合并实现。 - 扩充工具集:在
tools/utils中追加新的可执行文件即可被make_prompt.bat暴露到PATH,无需修改脚本。 - 多目录合并:当前脚本仅支持单一输入目录;如需合并多个目录,可在 Makefile 层多次调用或先拷贝汇总到同一目录。
测试情况
仓库中未发现针对这两个批处理脚本的自动化测试(脚本为构建辅助性质,随构建流程隐式验证:合并结果能通过链接即视为通过)。若需回归验证,可构造含多个已知符号的 .a 归档,合并后用 nm/链接器检查符号完整性。
Related Links
- tools/make_prompt.bat:开发环境引导脚本
- tools/utils/do_merge_libs.bat:库合并脚本
- tools/utils/:Windows 工具集目录(含 merge-archives.exe、make.exe 等)
- 构建系统与 Makefile 规则、链接脚本等主题请参见对应目录页(本页不展开)