CANN 开发场景:同一台服务器上要同时维护三个版本的 CANN——8.0.RC2(主力训练集群)、8.0.3(新算子验证)、8.1.beta(下一代架构评测)。传统方式:手动解压 run 包到 /usr/local/Ascend/ascend-toolkit/latest → 换版本 = 卸载 + 重装 = 30 分钟。三个版本同时存在?符号链接地狱。

cann-spack-package 基于 Spack 包管理器——每个 CANN 版本是一个独立的 Spack package,安装到独立的 prefix(/opt/spack/opt/linux-ubuntu20.04-x86_64/.../ascend-toolkit-8.0.3-xxx),通过 spack load 切换版本。Spack 的 concretizer 自动解决依赖冲突(bisheng 版本、Python 版本、系统库版本),每个 CANN 版本拥有完整的独立环境。

Spack 基础:cann-spack-package 的目录结构

cann-spack-package/
├── repo.yaml                          # Spack repo 注册文件
├── packages/                          # CANN 组件的 Spack package 定义
│   ├── ascend-toolkit/
│   │   └── package.py                 # CANN 主工具链(CANN 8.0.3)
│   ├── ascend-cann/
│   │   └── package.py                 # CANN 社区版(开源算子库 + 编译工具链)
│   ├── ascend-driver/
│   │   └── package.py                 # NPU 驱动(dkms 内核模块)
│   ├── ascend-firmware/
│   │   └── package.py                 # 昇腾 MCU 固件
│   ├── ascend-hccl/
│   │   └── package.py                 # HCCL 通信库(独立打包)
│   ├── ascend-toolkit-kernels/
│   │   └── package.py                 # 算子二进制包(ops-* 预编译)
│   ├── bisheng/
│   │   └── package.py                 # 毕昇编译器(CANN 依赖)
│   ├── pyasc/
│   │   └── package.py                 # pyasc Python 包
│   └── ascend-mindspore/
│       └── package.py                 # MindSpore + CANN backend
├── configs/
│   ├── packages.yaml                  # 全局包配置(版本偏好、编译器选择)
│   ├── compilers.yaml                 # 编译器配置(gcc/bisheng/clang)
│   └── repos.yaml                     # 外部 repo 配置(builtin + cann-spack-package)
├── environments/                      # 预定义的 spack 环境
│   ├── cann-8.0.3/
│   │   ├── spack.yaml                 # CANN 8.0.3 完整环境定义
│   │   └── spack.lock                 # concretizer 锁文件(精确版本快照)
│   ├── cann-8.0.rc2/
│   │   ├── spack.yaml                 # CANN 8.0.RC2 环境定义
│   │   └── spack.lock
│   └── cann-edge/                     # 边缘计算精简版
│       ├── spack.yaml
│       └── spack.lock
├── scripts/
│   ├── setup.sh                       # 一键初始化 Spack + 注册 CANN repo
│   ├── install-env.sh                 # 按环境名安装所有包
│   └── activate.sh                    # 激活特定 CANN 环境
└── README.md

核心 package.py:ascend-toolkit 的多版本定义

# cann-spack-package/packages/ascend-toolkit/package.py
#
# CANN 工具链的 Spack package 定义
# 支持 8.0.RC1 / 8.0.RC2 / 8.0.3 / 8.1.beta 四个版本并行安装

from spack.package import *
from spack.pkg.builtin.ascend_cann import AscendCannPackage

class AscendToolkit(AscendCannPackage):
    """CANN (Compute Architecture for Neural Networks) - Ascend Toolkit
    
    CANN is Huawei's heterogeneous compute architecture for Ascend NPUs.
    This package provides the full development toolkit including:
    - AscendCL (Application & operator development)
    - AOL (Ascend Operator Library)
    - AOE (Ascend Optimization Engine)  
    - Graph Engine (GE)
    - Runtime + Driver
    - HCCL (Huawei Collective Communication Library)
    - BiSheng Compiler
    """

    homepage = "https://www.hiascend.com/software/cann"
    
    # ====== 多版本并行定义 ======
    # 每个 version() 指定一个可安装的 CANN 版本
    # sha256 用于校验 run 包完整性
    
    # CANN 8.0.3 (stable, recommended)
    version("8.0.3", 
            sha256="a3f2c8b9d1e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0",
            preferred=True)   # preferred=True → concretizer 默认选这个
    
    # CANN 8.0.RC2 (legacy, still used in production)
    version("8.0.rc2",
            sha256="b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c9d8e7f6a5",
            deprecated=True)  # deprecated=True → 可用但推荐迁移到 8.0.3
    
    # CANN 8.1.beta (preview, next-gen)
    version("8.1.beta",
            sha256="c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6",
            preferred=False)

    # ====== 依赖声明 ======
    # 所有 CANN 版本的共同依赖
    
    # 编译器依赖:毕昇编译器 >= 3.3.0(CANN 8.0+ 要求)
    depends_on("bisheng@3.3.0:", type="build")
    
    # 运行时依赖:NPU 驱动(内核模块,系统级)
    depends_on("ascend-driver@1.0.firmware", type="run", when="@8.0:")
    
    # 算子库依赖(可选):
    # 如果安装 ascend-toolkit-kernels,自动注册到 CANN 算子路径
    depends_on("ascend-toolkit-kernels", type="run", when="+kernels")
    depends_on("ascend-toolkit-kernels@8.0.3", type="run", when="@8.0.3+kernels")
    
    # HCCL 通信库(独立打包,不同版本 CANN 可能用不同 HCCL)
    depends_on("ascend-hccl@8.0.3", type="run", when="@8.0.3")
    depends_on("ascend-hccl@8.0.rc2", type="run", when="@8.0.rc2")
    
    # Python 绑定(AscendCL Python API, pyasc)
    depends_on("python@3.8:3.10", type=("build", "run"))
    depends_on("py-numpy", type="run")
    
    # ====== Variant(可选特性) ======
    variant("kernels", default=True, 
            description="Install pre-built operator kernels")
    variant("torch", default=True, 
            description="Install PyTorch CANN backend (torch-npu)")
    variant("mindspore", default=False,
            description="Install MindSpore CANN backend")
    variant("debug", default=False,
            description="Build with debug symbols")
    
    # ====== 下载源(多个 mirror,自动 fallback) ======
    def url_for_version(self, version):
        """根据版本号构造下载 URL"""
        base = "https://ascend-repo.obs.cn-north-4.myhuaweicloud.com"
        if version >= Version("8.1"):
            base = "https://ascend-repo-beta.obs.cn-north-4.myhuaweicloud.com"
        
        return (
            f"{base}/CANN/{version}/"
            f"Ascend-cann-toolkit_{version}_linux-{self.arch}.run"
        )

    # ====== 安装步骤 ======
    def install(self, spec, prefix):
        """
        CANN 安装分三步:
        1. 自解压 .run 包
        2. 运行自带安装脚本(指定 prefix)
        3. 配置环境变量 + 符号链接
        """
        
        # Step 1: 自解压 run 包
        run_file = self.stage.archive_file  # 下载的 .run 文件
        chmod = which("chmod")
        chmod("+x", run_file)
        
        # 解压到临时目录
        extract_dir = join_path(self.stage.source_path, "extracted")
        mkdirp(extract_dir)
        
        # .run 包支持 --extract 参数
        Executable(run_file)(
            "--extract=" + extract_dir,
            "--quiet"
        )
        
        # Step 2: 运行自带安装脚本(指定 prefix)
        # CANN run 包内含 install.sh
        with working_dir(extract_dir):
            install_script = Executable("./install.sh")
            install_script(
                "--prefix=" + prefix,             # Spack 管理的安装路径
                "--install-type=full",            # 全量安装(非 minimal)
                "--install-for-all",              # 所有用户可访问
                "--quiet"
            )
        
        # Step 3: 环境变量 + 符号链接
        # Spack 通过 setup_run_environment() 自动设置
        # 这里只需确保安装目录结构正确
        
        # 验证关键文件存在
        for check_file in [
            "compiler/bin/bisheng",
            "runtime/lib64/libascendcl.so",
            "opp/built-in/op_impl/ai_core/tbe/op_info_cfg",
        ]:
            if not os.path.exists(join_path(prefix, check_file)):
                raise InstallError(
                    f"Missing critical file: {check_file}. "
                    f"CANN installation may be incomplete."
                )
        
        # 可选:安装 PyTorch CANN backend
        if spec.satisfies("+torch"):
            pip = which("pip")
            pip("install", f"torch-npu=={spec.version}")
    
    def setup_run_environment(self, env):
        """
        运行时环境变量(用户执行 spack load ascend-toolkit 时自动设置)
        """
        prefix = self.prefix
        
        # 基础路径
        env.set("ASCEND_HOME_PATH", prefix)
        env.set("ASCEND_TOOLKIT_HOME", prefix)
        
        # 编译器
        env.prepend_path("PATH", join_path(prefix, "compiler", "bin"))
        env.prepend_path("LD_LIBRARY_PATH", join_path(prefix, "compiler", "lib64"))
        
        # 运行时
        env.prepend_path("LD_LIBRARY_PATH", join_path(prefix, "runtime", "lib64"))
        env.prepend_path("PATH", join_path(prefix, "runtime", "bin"))
        
        # 算子库
        env.set("ASCEND_OPP_PATH", join_path(prefix, "opp"))
        
        # Python 绑定
        python_ver = f"python{self.spec['python'].version.up_to(2)}"
        env.prepend_path("PYTHONPATH", 
                        join_path(prefix, "python", "site-packages"))

    def setup_dependent_build_environment(self, env, dependent_spec):
        """
        下游包的构建环境(如 torch-npu、mindspore 需要 CANN 头文件和库)
        """
        env.set("ASCEND_TOOLKIT_HOME", self.prefix)
        env.prepend_path("CMAKE_PREFIX_PATH", self.prefix)

多版本并行安装:spack.yaml 环境定义

# cann-spack-package/environments/cann-8.0.3/spack.yaml
#
# CANN 8.0.3 完整环境定义
# spack install → 安装所有 12 个包 + 依赖 → spack load → 激活环境

spack:
  # 环境元数据
  specs:
    # 核心:CANN 工具链 + 算子库 + 通信库
    - ascend-toolkit@8.0.3 +kernels +torch
    
    # 可选:MindSpore(如不需要可注释掉)
    # - ascend-mindspore@2.2.0
    
    # 开发工具
    - pyasc@8.0.3 ~mindspore +torch
    - cmake@3.27
    
    # 推理引擎
    - ascend-toolkit-kernels@8.0.3
  
  # Concretizer 配置
  concretizer:
    unify: true           # 所有包统一 concretize(避免冲突)
    reuse: true           # 复用已安装包
  
  # 包配置(版本偏好、变体、编译器)
  packages:
    # 全局编译器:使用系统 gcc 12.3.0 或毕昇编译器
    all:
      compiler: [gcc@12.3.0, bisheng@3.3.0]
      providers:
        mpi: [openmpi@4.1]
        blas: [openblas]
    
    # ascend-toolkit 特定配置
    ascend-toolkit:
      variants: +kernels +torch ~mindspore ~debug
      version: [8.0.3]   # 优先 8.0.3
    
    # Python 版本固定
    python:
      version: [3.9.18]
      variants: +optimizations +ssl
    
    # 使用外部预装包(避免编译)
    cmake:
      buildable: false    # 使用系统 cmake
      externals:
        - spec: cmake@3.27.4
          prefix: /usr
  
  # 视图(View):将所有包链接到统一目录
  view: true              # spack view → $SPACK_ENV/.spack-env/view/
  
  # 模块文件生成
  modules:
    default:
      enable: [tcl, lmod]
      roots:
        lmod: /opt/modules/cann
        tcl: /opt/modules/cann-tcl

一键初始化 + 安装脚本(scripts/setup.sh)

#!/bin/bash
# cann-spack-package/scripts/setup.sh
# 一键初始化 CANN Spack 环境

set -e

SPACK_ROOT="${SPACK_ROOT:-/opt/spack}"
CANN_REPO_DIR="$(cd "$(dirname "$0")/.." && pwd)"

echo "============================================"
echo "CANN Spack Package Manager Setup"
echo "============================================"
echo ""

# Step 1: 安装 Spack(如未安装)
if [ ! -d "$SPACK_ROOT" ]; then
    echo "[1/5] Installing Spack..."
    git clone -c feature.manyFiles=true \
        https://github.com/spack/spack.git "$SPACK_ROOT"
    cd "$SPACK_ROOT"
    git checkout v0.21.0   # 锁定 Spack 版本
fi

# Step 2: 激活 Spack
echo "[2/5] Activating Spack..."
source "$SPACK_ROOT/share/spack/setup-env.sh"

# Step 3: 注册 cann-spack-package 为 Spack repo
echo "[3/5] Registering CANN Spack repo..."
spack repo add "$CANN_REPO_DIR"

# 验证注册成功
if spack repo list | grep -q "cann-spack-package"; then
    echo "  ✓ CANN Spack repo registered"
else
    echo "  ✗ Failed to register CANN Spack repo"
    exit 1
fi

# Step 4: 配置系统编译器
echo "[4/5] Configuring compilers..."
spack compiler find   # 自动检测 gcc / clang

# 手动注册毕昇编译器(如果已安装)
if [ -f "/opt/bisheng/3.3.0/bin/clang" ]; then
    spack compiler add --scope site /opt/bisheng/3.3.0
    echo "  ✓ BiSheng 3.3.0 registered"
fi

# Step 5: Mirror 配置(国内镜像加速)
echo "[5/5] Configuring mirrors..."
spack mirror add cann-mirror \
    https://mirrors.huaweicloud.com/ascend/cann-spack/

echo ""
echo "============================================"
echo "Setup complete! Next steps:"
echo ""
echo "  Install CANN 8.0.3:"
echo "    cd $CANN_REPO_DIR/environments/cann-8.0.3"
echo "    spack env activate ."
echo "    spack install"
echo ""
echo "  Load CANN environment:"
echo "    spack load ascend-toolkit@8.0.3"
echo ""
echo "  Switch to different version:"
echo "    spack unload ascend-toolkit@8.0.3"
echo "    spack load ascend-toolkit@8.0.rc2"
echo "============================================"

实际操作:三版本并存

# === CANN 三版本并行安装与切换 ===

# 1. 安装三个版本(并行,互不干扰)
cd cann-spack-package/environments/cann-8.0.rc2/
spack env activate . && spack install   # → /opt/spack/opt/.../ascend-toolkit-8.0.rc2-xxx/

cd cann-spack-package/environments/cann-8.0.3/
spack env activate . && spack install   # → /opt/spack/opt/.../ascend-toolkit-8.0.3-xxx/

cd cann-spack-package/environments/cann-8.1.beta/
spack env activate . && spack install   # → /opt/spack/opt/.../ascend-toolkit-8.1.beta-xxx/

# 2. 查看已安装版本
spack find ascend-toolkit
# ────────────────────────────────────────────────────
# ascend-toolkit@8.0.rc2  /opt/spack/opt/.../xxxx   ← 训练集群
# ascend-toolkit@8.0.3    /opt/spack/opt/.../yyyy   ← 新算子验证
# ascend-toolkit@8.1.beta /opt/spack/opt/.../zzzz   ← 下一代评测

# 3. 动态切换版本(秒级)
# 当前使用 8.0.rc2(训练任务)
spack load ascend-toolkit@8.0.rc2
python -c "import torch_npu; print(torch_npu.__version__)"  # → 8.0.RC2

# 切换到 8.0.3(测试新算子)
spack unload ascend-toolkit
spack load ascend-toolkit@8.0.3
python -c "import torch_npu; print(torch_npu.__version__)"  # → 8.0.3

# 切换到 8.1.beta(性能评测)
spack unload ascend-toolkit
spack load ascend-toolkit@8.1.beta

自定义 package:第三方库的 CANN 适配

# 为团队内部项目定义 Spack package
# myproject/packages/my-cann-app/package.py

class MyCannApp(CMakePackage):
    """Custom CANN application built against ascend-toolkit"""

    version("1.2.0", sha256="abc...")
    
    # 依赖 CANN 8.0.3(version 约束)
    depends_on("ascend-toolkit@8.0.3", type=("build", "run"))
    
    # 依赖 CANN 提供的算子库
    depends_on("ascend-toolkit-kernels@8.0.3", type="run")
    
    def cmake_args(self):
        cann = self.spec["ascend-toolkit"]
        return [
            f"-DASCEND_HOME={cann.prefix}",
            f"-DASCEND_OPP_PATH={cann.prefix}/opp",
            "-DCMAKE_BUILD_TYPE=Release",
        ]
    
    def setup_run_environment(self, env):
        env.prepend_path("PATH", join_path(self.prefix, "bin"))
        env.prepend_path("LD_LIBRARY_PATH", join_path(self.prefix, "lib"))

踩坑一:Spack concretizer 超时——55 个 warehouse 触发指数爆炸

# ❌ concretizer 同时求解 ascend-toolkit + 55 个算子库的依赖图
# → SAT solver 在 45 分钟内未找到解(组合爆炸)
# → CI timeout

# ✅ 分层 concretize:先求解核心包,再按组添加
# Step 1: 只 concretize 核心
cat > /tmp/core.yaml <<EOF
spack:
  specs: [ascend-toolkit@8.0.3, ascend-hccl@8.0.3]
  concretizer: {unify: true, reuse: true}
EOF
spack -e /tmp/core install

# Step 2: 复用核心 concrete hash → 添加算子库
spack install ascend-toolkit-kernels@8.0.3 ^/hash-of-core-toolkit
# → concretizer 只需求解算子库自身依赖(<1 秒)

踩坑二:spack build 中途失败——30 分钟编译后 COPTFLAGS 冲突

# ❌ ascend-toolkit build 进行到 30 分钟时失败
# Error: COPTFLAGS='-O3 -march=native -mtune=generic' conflicts with bisheng
# → 因为 Spack 全局编译 flag 注入了 -march=native,但毕昇编译器不认识这个 flag

# ✅ package.py 中 filter 掉不兼容的 compiler flag
class AscendToolkit(AscendCannPackage):
    def flag_handler(self, name, flags):
        """
        过滤掉毕昇编译器不兼容的 flag
        """
        if name == "cflags" or name == "cxxflags":
            # 毕昇(基于 LLVM)不认识 -march=native
            # 默认用 -mcpu=tsv110(Ascend 910 的 ARM 核心)
            if self.spec.satisfies("%bisheng"):
                flags = [f for f in flags if "march" not in f]
                flags.append("-mcpu=tsv110")
        return (flags, None, None)  # (cflags, cxxflags, fflags)

踩坑三:同机器上两个 CANN 版本的 Python 绑定冲突——import torch_npu 加载错误的 .so

# ❌ PATH 中残留旧版本的 PYTHONPATH
# export PYTHONPATH=/opt/spack/opt/.../ascend-toolkit-8.0.rc2-xxx/python:$PYTHONPATH
# spack load ascend-toolkit@8.0.3
# → 同时有 8.0.rc2 和 8.0.3 的 python 目录在 PYTHONPATH → 错误加载旧 .so

# ✅ 使用 Spack modulefiles(自动隔离版本)
# module load 比 spack load 更严格的路径管理
spack install --add ascend-toolkit@8.0.3
module use /opt/modules/cann
module load ascend-toolkit-8.0.3

# 验证:所有路径指向同一个 version
which python        # → /opt/spack/.../ascend-toolkit-8.0.3-xxx/python/bin/python
ldd $(python -c "import torch_npu; print(torch_npu.__file__)") \
    | grep ascend
# → libascendcl.so.8.0.3  ← 全部指向 8.0.3,没有 8.0.rc2 残留

cann-spack-package 用 Spack 包管理器实现 CANN 多版本并行安装。package.py 定义版本 + 依赖 + variant + 安装步骤 → spack.yaml 定义完整环境(12 个包 + concretizer 策略)→ spack install 安装到独立 prefix → spack load/unload 秒级切换版本(三版本并行共存:8.0.RC2/8.0.3/8.1.beta)。三个踩坑:concretizer SAT 55 包 45 分钟→分层 concretize 核心+算子库、毕昇编译器不认识 -march=native→filter 改为 -mcpu=tsv110、两版本 PYTHONPATH 残留→modulefiles 代替 script load。

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐