第 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 延伸阅读