本文档介绍项目中的构建版本信息机制:如何在构建产物(Python wheel 包、C++ 二进制)中嵌入 git commit、构建时间等元数据,以及如何为新组件接入该机制。
版本信息的注入基于 Bazel 的 workspace status 机制,整体流程如下:
workspace_status.sh Bazel stamp 机制 gen_version_info.py
┌──────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐
│ 执行 git 命令 │───>│ stable-status.txt │───>│ 读取 status 文件 │
│ 输出 key-value │ │ volatile-status.txt │ │ 生成 .py 或 .h 文件 │
└──────────────────┘ └──────────────────────┘ └──────────────────────┘
.bazelrc 中配置了:
build --workspace_status_command=bazel/workspace_status.sh
每次 bazel build 时,Bazel 会执行该脚本,脚本输出 key-value 对:
| 变量名 | 示例值 | 说明 |
|---|---|---|
STABLE_GIT_COMMIT |
2cd6c5a9 |
git short commit hash (8位) |
STABLE_GIT_COMMIT_FULL |
2cd6c5a9... |
git 完整 commit hash |
STABLE_GIT_REPO |
git@github.com:alibaba/tair-kvcache.git |
远程仓库地址 |
STABLE_KVCM_VERSION |
0.0.1 |
语义版本号 |
STABLE_BUILD_TIMESTAMP |
20260409113826 |
构建日期+时间,用于完整版本号,避免同 commit 打包冲突 |
BUILD_DATE |
20260409 |
构建日期 |
BUILD_TIME |
2026-04-09 11:38:26 |
构建时间 |
STABLE_ 前缀与 volatile 变量的区别:
STABLE_前缀的变量写入stable-status.txt,值变化时会触发依赖目标重新构建。适用于 git commit、版本号等不频繁变化的值。- 无前缀的变量写入
volatile-status.txt,值变化时不会触发重新构建。适用于构建时间等每次都会变化的值,避免不必要的重新编译。
在 BUILD 文件中通过 stamp = 1 的 genrule 调用 gen_version_info.py,该脚本读取 Bazel 生成的 status 文件,输出格式化的版本信息文件。
- Python 组件:生成
_version_info.py,包含VERSION、GIT_COMMIT、FULL_VERSION等模块级变量 - C++ 组件:生成
build_version.h和build_version.cc,包含kKvcmVersion、kKvcmGitCommit、kKvcmFullVersion等常量
Python wheel 包的版本号通过 py_wheel 规则的 stamp 属性注入。stamp = 1 时,version 字段中的 {VARIABLE} 占位符会被替换为 status 文件中的实际值:
py_wheel(
name = "my_wheel",
version = "{STABLE_KVCM_VERSION}+{STABLE_BUILD_TIMESTAMP}.{STABLE_GIT_COMMIT}",
stamp = 1,
...
)最终 wheel 内部的版本号为 0.0.1+20260409113826.2cd6c5a9(符合 PEP 440 local version 规范)。
注意:Bazel 输出路径中的文件名在分析阶段确定,仍包含字面占位符。使用
.dist后缀目标(如bazel build :my_wheel.dist)可在dist/目录下获得正确文件名的 wheel 文件。
最终的完整版本号格式为:
{STABLE_KVCM_VERSION}+{STABLE_BUILD_TIMESTAMP}.{STABLE_GIT_COMMIT}
示例:0.0.1+20260409113826.2cd6c5a9
0.0.1:语义版本号,在bazel/workspace_status.sh中定义20260409113826:构建日期+时间,使用单个数字段避免 PEP 440 对纯数字 local version segment 去除前导零2cd6c5a9:git commit 短 hash
bazel/
├── workspace_status.sh # Bazel workspace status 脚本,提供 stamp 变量
├── gen_version_info.py # 版本信息文件生成器(支持 Python / C++ 输出)
├── version.bzl # 可复用的 Starlark 宏(version_info_py / version_info_cc)
└── BUILD # exports_files 声明
- 在 BUILD 文件中加载宏并生成版本模块:
load("//bazel:version.bzl", "version_info_py")
version_info_py(name = "gen_version_info")
py_library(
name = "my_lib",
srcs = glob(["*.py"]) + [":gen_version_info"],
)- 在 Python 代码中导入使用:
from my_package._version_info import FULL_VERSION, GIT_COMMIT, BUILD_TIME
logger.info("version: %s (commit: %s, build: %s)", FULL_VERSION, GIT_COMMIT, BUILD_TIME)生成的 _version_info.py 包含以下变量:
| 变量名 | 类型 | 示例值 | 说明 |
|---|---|---|---|
VERSION |
str | "0.0.1" |
语义版本号 |
GIT_COMMIT |
str | "2cd6c5a9" |
短 commit hash |
GIT_COMMIT_FULL |
str | "2cd6c5a9..." |
完整 commit hash |
GIT_REPO |
str | "git@github.com:..." |
远程仓库地址 |
BUILD_DATE |
str | "20260409" |
构建日期 |
BUILD_TIMESTAMP |
str | "20260409113826" |
构建日期+时间 |
BUILD_TIME |
str | "2026-04-09 11:38:26" |
构建时间 |
FULL_VERSION |
str | "0.0.1+20260409113826.2cd6c5a9" |
完整版本号 |
- 在 BUILD 文件中加载宏并生成版本库:
load("//bazel:version.bzl", "version_info_cc")
version_info_cc(name = "build_version")
cc_library(
name = "my_lib",
deps = [":build_version"],
...
)version_info_cc 会生成稳定的 build_version.h 声明文件和包含实际字符串值的 build_version.cc。构建时间变化时只需要重编这个很小的 .cc,依赖方通常只需要重新链接。
- 在 C++ 代码中包含使用:
#include "my_package/build_version.h"
// 可用的常量:
// kKvcmVersion - "0.0.1"
// kKvcmGitCommit - "2cd6c5a9"
// kKvcmGitCommitFull - "2cd6c5a9..."
// kKvcmGitRepo - "git@github.com:..."
// kKvcmBuildDate - "20260409"
// kKvcmBuildTimestamp - "20260409113826"
// kKvcmBuildTime - "2026-04-09 11:38:26"
// kKvcmFullVersion - "0.0.1+20260409113826.2cd6c5a9"
std::cout << "Version: " << kKvcmFullVersion << std::endl;在 py_wheel 规则中启用 stamp:
py_wheel(
name = "my_package",
version = "{STABLE_KVCM_VERSION}+{STABLE_BUILD_TIMESTAMP}.{STABLE_GIT_COMMIT}",
stamp = 1,
...
)构建带正确文件名的 wheel:
# 标准构建(输出文件名包含字面占位符,但 wheel 内部元数据正确)
bazel build //path/to:my_package
# 推荐:使用 .dist 目标获取正确文件名
bazel build //path/to:my_package.dist
# 输出位于 bazel-bin/path/to/my_package.dist/
# 文件名示例:my_package-0.0.1+20260409113826.2cd6c5a9-cp310-cp310-linux_x86_64.whl| 组件 | 类型 | 版本信息位置 |
|---|---|---|
| sglang connector | Python wheel + 日志 | py_connector/common/_version_info.py,启动时打印 |
| vllm connector | Python wheel + 日志 | py_connector/common/_version_info.py,启动时打印 |
| client pybind | Python wheel | wheel 包版本号 |
| optimizer pybind | Python wheel | wheel 包版本号 |
| manager (C++) | C++ 二进制 | common/build_version.h / .cc,./main -v 输出 |
语义版本号定义在 bazel/workspace_status.sh 中:
echo "STABLE_KVCM_VERSION 0.0.1"发版时修改此值即可,所有组件会自动使用新版本号。
如果源码不在 git 仓库中(例如直接下载的源码包),构建流程仍然可以正常完成,不会失败。具体行为:
workspace_status.sh中的所有 git 命令都有|| echo unknown兜底,git 信息会退化为unknownSTABLE_KVCM_VERSION、STABLE_BUILD_TIMESTAMP、BUILD_DATE、BUILD_TIME不依赖 git,不受影响- 最终版本号为
0.0.1+20260409113826.unknown,PEP 440 合法 - py_wheel stamp 中
{STABLE_GIT_COMMIT}被替换为unknown,wheel 正常构建 - C++ 常量同理,编译不受影响