昇腾CANN manifest:仓库清单与版本管理实战
55 个独立仓库,每个仓库独立迭代——CANN 8.0 里的 ops-transformer 是哪个 commit?hccl 是 v2.1.3 还是 v2.2.0?runtime 和 driver 的版本是否兼容?manifest 仓库用一份 XML 格式的清单文件回答了所有这些问题。它是 CANN 发行版的「物料清单」——定义一次发行包含哪些仓库、每个仓库用哪个 commit、仓库之间的依赖关系。
Manifest 文件结构
每个 CANN 发行版有一个 manifest XML 文件:
<!-- manifest/releases/CANN_8.0.0.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
<remote name="atomgit" fetch="https://atomgit.com/cann/" />
<!-- 核心算子库 -->
<project name="opbase" path="src/opbase" revision="e3f2a1b" />
<project name="ops-math" path="src/ops-math" revision="7c8d9e0" />
<project name="ops-nn" path="src/ops-nn" revision="a1b2c3d" />
<project name="ops-transformer" path="src/ops-transformer" revision="f4e5d6c" />
<project name="ops-cv" path="src/ops-cv" revision="b7a8c9d" />
<project name="ops-blas" path="src/ops-blas" revision="1a2b3c4" />
<project name="ops-fft" path="src/ops-fft" revision="d5e6f7a" />
<project name="ops-rand" path="src/ops-rand" revision="8b9c0d1" />
<project name="ops-tensor" path="src/ops-tensor" revision="e2f3a4b" />
<!-- 编译与运行时 -->
<project name="ge" path="src/ge" revision="5c6d7e8" />
<project name="runtime" path="src/runtime" revision="f9a0b1c" />
<project name="driver" path="src/driver" revision="2d3e4f5" />
<project name="metadef" path="src/metadef" revision="6a7b8c9" />
<!-- 加速库 -->
<project name="catlass" path="src/catlass" revision="0d1e2f3" />
<project name="ascend-transformer-boost" path="src/atb" revision="a4b5c6d" />
<project name="graph-autofusion" path="src/graph-autofusion" revision="e7f8a9b" />
<project name="asnumpy" path="src/asnumpy" revision="0c1d2e3" />
<project name="torchtitan-npu" path="src/torchtitan-npu" revision="4f5a6b7" />
<!-- 通信库 -->
<project name="hccl" path="src/hccl" revision="8c9d0e1" />
<project name="hcomm" path="src/hcomm" revision="f2a3b4c" />
<project name="hixl" path="src/hixl" revision="5d6e7f8" />
<project name="ascend-boost-comm" path="src/ascend-boost-comm" revision="9a0b1c2" />
<project name="shmem" path="src/shmem" revision="3d4e5f6" />
</manifest>
每个 <project> 元素的核心三个字段:
name:AtomGit 仓库名path:在源码树中的相对路径revision:Git commit hash(短格式,7 位)
有了这份 manifest,可以一键拉取整个 CANN 发行版的全部源码:
# 用 repo 工具拉取 CANN 8.0.0 完整源码
repo init -u https://atomgit.com/cann/manifest \
-b refs/tags/CANN_8.0.0 -m releases/CANN_8.0.0.xml
repo sync -j16
# 拉取后的目录结构:
# src/
# ├── opbase/ → opbase@e3f2a1b
# ├── ops-math/ → ops-math@7c8d9e0
# ├── ops-nn/ → ops-nn@a1b2c3d
# ├── ops-transformer/ → ops-transformer@f4e5d6c
# ├── ge/ → ge@5c6d7e8
# ├── runtime/ → runtime@f9a0b1c
# ├── driver/ → driver@2d5e4f5
# ...(共 55 个仓库,精确到 commit)
repo sync 自动执行 git clone + git checkout 到 manifest 指定的 commit——不需要手动挨个 clone 55 个仓库。
Manifest 的版本管理
CANN 有两个 release 轨道:stable 和 RC(Release Candidate)。Manifest 用 Git 分支区分:
manifest 仓库分支结构
├── main(开发分支,跟着各仓库的 HEAD 走)
├── releases/8.0.x(8.0 stable 线)
│ ├── CANN_8.0.0.xml → 各仓库 commit 指向 8.0.0 发布时的
│ ├── CANN_8.0.1.xml → bugfix 发布,仅变化了出 bug 的仓库
│ └── CANN_8.0.2.xml
├── releases/8.5.x(8.5 stable 线)
│ └── CANN_8.5.0.xml
└── releases/rc(RC 线)
├── CANN_8.5.0-rc1.xml
├── CANN_8.5.0-rc2.xml
└── CANN_8.5.0-rc3.xml
关键原则:bugfix 发布只修改出 bug 的仓库的 revision,其余仓库不变。
# CANN_8.0.1.xml 相对于 CANN_8.0.0.xml 的 diff
# 只有一个仓库的 revision 变了
--- releases/CANN_8.0.0.xml
+++ releases/CANN_8.0.1.xml
@@ -5,7 +5,7 @@
- <project name="ops-nn" path="src/ops-nn" revision="a1b2c3d" />
+ <project name="ops-nn" path="src/ops-nn" revision="x9y8z7w" />
只有 ops-nn 仓库在 8.0.1 中更新了(修复了 MatMul 的一个 bug),其余 54 个仓库不变。
CI 自动生成 Manifest
Manifest 不该手动编辑——55 个仓库的 commit hash 手动写容易出错。CI 自动生成:
# .github/workflows/generate-manifest.yml(manifest 仓库自身的 CI)
name: Generate Release Manifest
on:
workflow_dispatch:
inputs:
version:
description: 'CANN version (e.g. 8.0.0, 8.0.1)'
required: true
pre_release:
type: boolean
default: false
jobs:
generate:
runs-on: ubuntu-latest
steps:
- name: Checkout manifest repo
uses: actions/checkout@v3
- name: Resolve revisions
run: |
python3 tools/resolve_manifest.py \
--version=${{ inputs.version }} \
--output=releases/CANN_${{ inputs.version }}.xml
- name: Validate manifest
run: |
python3 tools/validate_manifest.py \
--manifest=releases/CANN_${{ inputs.version }}.xml
# 验证:
# 1. 所有 55 个仓库的 commit 都存在(没有 rebase 丢掉的)
# 2. 仓库之间的依赖关系正确
# 3. 没有循环依赖
- name: Tag and commit
run: |
git tag CANN_${{ inputs.version }}
git push --tags
tools/resolve_manifest.py 的逻辑:
# manifest/tools/resolve_manifest.py
import subprocess
import xml.etree.ElementTree as ET
# 55 个仓库的清单
REPOS = [
("opbase", "src/opbase"),
("ops-math", "src/ops-math"),
# ... 全部 55 个仓库
]
def resolve_manifest(version_tag_prefix):
doc = ET.Element("manifest")
ET.SubElement(doc, "remote", name="atomgit", fetch="https://atomgit.com/cann/")
for name, path in REPOS:
# 获取仓库在 version_tag 下的 commit hash
url = f"https://atomgit.com/cann/{name}"
tag = f"v{version_tag_prefix}"
# git ls-remote 获取 tag 对应的 commit
commit = subprocess.check_output(
["git", "ls-remote", url, f"refs/tags/{tag}"],
text=True
).split()[0][:7] # 取短 hash
ET.SubElement(doc, "project",
name=name, path=path, revision=commit)
return ET.tostring(doc, encoding="unicode")
每个仓库的 release tag(如 v8.0.0)指向该仓库在 CANN 8.0.0 时的 commit。Manifest 把所有这些 commit 汇成一份清单。
跨仓库兼容性检查
55 个仓库各自独立迭代——但 CANN 是一个整体。ops-transformer 依赖 ops-nn 的一个特定 API——如果 ops-nn 的 API 改了,ops-transformer 也必须更新。Manifest 的兼容性检查确保所有仓库的 commit 之间是兼容的。
# manifest/tools/validate_manifest.py
def validate_compatibility(manifest_xml):
# 解析 manifest
tree = ET.parse(manifest_xml)
repos = {}
for proj in tree.findall("project"):
repos[proj.get("name")] = proj.get("revision")
# 已知的跨仓库依赖关系
deps = {
"ops-nn": ["opbase"],
"ops-transformer": ["opbase", "ops-nn"],
"ascend-transformer-boost": ["ops-transformer", "ge"],
"cann-recipes-infer": ["ascend-transformer-boost", "runtime"],
}
for repo, req_deps in deps.items():
# 检查被依赖的仓库是否在 manifest 中
for dep in req_deps:
if dep not in repos:
raise ValueError(f"{repo} depends on {dep}, but {dep} not in manifest")
# 从仓库的 API 版本文件中读取依赖的版本约束
dep_versions = read_api_versions(repo, repos[repo])
for dep_name, min_version in dep_versions.items():
actual_version = read_api_version(dep_name, repos[dep_name])
if actual_version < min_version:
raise ValueError(
f"{repo}@{repos[repo]} requires {dep_name}>={min_version}, "
f"but manifest has {dep_name}@{repos[dep_name]} (v{actual_version})"
)
兼容性检查在 CI 中跑——Manifest 生成后自动验证,验证失败则不允许发布。
踩坑一:仓库 rebase 后 Manifest 中的 commit hash 失效
开发者对 ops-nn 的 main 分支做了 rebase(压缩 commit 历史)。Manifest 中的 ops-nn commit a1b2c3d 不再存在于远程仓库——repo sync 会报错。
修复:保护 release 分支的 commit 不被 rebase 删除。
# 在每个仓库设置 GitHub 保护规则
# main 分支:禁止 force push(保护 release tag 的 commit 不被重写)
# 每个 release tag(如 v8.0.0):创建后不可删除、不可移动
# 如果确实需要修改 release 分支(严重安全漏洞):
# 1. 更新 Manifest,指向新的 commit
# 2. 撤销旧 release tag,打新 tag
# 3. 发布 hotfix 版本(CANN 8.0.0-hotfix1)
踩坑二:Submodule vs Manifest 的选择
Git submodule 也能管理多仓库——在父仓库里 git submodule add 每个子仓库,submodule 的 commit hash 记录在父仓库中。为什么 CANN 不用 submodule 而用 Manifest + repo 工具?
关键原因:CANN 有 55 个仓库。git submodule update --recursive 在 55 个仓库上是串行的(逐个 clone)——耗时长。repo sync -j16 是并行的(16 个仓库同时 clone)——快得多。Manifest 的另一个优势是分支管理:同一个 manifest 仓库可以维护多条 release 线的 manifest 文件(8.0.x / 8.5.x / rc),而 submodule 的 commit hash 直接绑在父仓库的 Git 历史上——更难做多线维护。
踩坑三:Release Tag 命名不一致
不同的 CANN 仓库用不同的 release tag 格式:
ops-nn:v8.0.0hccl:v2.1.3(版本号独立于 CANN 版本)driver:1.76.22.3.220(固件版本号格式)
Manifest 的 resolve_manifest.py 假设所有仓库都用 v{CANN_VERSION} 格式的 tag——碰到 hccl 的 v2.1.3 和 driver 的 1.76.22.3.220 就找不到。
修复:每个仓库加 manifest.tag 配置文件。
# hccl/manifest.tag
# 在 hccl 仓库根目录存放 tag 映射
# CANN 版本 → hccl 的 release tag
8.0.0 = v2.1.3
8.5.0 = v2.2.0
resolve_manifest.py 读取各仓库的 manifest.tag 做映射——CANN 版本和仓库自身的 tag 之间通过这个文件桥接。
Manifest 不是一个会写代码的仓库——它是 55 个仓库的「快照」。每一个 CANN 发行版的构建、测试、发布、部署、回滚——都从这份 manifest XML 文件开始。自动化是 manifest 的核心:manifest 由 CI 自动生成、跨仓库兼容性自动检验、repo sync 自动拉取全部源码。从 manifest 出发,可以重建任意一个 CANN 版本的完整源码树——这是软件工程里的 reproducibility(可重现性),开源社区的基础设施。
更多推荐




所有评论(0)