首页
/ kitty 源码构建完全指南:dev.sh 构建系统、依赖管理、调试构建与打包实践

kitty 源码构建完全指南:dev.sh 构建系统、依赖管理、调试构建与打包实践

2026-09-05 23:40:02作者:柯茵沙

本文基于 kitty 官方文档 docs/build.rst 编写,系统讲解从源码构建 kitty 终端模拟器的完整流程:通过 ./dev.sh build 一键完成依赖下载与编译、理解构建系统背后的工作原理、掌握 debug/sanitize 构建参数的用法、了解 Linux/macOS 系统级依赖清单,以及面向发行版打包者的 linux-package 打包方案与交叉编译流程。读完本文,你将能够独立完成 kitty 的源码构建、长期维护构建环境,并为其制作 Linux/macOS 软件包。

从源码运行:一条命令完成构建

kitty 被设计为可以直接从源码目录运行(designed to run from source),目的是方便二次开发与修改。在准备一个 C 编译器和 Go 编译器(Linux 上还需要 X11 开发库)之后,官方构建流程只有两条命令:

git clone https://github.com/kovidgoyal/kovidgoyal/kitty.git && cd kitty
./dev.sh build

构建完成后,直接运行 kitty/launcher/kitty 即可启动 kitty。后续如果修改了 kitty 的代码,只需重新执行 ./dev.sh build 即可带上你的改动重新构建。

提示:如果你只是想体验 kitty 的最新改动而非参与开发,无需从源码构建,安装官方 nightly 构建版即可。

构建系统剖析:dev.sh 背后的自动化

"一条命令魔法般地构建完成"并非黑盒,仓库中的构建脚本链条清晰可查:

  1. dev.sh 只有一行实质代码:exec go run bypy/devenv.go "$@"。即 ./dev.sh 的所有子命令都交给 Go 编写的 bypy/devenv.go 处理。

  2. bypy/devenv.gobuild 子命令(见 build() 函数,L389-L410)做了四件事:

    • 如果 dependencies 目录不存在,先自动调用 dependencies() 下载依赖——这就是"魔法"的来源:./dev.sh build 会自动补齐依赖,无需单独执行 ./dev.sh deps;
    • 设置 DEVELOP_ROOT 环境变量,并把预编译依赖自带的 slangc(shader 编译器)路径写入 SLANGC;
    • PKG_CONFIG_PATH 指向前者,使 pkg-config 优先找到预编译的依赖包;
    • 最后用预编译的 Python 执行 python setup.py develop <你的参数>,即真正进入 setup.py 的编译流程,成功后打印 Build successful. Run kitty as: kitty/launcher/kitty
  3. 依赖下载机制:dependencies() 函数(L251-L356)从 CI 工作流脚本 .github/workflows/ci.py 中正则提取 BUNDLE_URL(当前为 https://download.calibre-ebook.com/ci/kitty/{platform}-64.tar.xz),按平台(linux/macos)与架构(amd64/arm64)下载预编译依赖包,解压到 dependencies/<os>-<arch>/ 目录;下载采用 ETag 缓存(cached_download(),L160-L200),未变更时直接复用本地文件。此外还会下载 Nerd Font 的 SymbolsNerdFontMono-Regular.ttf 放入 fonts/ 目录,这正是文档"构建时依赖"一节中提到的字体文件。

  4. 为什么能"从源码运行":kitty 的 C 扩展 kitty/data-types.c(L1007-L1010)在编译期通过 -DDEVELOP_ROOT="..." 宏把源码目录绝对路径嵌入二进制(该值由 bypy/devenv.go 在 L395 注入)。这解释了官方文档中的一个重要注意事项:构建产物默认假设源码目录不变——如果你移动或重命名了构建目录,需要执行 make clean && ./dev.sh build 重新构建。

  5. 运行时加载源码:kitty/constants.py L44-L47 会读取 fast_data_types.DEVELOP_ROOT,非空时优先使用该路径下的 slangc 与源码资源——这是"从源码运行"的另一环。

长期从源码运行的注意事项

官方文档特别列出了长期以源码方式运行 kitty 时需要留意的三点,均可在源码中得到印证:

  • 定期刷新依赖:执行 ./dev.sh deps 重新下载依赖。从 bypy/devenv.godependencies() 实现看,该命令每次会 os.RemoveAll 清空 dependencies/<os>-<arch>/ 目录再重新解压,并删除包内自带的 libfontconfig.so 以便使用发行版自己的 fontconfig 配置目录(L329-L331);
  • 目录不可移动:如上所述,构建产物内嵌了首次执行 ./dev.sh build 时的目录路径,移动/重命名目录后需 make clean && ./dev.sh build;
  • 加入 PATH:建议在 PATH 中的某个目录为 kittykitten 二进制创建符号链接,方便日常启动。

macOS 专属:kitty.app

在 macOS 上还可以用 kitty/launcher/kitty.app 以应用形式运行 kitty。但要注意这是未经签名的 kitty.app,部分功能(如系统通知)会被 Apple 的签名策略阻止。若确需这些功能,可用自签名证书对构建出的 kitty.app 进行 codesign 签名后使用。

调试构建:debug 与 sanitizer

文档提供的三种构建变体如下:

# 带调试符号构建
./dev.sh build --debug

# 带 ASan/UBSan  sanitizer 与调试符号构建
./dev.sh build --debug --sanitize

# 查看构建脚本支持的全部选项
./dev.sh build -h

这些参数最终透传给 setup.py develop。查看 setup.pyoption_parser()(L2244-L2381),与日常构建最相关的选项包括:

选项 默认值 作用
--debug 以调试符号构建扩展模块
--sanitize 开启 AddressSanitizer + UndefinedBehaviorSanitizer 以检测内存访问错误与未定义行为(性能开销大)
-v/--verbose 0 输出更详细构建日志
--full 全量重建,忽略增量编译缓存
--profile 添加性能剖析所需的编译选项
--prefix ./linux-package 打包时安装的暂存目录(staging area)
--skip-code-generation 跳过生成 *_generated.* 文件,用于两阶段交叉编译
--clean-for-cross-compile 交叉编译场景下保留生成的 Go 源码不删除
--extra-include-dirs/-I--extra-library-dirs/-L 追加头文件/库搜索路径
--ignore-compiler-warnings 忽略编译器警告(默认 -Werror,警告即报错)
--disable-link-time-optimization LTO 开 关闭链接时优化(也可用环境变量 KITTY_NO_LTO 控制)
--vcs-rev 自动读取 .git 指定嵌入二进制的版本修订号

init_env()(L595-L749)可以看到参数如何真正影响编译:

  • debug 构建:优化级别从 -O3 切换为 -g3 -Og(编译器 ≥ 5.0 时),并额外追加 -DKITTY_DEBUG_BUILD-fno-omit-frame-pointer,保证调试器能拿到完整符号与栈帧;
  • sanitize 构建:追加 -fsanitize=address,undefined -fno-omit-frame-pointer,同时关闭 native 优化与 LTO(两者与 sanitizer 不兼容);
  • 发布构建:默认开启 -O3-flto(链接时优化)、-fstack-protector-strong-D_FORTIFY_SOURCE=2,x86 平台还会加 -march=native -mtune=native-fcf-protection=full 控制流保护。

--extra-logging=event-loop(对应 Makefile 的 debug-event-loop 目标)可用于在构建中开启事件循环的额外日志,便于排查渲染/事件问题。

系统级依赖:何时需要手动安装

需要强调的适用前提:上述依赖清单只在你选择链接系统库(即不通过 dev.sh 的预编译依赖、而直接用 make/setup.py 构建)时才需要手动安装。使用 ./dev.sh build 时,主要依赖均已随预编译包自动就位,Linux 上真正必需的系统库只剩 X11 与 DBUS。

运行期依赖

依赖 版本要求/说明
python 版本下限由 pyproject.tomlrequires-python 约束,setup.py 启动时会校验(L31-L45)
harfbuzz >= 2.2.0(setup.py 实际用 pkg-config 检查,见 at_least_version 调用)
zlib
libpng
liblcms2
libxxhash
openssl
shader-slang 仅使用自定义 shader 时需要
pixman / cairo / freetype / fontconfig / libcanberra macOS 上不需要
libsystemd 可选,非 systemd 系统不需要
ImageMagick 可选,终端内显示不常见图片格式时需要

构建期依赖

  • gccclang;
  • simde(SIMD 移植层);
  • shader-slang;
  • go(>= _build_go_version,当前 go.mod 声明 go 1.26.0/toolchain go1.26.6,该文件同时列出了构建期使用的 Go 包);
  • pkg-config;
  • Nerd Font Mono 字体:系统已安装,或放置在 fonts/SymbolsNerdFontMono-Regular.ttf(使用 dev.sh 时会自动下载到此路径);
  • Linux 上可能还需:liblcms2-devlibfontconfig-devlibssl-devlibpython3-devlibxxhash-devlibsimde-devlibcairo2-dev;
  • X11 开发库:libdbus-1-devlibxcursor-devlibxrandr-devlibxi-devlibxinerama-devlibgl1-mesa-devlibxkbcommon-x11-devlibfontconfig-devlibx11-xcb-dev

kitty 仓库自带的 Nix 环境 shell.nix 恰好是上述清单的可执行版本:mkShellbuildInputs 包含 harfbuzz、lcms2、xxHash、simde、go、shader-slang,Linux 分支另含 fontconfig、libcanberra、X11 系列、wayland、openssl、dbus、cairo 等,并在 shellHook 中为 Linux 设置 KITTY_EGL_LIBRARYKITTY_CANBERRA_LIBRARY 等库路径、为 macOS 设置 KITTY_NO_LTO 并手动复制 Nerd 字体到 fonts/

用 Nix 构建并运行

在 NixOS 或任何安装了 Nix 包管理器的 Linux/macOS 系统上:

nix-shell            # 自动拉取全部构建依赖并注入 shell
nix-shell --pure     # 排除全局安装包等外部环境影响

进入 Nix shell 后,按前文对应平台的说明执行 makemake app 即可。

本地构建文档

# 生成可本地浏览的文档
./dev.sh deps -for-docs && ./dev.sh docs

# 开发文档:本地服务器 + 实时热重载
./dev.sh deps -for-docs && ./dev.sh docs -live-reload

从源码看其实现:bypy/devenv.godependencies_for_docs()(L219-L249)先下载 get-pip.py,再用预编译 Python 执行 pip install -r docs/requirements.txt;docs/requirements.txt 包含 sphinx、furo、sphinx-copybutton、sphinxext-opengraph、sphinx-design、sphinx-autobuild、matplotlib。docs 子命令(L412-L439)则调用 make docs SPHINXBUILD=... SPHINXAUTOBUILD=...;-live-reload 切换为 docs/Makefile 中的 develop-docs 目标,使用 sphinx-autobuild 监视 kitty/kittens/ 源码目录变更并热重载。根 Makefile 还保留了传统入口:make manmake htmlmake dirhtml 等直接代理到 docs 目录的 Sphinx 构建。

面向 Linux/macOS 打包者的说明

kitty 的发布源码以 tarball 形式提供于 GitHub Releases 页面。由于 kitty 虽使用 Python 但不是传统 Python 包,不应安装进 site-packages,正确的打包方式是:

make linux-package

该目标(Makefile L38-L40)会先 rm -rf linux-package 再运行 python3 setup.py linux-package,把所有文件安装到 linux-package 暂存目录:

  • 可执行文件:linux-package/bin/kitty;
  • 运行所需文件:linux-package/lib/kitty;
  • terminfo:linux-package/share/terminfo

把这三个目录内容拷入 /usr 即完成安装;可用 --prefix 参数指定其他暂存目录。

官方建议将 kitty 拆分为三个软件包,以便用户可以在只 ssh 登录的服务器上仅安装 terminfo 与 shell 集成脚本:

  • kitty-terminfo:仅安装 terminfo 文件;
  • kitty-shell-integration:安装 shell 集成脚本(即源码中 shell-integration/ 目录内容),通常放到 /usr/share/kitty/shell-integration;
  • kitty:安装主程序。

硬性约束:安装 kitty 主包时,shell 集成脚本必须同时存在于 lib/kitty/shell-integration,因为 kitty 程序在运行时预期从该位置读取它们。

额外构建依赖:编译 terminfo 需要 tic(通常随 ncurses 开发包提供);若从 git 检出(而非发布 tarball)构建 linux-package,还需按 docs/requirements.txt 安装文档依赖(最简方式:python -m pip install -r docs/requirements.txt)。以上规则同样适用于 Homebrew、MacPorts 等 macOS 打包场景。

交叉编译

官方立场:交叉编译既不受官方支持也不推荐——因为交叉编译出的构建无法运行测试套件。但仓库提供了一定程度的支持,流程为两步:

# 第一阶段:构建出已生成代码(生成的 Go/C 源文件保留)
make prepare-for-cross-compile

对应 Makefile L66-L67:先 clean all,再执行 python3 setup.py clean --clean-for-cross-compile(该选项的作用正是保留生成的 Go 源文件,见 setup.py 参数说明)。

# 配置好交叉编译环境(CC、CFLAGS、PATH 等)后:
make cross-compile

对应 L69-L70:python3 setup.py linux-package --skip-code-generation,即跳过代码生成阶段、直接链接交叉目标,产物同样落在 linux-package 目录。

小结:构建流程速查

场景 命令
首次/日常源码构建 ./dev.sh build
刷新预编译依赖 ./dev.sh deps
调试构建 ./dev.sh build --debug
sanitizer 构建 ./dev.sh build --debug --sanitize
本地文档(热重载) ./dev.sh deps -for-docs && ./dev.sh docs -live-reload
Nix 环境构建 nix-shell && make
制作软件包 make linux-package
交叉编译 make prepare-for-cross-compile → 配置环境 → make cross-compile

核心结论:kitty 的构建体系围绕"预编译依赖 + 源码直跑"设计,./dev.sh build 之下的 bypy/devenv.go 负责依赖自动化,setup.py 负责编译细节(严格告警、LTO、SIMD 优化、架构探测);理解 DEVELOP_ROOT 这一内嵌路径宏,是理解其"从源码运行"特性与目录不可移动约束的关键。

登录后查看全文
热门项目推荐
相关项目推荐