在 CMake 项目中静态链接 libghostty-vt:Ghostty 官方示例 c-vt-cmake-static 全解
Ghostty 除了作为终端模拟器本体,还对外提供了名为 libghostty-vt 的 C 语言虚拟终端库,负责解析转义序列、维护终端状态、编码输入事件等核心能力。本仓库的 example/c-vt-cmake-static 示例演示了如何用 CMake 的 FetchContent 机制以静态库形式集成该库:创建一个 80x24 的终端、向其中写入 VT 转义序列,再借助 Formatter 把屏幕内容输出为纯文本。读完本文,你可以完整复现该示例的构建流程,理解静态链接与动态链接在目标名、链接依赖(如 SIMD、C++ 运行时)上的差异,并能把同一套集成方式移植到自己的 CMake 工程中。
示例定位:与共享库示例的对照
example/c-vt-cmake-static/README.md 对该示例的定位一句话概括为:
Demonstrates consuming libghostty-vt as a static library from a CMake project using
FetchContent. Creates a terminal, writes VT sequences into it, and formats the screen contents as plain text.
即:使用 FetchContent 从 CMake 工程以静态库形式消费 libghostty-vt。仓库中与之并列的还有共享库版本的 example/c-vt-cmake/README.md,两者业务代码完全一致,唯一区别在于链接的目标名——共享库示例链接 ghostty-vt,而本静态示例链接 ghostty-vt-static。这个"目标名只差一个后缀"的差异背后,是 CMake 封装层对两条产物路径的差异化处理(编译宏、平台链接库),后文会展开。
libghostty-vt 的能力边界可以从头文件 include/ghostty/vt.h 的 Doxygen 说明中得到确认:它包含解析转义序列、维护终端状态(样式、光标、屏幕、回滚缓冲)、编码输入事件等逻辑,支持回滚缓冲、行换行、resize 时重排等特性;API 分组涵盖 Terminal、Render State、Formatter、Snapshot、Search、OSC/SGR Parser、Paste、Unicode 工具、Focus/Key/Mouse 编码等。需要特别注意,该头文件同时声明 API 尚不稳定、仍在开发中,生产环境使用前需自行承担变更风险。
构建与运行步骤
原 README 给出的构建流程只有三步,前提是本机已安装 zig(CMake 封装层会在配置阶段执行 find_program(zig REQUIRED),Zig 必须在 PATH 中):
cd example/c-vt-cmake-static
cmake -B build
cmake --build build
./build/c_vt_cmake_static
执行后程序会在标准输出打印一段纯文本(约 3 行带样式的字符串被还原为无格式文本),即 main.c 中写入终端的三行内容的 Plain 格式渲染结果。
其中 cmake --build build 阶段实际发生的事情是:顶层 CMakeLists.txt 通过 add_custom_command 触发 zig build -Demit-lib-vt,一次性产出共享库、静态库、头文件和 pkg-config 文件到 zig-out/ 目录,CMake 随后把这两个产物注册为 IMPORTED 目标供下游链接。也就是说,CMake 只是"包装器",真正的编译由 Zig 构建系统完成——这也是该仓库 CMake 封装头注释明确交代的架构("delegates to zig build -Demit-lib-vt")。
使用本地代码库代替远端拉取
如果不想从远端仓库克隆整个 Ghostty(例如要调试本地修改),README 提供了第二条命令:
cmake -B build -DFETCHCONTENT_SOURCE_DIR_GHOSTTY=../..
cmake --build build
FETCHCONTENT_SOURCE_DIR_<NAME> 是 FetchContent 的标准覆盖机制:当 <NAME> 与 FetchContent_Declare 声明的名称(此处为 ghostty,不区分大小写)一致时,FetchContent_MakeAvailable 会直接使用你指定的本地目录,跳过 clone/fetch。示例中 ../.. 正是相对于 example/c-vt-cmake-static 的仓库根目录。顶层 CMakeLists.txt 头注释中也给出了同样的写法(cmake -B build -DFETCHCONTENT_SOURCE_DIR_GHOSTTY=/path/to/ghostty),两者等价,绝对路径更通用。
解读示例工程的 CMakeLists.txt
示例的 example/c-vt-cmake-static/CMakeLists.txt 全文仅 13 行,但每一行都值得拆解:
cmake_minimum_required(VERSION 3.19)
project(c-vt-cmake LANGUAGES C)
include(FetchContent)
FetchContent_Declare(ghostty
GIT_REPOSITORY https://github.com/ghostty-org/ghostty.git
GIT_TAG main
)
set(GHOSTTY_ZIG_BUILD_FLAGS "-Dsimd=false" CACHE STRING "" FORCE)
FetchContent_MakeAvailable(ghostty)
add_executable(c_vt_cmake_static src/main.c)
target_link_libraries(c_vt_cmake_static PRIVATE ghostty-vt-static)
逐点说明:
-
FetchContent_Declare锁定GIT_TAG main:每次配置阶段都会拉取 Ghostty 主分支的最新代码构建。由于 libghostty-vt API 尚未稳定,跟踪 main 分支意味着 API 可能随时变动;生产集成时通常应改钉具体 tag/commit。 -
set(GHOSTTY_ZIG_BUILD_FLAGS "-Dsimd=false" CACHE STRING "" FORCE)是本示例区别于共享库示例的关键一行。GHOSTTY_ZIG_BUILD_FLAGS是顶层 CMake 工程定义的 cache 变量(见 CMakeLists.txt),原样透传给zig build。-Dsimd=false会关闭 SIMD 路径,从而移除全部 C++ 运行时依赖(highway、simdutf 以及 C++ 标准库)。为什么静态示例要这么做?答案在顶层 CMakeLists.txt 对静态目标的注释中:On Linux and macOS, the static library is a fat archive that bundles the vendored SIMD dependencies (highway, simdutf). Consumers only need to link libc. On Windows, the SIMD dependencies are not bundled and must be linked separately. Building with
-Dsimd=falseremoves all runtime dependencies.从源码结构看:在 Linux/macOS 上,
libghostty-vt.a是一个把 SIMD 依赖打进去的 fat archive,消费方只需链接libc;但在 Windows 上 SIMD 依赖不被打包,消费方必须自行补链。示例选择在配置期强制-Dsimd=false,可以跨平台统一做到"零额外运行时依赖",代价是放弃 SIMD 加速。若你只在 Linux/macOS 使用静态库且希望保留 SIMD,删掉这一行即可(此时仍需确认 highway/simdutf 已随 fat archive 打包)。 -
FetchContent_MakeAvailable(ghostty)会执行被拉取工程的顶层CMakeLists.txt,从而得到两个 IMPORTED 全局目标:ghostty-vt(共享)与ghostty-vt-static(静态)。 -
target_link_libraries(... PRIVATE ghostty-vt-static)把静态目标接给可执行文件。链接ghostty-vt-static时会自动获得两样接口属性:头文件搜索路径指向zig-out/include,以及编译宏GHOSTTY_STATIC(见下文)。
PRIVATE 在此处意味着不向依赖本库的其他目标导出接口;由于这是最终可执行文件,用 PRIVATE 是标准写法。
静态目标的接口属性:GHOSTTY_STATIC 宏与 Windows 链接库
顶层 CMakeLists.txt 对 ghostty-vt-static 目标设置了三个关键属性:
add_library(ghostty-vt-static STATIC IMPORTED GLOBAL)
set_target_properties(ghostty-vt-static PROPERTIES
IMPORTED_LOCATION "${GHOSTTY_VT_STATIC_LIBRARY}" # Linux/macOS: zig-out/lib/libghostty-vt.a
INTERFACE_INCLUDE_DIRECTORIES "${ZIG_OUT_DIR}/include"
INTERFACE_COMPILE_DEFINITIONS "GHOSTTY_STATIC"
)
if(WIN32)
set_target_properties(ghostty-vt-static PROPERTIES
INTERFACE_LINK_LIBRARIES "ntdll;kernel32"
)
endif()
GHOSTTY_STATIC编译宏:INTERFACE_COMPILE_DEFINITIONS会自动注入到每个链接该目标的编译单元中。从源码结构看,它是 C ABI 头文件中用于区分静态/动态消费场景的条件编译开关(例如导出符号的可见性修饰),消费方不需要手动定义,链接即生效。- Windows 专属的
ntdll;kernel32:注释解释了原因——Windows 上 Zig 标准库使用了 NT API 函数(NtClose、NtCreateSection等)和 kernel32 函数,静态链接时这些系统库必须由消费方补齐;而共享库示例不需要这一步,因为 DLL 自身已声明这些依赖。 - 静态产物命名:Linux/macOS 上是
libghostty-vt.a,Windows 上特意命名为ghostty-vt-static.lib,以避开与 DLL 导入库ghostty-vt.lib的同名冲突(CMakeLists.txt 注释)。
此外,构建类型的映射也值得注意:CMake 的 CMAKE_BUILD_TYPE 为 Release/MinSizeRel/RelWithDebInfo 时,封装层会自动追加 -Doptimize=ReleaseFast 传给 zig build(CMakeLists.txt);未指定 build type 时不加优化参数,走 Debug 语义。
示例程序 main.c 全流程拆解
example/c-vt-cmake-static/src/main.c 完整展示了 libghostty-vt 最核心的"写入—格式化"调用链:
#include <ghostty/vt.h>
int main() {
// 1. 创建 80x24 终端
GhosttyTerminal terminal;
GhosttyResult result = ghostty_terminal_new(NULL, &terminal, 80, 24);
assert(result == GHOSTTY_SUCCESS);
// 2. 写入 VT 转义序列(粗体/下划线/前景色 + CRLF)
const char *commands[] = {
"Hello from a \033[1mCMake\033[0m-built program (static)!\r\n",
"Line 2: \033[4munderlined\033[0m text\r\n",
"Line 3: \033[31mred\033[0m \033[32mgreen\033[0m \033[34mblue\033[0m\r\n",
};
for (size_t i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) {
ghostty_terminal_vt_write(terminal, (const uint8_t *)commands[i],
strlen(commands[i]));
}
// 3. 创建 Formatter,输出纯文本、自动裁剪行尾
GhosttyFormatterTerminalOptions fmt_opts =
GHOSTTY_INIT_SIZED(GhosttyFormatterTerminalOptions);
fmt_opts.emit = GHOSTTY_FORMATTER_FORMAT_PLAIN;
fmt_opts.trim = true;
GhosttyFormatter formatter;
result = ghostty_formatter_terminal_new(NULL, &formatter, terminal, fmt_opts);
assert(result == GHOSTTY_SUCCESS);
// 4. 分配缓冲区并格式化整块屏幕
uint8_t *buf = NULL;
size_t len = 0;
result = ghostty_formatter_format_alloc(formatter, NULL, &buf, &len);
assert(result == GHOSTTY_SUCCESS);
printf("Plain text (%zu bytes):\n", len);
fwrite(buf, 1, len, stdout);
printf("\n");
// 5. 按创建顺序释放
ghostty_free(NULL, buf, len);
ghostty_formatter_free(formatter);
ghostty_terminal_free(terminal);
return 0;
}
各环节的要点:
ghostty_terminal_new(NULL, &terminal, 80, 24):第一个参数是 allocator(传NULL使用默认分配器),后两个参数是列宽、行高。这对应 include/ghostty/vt.h 中 Terminal 与 Memory Management 两组的 API。ghostty_terminal_vt_write:把字节流(这里是含 CSI 序列\033[1m、\033[4m、\033[31m等的文本)送入解析器。写入后终端内部即完成了转义序列解析与屏幕状态更新——这正是"静态库内嵌一个完整 VT 引擎"的含义:无需 pty、无需真实终端环境。GHOSTTY_INIT_SIZED(...)与fmt_opts.emit = GHOSTTY_FORMATTER_FORMAT_PLAIN:Formatter 支持把屏幕内容输出为纯文本、VT 序列或 HTML(见头文件第 33 行的分组说明),此处选择 Plain;trim = true裁掉行尾空白。- 内存约定:
ghostty_formatter_format_alloc分配的缓冲区必须用对应的ghostty_free释放,不能直接free();随后按"后进先出"依次释放 formatter 与 terminal。这种"分配函数 + 配套释放函数"的配对是 libghostty-vt 内存管理组的通用约定。
该示例与 example/c-vt-formatter/README.md 的 Formatter 示例在 API 层面同源,但本示例额外验证了"从 CMake 静态链接进来的库在 C 侧行为一致"这一集成命题。
另一条集成路线:find_package 与交叉编译
FetchContent 只适合"构建时集成"。顶层 CMakeLists.txt 头注释还给出了第二条路线:安装到 prefix 后用 find_package(ghostty-vt REQUIRED),消费命名空间目标:
find_package(ghostty-vt REQUIRED)
target_link_libraries(myapp PRIVATE ghostty-vt::ghostty-vt) # shared
target_link_libraries(myapp PRIVATE ghostty-vt::ghostty-vt-static) # static
该路线的配置文件由 dist/cmake/ghostty-vt-config.cmake.in 生成(install 时会写入 <prefix>/lib/cmake/ghostty-vt/)。其中关于静态目标的注释与 CMake 封装侧的表述略有差异:config 文件写明消费方需自行链接传递依赖——"libc、libc++(Linux 上为 libstdc++)、highway、simdutf;使用 -Dsimd=false 构建可移除 C++ / highway / simdutf 依赖"。这与本示例选择 -Dsimd=false 的做法相互印证:静态集成时最省心的依赖面配置就是关闭 SIMD。
若需要为非本机目标构建静态库(交叉编译),封装层提供了 ghostty_vt_add_target() 函数(CMakeLists.txt),它会自动处理 zig 发现、build type 到优化级别的映射、输出路径约定,并生成 ghostty-vt-static-<NAME> / ghostty-vt-<NAME> 两个目标:
FetchContent_MakeAvailable(ghostty)
ghostty_vt_add_target(NAME linux-amd64 ZIG_TARGET x86_64-linux-gnu
ZIG_FLAGS -Dsimd=false)
target_link_libraries(myapp PRIVATE ghostty-vt-static-linux-amd64) # static
target_link_libraries(myapp PRIVATE ghostty-vt-linux-amd64) # shared
该仓库另有 example/c-vt-cmake-cross/README.md 专门演示交叉编译场景,可作为本文 FetchContent 方式的进阶参考。
小结:适用前提与集成决策
把本示例的结论压缩成一份可执行的集成决策表:
| 决策点 | 说明 | 依据 |
|---|---|---|
| 构建前提 | 构建机 PATH 中必须存在 zig,且版本满足 Ghostty 官方构建文档的要求 |
CMakeLists.txt 中 find_program(zig REQUIRED)(实际路径为 CMakeLists.txt#L94) |
| 静态 vs 共享 | 静态链接 ghostty-vt-static,共享链接 ghostty-vt;静态目标自动带 GHOSTTY_STATIC 宏 |
顶层 CMakeLists.txt 静态/共享目标定义 |
| 平台依赖 | Linux/macOS 静态库已打包 SIMD 依赖,仅需 libc;Windows 需补链 ntdll;kernel32,SIMD 依赖不打包 |
顶层 CMakeLists.txt 注释 |
| 依赖面收敛 | 追加 GHOSTTY_ZIG_BUILD_FLAGS="-Dsimd=false" 可移除全部 C++/SIMD 运行时依赖,代价是性能路径回退标量实现 |
本示例 CMakeLists.txt 与 config 模板注释 |
| 版本锁定 | 示例用 GIT_TAG main 跟踪主分支,API 未稳定,生产集成应钉住具体版本;本地调试用 FETCHCONTENT_SOURCE_DIR_GHOSTTY 覆盖 |
example/c-vt-cmake-static/README.md |
需要再次强调的两个适用前提:其一,libghostty-vt 当前处于 work-in-progress 状态,头文件明确提示"API 不稳定,预期会有破坏性变更";其二,CMake 封装层本身只是一个转发器,所有编译工作都委托给 zig build -Demit-lib-vt,因此集成方必须同时维护好 Zig 工具链。满足这两点的前提下,example/c-vt-cmake-static 提供的 13 行 CMake 与 47 行 C 代码,就是一个可直接复制到自有工程的最小可运行骨架。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00