第 39 章 / 共 51 章
卷五 · 扩展、运维与实战
自定义扩展:三件套接入任何 C 库
sqlite 与 xtp2 之外,你自己的库也能以同样的方式编入——注册表设计成"加一个条目即可",且每一道输入都有校验。
本章导读
站位:面向"想把手头的 C 库带进 XServer"的开发者。产出物是三件套 + 一条注册——照着 sqlite/xtp2 的现成样例做即可,本章讲每一步为什么。
39.1 引入
扩展机制的设计目标一句话:新增库 = 声明,不是代码。宿主代码里没有任何 per-library 分支(if sqlite… else if xtp…)——注册表在构建期生成符号聚合头 xs_build_extensions.h,运行期统一经 XS_TccAddExtensions() 注入 TCC。这意味着接入新库不需要改宿主源码,也意味着扩展的边界完全由声明文件决定。
39.2 三件套
extensions.json 真实条目(仓库原文)实机验证出处:xserver/tools/extensions.json
{
"mylib": {
"macro": "XS_USE_MYLIB", /* ① 编译宏:脚本侧可见性开关 */
"headers": {"mylib.h": "lib/mylib.h"}, /* ② 头文件 → VFS 路径映射 */
"sources": [{"path": "lib/mylib.c",
"flags": ["-O2"]}], 编入宿主的源(含编译参数)*/
"symbols": "src/script/import_mylib.inc", /* ③ 符号表 */
"symbol_macro": "XS_MYLIB_SYMBOL"
}
}
三件套的关系:源编进宿主(进程里有了实现)、头进 VFS(脚本能 include 到声明)、符号表注入 TCC(脚本的调用按地址解析到实现)。宏 XS_USE_MYLIB 会在嵌套编译器里预定义——脚本可以 #ifdef 探测扩展可用性。单头库(stb 风格)用 defines 字段带实现开关(xtp2 条目就是 XTP2_IMPLEMENTATION 的范例)。
39.3 注册中心校验
注册中心(xs_extensions.py)对每条输入做防御性检查,注册错了构建期就爆:
| 检查 | 防的祸 |
|---|---|
| source_path 必须在项目内且存在 | 路径逃逸(引用仓库外的意外文件) |
| feature 宏全局去重 | 两个扩展抢同一个宏名 |
| VFS 头名禁 .. 且跨扩展查重 | 头文件互相覆盖 / 路径穿透 |
| 字段白名单 | 拼错的字段被静默忽略 |
| 宏名/库名格式校验 | 生成非法代码 |
这套校验有专门的单测看护(test_extensions.py 的 10 个用例之一就是"加一个 demo 条目即生效,零代码分支")。
39.4 两级测试
- 计划级(test_extensions.py):注册表解析、构建计划、符号表与头的联动、发布失败保护——纯逻辑,秒级
- 运行级(test_extensions_runtime.py):真实构建的 XServer 里跑内嵌 C 探针——真实调用扩展 API、嵌套 TCC 里也能用扩展、反向验证"未选扩展时头与符号确实不可用"(编译失败 + 报错点名)
给你的库写扩展时,两级都补上:计划级保证注册不烂,运行级保证"编进去了真的能用、没编进去真的没有"。
39.5 契约
- 声明线:新库三件套 + 一条 json,宿主零改动。
- 一致线:VFS 头、符号表、宿主源三者必须同版本——符号表列了头里没有的函数就是定时炸弹。
- 测试线:两级测试是扩展的验收定义。
常见错误
符号表与头不同步
- 症状
- 脚本链接失败(有头没符号)或运行期跳到野地址(有符号头过时)。
- 原因
- 库升级后只改了头没重生成符号表(或反之)。把符号表生成做成脚本、与头一起变更。
忘了符号宏包裹
- 症状
- 符号表 .inc 在非扩展构建下编译宿主失败。
- 原因
- import 表应被 symbol_macro 包裹(对照 import_sqlite.inc 的 XS_SQLITE_SYMBOL 写法)——扩展符号只在选中的变体里编进宿主。
编程练习
基础注册一个玩具库
写 add(int,int) 的单头库,按三件套注册并用 --list-extensions 确认。
验收:build.bat mylib 成功;脚本调用 add 返回正确。
进阶可用性探测
写一段脚本用 #ifdef XS_USE_MYLIB 提供两个实现(有库走库、无库走内置),两种变体都编译通过。
验收:default 与 mylib 变体行为一致(来源不同)。
挑战补齐两级测试
为你的库补计划级 + 运行级测试,含"未选时不可用"的反向断言。
验收:正反断言都过;故意改坏注册表能被计划级测试抓住。
小结
| 要点 | 结论 |
|---|---|
| 机制 | 新增库 = 三件套 + json 条目,宿主零改动 |
| 三件套 | 源(编入)+ 头(进 VFS)+ 符号表(注入 TCC) |
| 校验 | 路径逃逸 / 宏冲突 / 头覆盖 / 字段白名单 |
| 测试 | 计划级 + 运行级(含反向不可用断言) |
本章涉及的 API 参考
xrt 延伸阅读
- xrt 参考 · core — 扩展源编入宿主时共享的基础类型约定