首页
/ llama.cpp 发布工程实践:语义化版本、release 工作流与分发渠道全解析

llama.cpp 发布工程实践:语义化版本、release 工作流与分发渠道全解析

2026-09-04 13:09:23作者:廉彬冶Miranda

llama.cpp 的版本管理遵循语义化版本(Semantic Versioning, MAJOR.MINOR.PATCH),发布流程由根 CMakeLists.txt 中的版本变量、.github/workflows/make-release.yml 手动工作流以及一套预检脚本共同构成。读完本篇,你能掌握 llama.cpp 完整的版本升级规则、发布前置校验逻辑(分支归属、3 天时效、上游 ggml 一致性比对)、nightly 构建机制,以及预编译二进制、包管理器、源码编译三类分发渠道的工作原理。

语义化版本规则与版本号的唯一事实来源

根据 docs/release.md,llama.cpp 的版本变更遵循如下映射规则:

变更类型 版本组件
公共 C API(include/llama.h)的破坏性变更 MAJOR
向后兼容的功能、新模型支持或 API 新增 MINOR
不涉及 API 变更的缺陷修复 PATCH

版本号集中定义在根 CMakeLists.txt 顶部的三个变量中(当前仓库快照为 0.3.0):

# CMakeLists.txt(第 5-21 行)
### llama.cpp version
set(LLAMA_VERSION_MAJOR 0)
set(LLAMA_VERSION_MINOR 3)
set(LLAMA_VERSION_PATCH 0)
set(LLAMA_VERSION_BASE "${LLAMA_VERSION_MAJOR}.${LLAMA_VERSION_MINOR}.${LLAMA_VERSION_PATCH}")

# whether this is a development/nightly build
# set this to OFF when making a release from a release tag (vX.Y.Z)
option(LLAMA_BUILD_IS_DEV "llama: dev build" ON)

if (LLAMA_BUILD_IS_DEV)
    set(LLAMA_VERSION "${LLAMA_VERSION_BASE}-dev")
else()
    set(LLAMA_VERSION "${LLAMA_VERSION_BASE}")
endif()

文档同时强调了一条流程约定:版本号的 bump 应当包含在引入变更的 PR 中,或作为独立 bump 提交在切出 release 之前合入。文档中还留有一个待办事项——为 PR 增加 semver: patch / semver: minor / semver: major 标签,以便在切 release 前识别哪些 PR 需要版本号变更。

-dev 后缀的机制:区分 nightly 与正式构建

LLAMA_BUILD_IS_DEV 默认值为 ON,会给最终版本字符串追加 -dev 后缀,用于标记 nightly/开发构建;从 release tag 构建正式分发包时必须显式传入 -DLLAMA_BUILD_IS_DEV=OFF,才能得到干净的版本字符串(例如 0.3.0 而非 0.3.0-dev)。

这个版本字符串并非只用于打印,它沿着清晰的传播链路进入构建产物与公共 API:

const char * llama_version(void) {
    return LLAMA_VERSION;
}

即运行时打印的版本号与构建时注入的 LLAMA_VERSION 宏完全一致。这也解释了为什么 LLAMA_BUILD_IS_DEV 必须参与正式构建的开关控制:它是唯一区分 x.y.zx.y.z-dev 的闸门。

版本升级:release.sh 的准备流程

仓库提供 scripts/release.sh 作为版本准备的半自动化工具,其用法为:

./scripts/release.sh [major|minor|patch] [--dry-run]

# 示例:升级次版本号
$ ./scripts/release.sh minor

脚本执行前会做三项硬性校验(见脚本内 check_git_statuscheck_master_branchcheck_master_up_to_date 函数):工作区无未提交变更、当前分支必须是 master、本地 master 必须与 origin/master 一致(会执行 git fetch origin master 比对 SHA)。--dry-run 模式则跳过这些校验,只打印将要执行的操作。

校验通过后,脚本依次完成:

  1. 读取当前版本:从根 CMakeLists.txtgrepLLAMA_VERSION_MAJOR/MINOR/PATCH 三个值;
  2. 计算新版本号:按 semver 规则进位——major 升级时 MINORPATCH 归零,minor 升级时 PATCH 归零,缺省参数按 patch 处理;
  3. 创建 release candidate 分支:命名为 llama-rc-v<新版本号>(如 llama-rc-v0.4.0)并切换过去;
  4. 修改 CMakeLists.txt 并提交:通过兼容 GNU 与 BSD sed 的 sed_inplace 原地替换三个 set(LLAMA_VERSION_*) 语句,提交信息固定为 llama.cpp : bump version to <新版本号>

脚本结束后打印的 Next steps 明确了后续分工:推送 RC 分支、向 master 发起 PR、等待合并且 build-cpu 工作流通过后,再由 make-release 工作流创建 release tag。也就是说,版本号变更走常规 PR 评审流程,tag 创建才由工作流完成——这与文档中"bump 应包含在引入变更的 PR 中"的约定形成互补。

make-release 工作流:tag 即发布

正式发布通过 .github/workflows/make-release.yml 触发,这是一个 workflow_dispatch 手动工作流,暴露两个输入:

输入 说明 默认值
commit 要发布的 commit SHA,留空则使用所选分支的 HEAD
dry_run 仅执行校验、不创建 tag true

工作流运行在"Run workflow"对话框所选的分支上(默认 master)。当指定了 commit 时,scripts/make-release-checks.sh 会验证该 commit 属于目标分支且不晚于分支 HEAD 的 3 天前,满足条件时发布该 commit 而非分支 HEAD。

发布前的四道校验

make-release-checks.sh 是发布门禁的核心,按顺序执行:

  1. 推导版本号:同样从根 CMakeLists.txt 解析出 v<MAJOR>.<MINOR>.<PATCH>(注意此处带 v 前缀,即 tag 形如 v0.3.0);
  2. commit 归属与时效检查:用 git merge-base --is-ancestor 确认 commit 在 origin/<RELEASE_BRANCH> 上,并用提交时间戳差值确认其落后分支 HEAD 不超过 3 * 86400 秒;
  3. tag 冲突检查git ls-remote --tags origin <版本> 确认远端不存在同名 tag;
  4. CI 与上游一致性检查:通过 GitHub API 确认该 commit 存在一次 conclusion == success 的 release.yml(nightly)运行——"nightly 构建必须先成功才能发布";随后读取 ggml/CMakeLists.txt 中的 GGML_VERSION_*(当前为 0.22.0),git clone --depth 1 --branch v<GGML_VERSION> 拉取上游 ggml 对应 tag,对本地 vendored 的 ggml/srcggml/includeggml/CMakeLists.txtdiff -rq 全量比对,本地 vendored 副本与上游 tag 不一致时中止发布--dry-run 模式下仅记录告警并置 checks_passed=false)。

校验通过后,工作流执行(dry_run=false 时):

  • github-actions[bot] 身份创建附注 taggit tag -a)并推送远端,例如 v0.3.0
  • 调用 scripts/make-release-desc.sh 生成发布描述:自动寻找低于当前版本号的最近一个纯 semver tag 作为基准,用 git log --oneline <PREV>..<RELEASE_COMMIT> 产出逐 commit 的 changelog,并定位指向同一 commit 的 b* 系列 nightly tag 以生成对应的 nightly 构建链接;
  • 创建 GitHub Release 对象(prerelease: false),并将 nightly-tag.txt(内容为对应 nightly tag 名)作为 Release asset 上传,供下游工具链追踪该版本对应的 nightly 构建。

发布描述的写作规范

scripts/make-release-summary.txt 定义了人工撰写 Release 概述的模板,要求基于 make-release-desc.sh 输出的 changelog,按以下章节组织:Overview(1-3 句)、API changes(/include/*/tools/mtmd/mtmd.h/tools/server 的变更)、New models(src/models/ 新增模型)、Core changes(/src/*)、Multi-modality changes(/tools/mtmd/)、Server changes(/tools/server/)、UI changes(/tools/ui/)、ggml changes(ggml 版本变更时链接对应上游 release 并摘录其描述)。规范还要求每条 bullet 尽量单行不超过 120 字符、尽量附 PR 链接,且不要重复 ggml 侧已由链接覆盖的内容。

nightly 构建:发布流水线的上游

文档提到"目前只有 nightly/开发构建发布在 GitHub Releases 上",其来源是 .github/workflows/release.yml。该工作流有两条触发路径:master 分支 push(按源码文件类型过滤,且 commit message 含 [no release] 时跳过)以及手动 workflow_dispatchcreate_release 输入强制为 true)。构建统一使用:

-DLLAMA_BUILD_EXAMPLES=OFF -DLLAMA_BUILD_TESTS=OFF -DLLAMA_BUILD_TOOLS=ON -DLLAMA_BUILD_SERVER=ON -DGGML_RPC=ON

以 macOS 为例,矩阵包含 arm64-DGGML_METAL_EMBED_LIBRARY=ON,部署目标 13.3)与 x64(Metal 关闭以规避 runner 无 GPU 导致的间歇性失败)两个构建,均集成 ccache、构建后将 LICENSE 一并打入 build/bin/ 打包产物,并按 get-tag-name action 确定的 b* tag 命名发布。nightly tag 与正式 tag 指向同一 commit 的机制,正是 make-release-desc.sh 能在正式 Release 中链接对应 nightly 构建的基础,也是 make-release-checks.sh 要求"该 commit 必须有成功 nightly 运行"才能放行正式发布的原因——正式发布实质上是"把某个已验证的 nightly commit 固化成 semver tag"

发布物如何触达用户

文档明确当前分发渠道共三种:

  • 预编译二进制:下载基于 release tag 构建的预编译产物(nightly/开发构建直接可用,正式构建从 tag 产出),对应上述 release.yml 矩阵产物;
  • 包管理器:直接消费 git tag。tag 是唯一的发布工件——v<MAJOR>.<MINOR>.<PATCH> 形态的附注 tag 推送远端后,下游即可基于该 tag 出包;仓库中还存在如 .github/workflows/winget.yml 这类面向具体包生态的自动化工作流,以及 docs/docker.md 等面向容器用户的文档;
  • 源码构建:用户克隆仓库并 checkout 指定 tag 自行编译,此时应确保构建参数携带 -DLLAMA_BUILD_IS_DEV=OFF 以获得无 -dev 后缀的版本字符串,同时可用 llama_version() 运行时验证。

小结:一条完整的发布链路

把各组件串起来,llama.cpp 的发布链路是:

  1. 功能/修复 PR 合入时同步 bump CMakeLists.txt 三个版本变量(可借助 scripts/release.sh 生成 RC 分支与 bump 提交);
  2. master 每次符合条件的 push 触发 release.yml,产出 b* tag 的 nightly 构建并上传 GitHub Releases;
  3. 手动触发 make-release 工作流:make-release-checks.sh 依次验证版本推导、commit 分支归属与 3 天时效、tag 唯一性、nightly CI 成功、vendored ggml 与上游 tag 逐文件一致;
  4. 创建并推送附注 tag(如 v0.3.0),生成 changelog 与 nightly 关联链接,产出 GitHub Release 及 nightly-tag.txt asset;
  5. 用户经由预编译二进制、包管理器(直接消费 tag)或源码 checkout 三条渠道获取该版本,llama_version() 返回的最终字符串由 LLAMA_BUILD_IS_DEV 决定带不带 -dev

整套机制的关键设计在于:版本号单一事实来源(CMake)、tag 即发布、nightly 先行验证、vendored 依赖一致性强制比对,四个环节互为约束,使得任何一个环节的漂移(错误的 bump、未过 CI 的 commit、本地 ggml 魔改)都会在发布前被拦截。

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