Zephyr学习记录(2):west 工具与 Manifest

成元 2026-04-30 { 教程, 笔记 } [ Zephyr ]

前言

上一篇把环境安装和点灯简单过了一遍,这一篇继续补 Zephyr 开发里一个绕不开的话题,也就是 westmanifest

刚开始接触 Zephyr 的时候,很容易把它们看成“又一个构建命令”和“又一个配置文件”。但实际用下来会发现,它们更像是 Zephyr 工作流的骨架。

如果不理解这两个东西,后面在拉取源码、管理依赖、切换版本、构建用户工程的时候就很容易迷糊。

west 是什么

官方文档把 west 描述为 Zephyr 的一个 meta-tool,也就是“元工具”。

我的理解是:它不是单纯替代 cmakeninja 的命令,而是站在更高一层,把 多仓库管理、工程构建、烧录、调试 这些事情统一到一个命令行入口中。

也就是说,在 Zephyr 的日常开发中,west 主要负责下面几类事情:

它的基本调用形式和 git 很像:

west [common-opts] <command> [opts] <args>

比如:

west init
west update
west build -b stm32_min_dev@blue samples/basic/blinky
west flash

west 常见指令与作用

west 的命令不少,不过日常最常碰到的其实就是下面这些。

west init

作用是 初始化 west 工作空间

最常见的情况,就是先创建一个工作区,把 manifest 仓库放进去:

west init ~/zephyrproject

执行之后,west 会创建工作空间根目录,并生成 .west/ 配置目录。

如果使用官方默认仓库,通常会得到类似下面这样的结构:

zephyrproject/
├── .west/
│   └── config
└── zephyr/
    └── west.yml

这里先只有工作空间和 manifest 仓库,真正的其它模块仓库还要靠 west update 去拉。

west update

作用是 根据 manifest 下载或更新项目依赖

west update

这个命令会读取 west.yml 中的项目列表,然后逐个执行下面这些动作:

所以从直觉上看,west init 是“搭骨架”,west update 是“把肉补齐”。

west list

作用是 列出当前工作空间中由 west 管理的项目

west list

这个命令很适合在你忘了工作空间里到底有哪些模块时快速确认。

west status

作用是 查看各个仓库的状态

west status

它有点像对整个工作空间批量执行 git status

west diff

作用是 查看各个仓库的本地修改

west diff

这个命令在你改了多个模块,又忘了自己动过哪些地方时很好用。

west forall

作用是 在多个项目仓库里批量执行命令

west forall -c "git status --short"

这类命令适合做统一检查,或者批量跑一些 Git 命令。

west manifest

作用是 管理和检查 manifest

例如:

west manifest --resolve
west manifest --freeze

其中:

west config

作用是 读取或设置 west 配置项

west config -l
west config manifest.path zephyr

例如 manifest.pathmanifest.file 这些配置都会影响 west 去哪里找 manifest。

west topdir

作用是 输出当前工作空间根目录

west topdir

在脚本里这个命令很方便,因为它能让你快速确认自己是不是处于某个 west workspace 之中。

west build

作用是 构建 Zephyr 工程

west build -b stm32_min_dev@blue samples/basic/blinky

它本质上是在帮你组织 CMake/Ninja 相关参数,所以用户一般不需要直接手写一长串底层构建命令。

west flash

作用是 把构建产物烧录到目标板

west flash

前提当然是工程已经成功构建,并且对应板卡的 runner 配置可用。

west debug

作用是 启动调试流程

west debug

这个命令会结合目标板支持的调试方式,把 GDB、OpenOCD、J-Link 之类的流程串起来。

其它说明

除了上面这些,west 还有 comparegrephelp 等命令。

另外,一些文章里还会提到 west attachwest boards 这类命令。它们通常属于 Zephyr 提供的扩展命令,是否可用与具体环境、版本和扩展命令集有关。实际使用时最稳妥的方法还是直接执行:

west --help
west <command> -h

manifest 是什么

manifest 可以理解成 west 工作空间的总清单

它告诉 west

在 Zephyr 里,manifest 文件通常就叫 west.yml

所以你也可以简单把它理解为:
west.yml 决定工作空间长什么样,west update 负责把这个样子真正落到本地。

manifest 的基本格式

manifest 本质上是一个 YAML 文件,顶层一般是 manifest:

不过如果只看那种“教学版极简例子”,实际遇到真正的 west.yml 时还是容易发懵。

所以这里我直接结合一个真实的上游 Zephyr west.yml 来理解。它开头的核心结构大致可以概括成下面这样:

manifest:
  defaults:
    remote: upstream

  remotes:
    - name: upstream
      url-base: https://github.com/zephyrproject-rtos
    - name: babblesim
      url-base: https://github.com/BabbleSim

  group-filter: [-babblesim, -optional, -testing]

  projects:
    - name: acpica
      revision: 8d24867bc9c9d81c81eeac59391cda59333affd4
      path: modules/lib/acpica

    - name: hal_espressif
      revision: 15789ec576bdb3397c6fc39542b0d9b534d98d2d
      path: modules/hal/espressif
      west-commands: west/west-commands.yml
      groups:
        - hal

    - name: babblesim_base
      remote: babblesim
      repo-path: base
      path: tools/bsim/components
      revision: 1cce925c72805cfdbb6341ca8c0755c7d42699d3
      groups:
        - babblesim

  self:
    path: zephyr
    west-commands: scripts/west-commands.yml
    import: submanifests

这个例子比“教学版”更接近真实情况,因为它已经把 Zephyr 实际维护多个仓库、多个分组、扩展 west 命令的方式体现出来了。

上面这些字段可以这样理解。

remotes

定义远程仓库地址前缀。

比如:

remotes:
  - name: upstream
    url-base: https://github.com/zephyrproject-rtos
  - name: babblesim
    url-base: https://github.com/BabbleSim

这表示当前 manifest 里至少定义了两个远程来源:

后面 projects 中如果写了 remote: babblesim,那就表示这个项目默认从 https://github.com/BabbleSim 下面去找。

defaults

定义默认值。

在这个真实示例里写的是:

defaults:
  remote: upstream

意思是:如果某个项目没有单独写 remote,那就默认从 upstream 这个远程来源拉。

所以像下面这种项目:

- name: acpica
  revision: 8d24867bc9c9d81c81eeac59391cda59333affd4
  path: modules/lib/acpica

虽然没有显式写 remote: upstream,但它依然会继承 defaults 的设置。

group-filter

这个字段在很多入门文章里容易被忽略,但在真实工程里很有用。

这个示例中是:

group-filter: [-babblesim, -optional, -testing]

它的意思可以粗略理解成:
默认不启用 babblesimoptionaltesting 这些组里的项目。

这样做的好处是:

所以 group-filter 本质上是在控制“哪些项目默认参与当前工作空间”。

projects

这是最核心的部分,用来列出需要管理的项目仓库。

常见字段有:

例如这个示例里有一个很简单的项目:

- name: acpica
  revision: 8d24867bc9c9d81c81eeac59391cda59333affd4
  path: modules/lib/acpica

这表示:

这里的 revision 不是 main,而是一个具体 commit id。
这说明 Zephyr 更关注 可复现性,也就是希望大家同步下来的是一组确定版本的依赖,而不是每次都跟着分支头乱跑。

再看一个更复杂一点的例子:

- name: babblesim_base
  remote: babblesim
  repo-path: base
  path: tools/bsim/components
  revision: 1cce925c72805cfdbb6341ca8c0755c7d42699d3
  groups:
    - babblesim

这段可以这样理解:

这里的 repo-path 很关键。
如果没有它,west 往往会默认用 name 去拼远程仓库地址;而有了它,就说明“显示名”和“远程仓库名”不一定相同。

再看一个和 west 扩展命令相关的例子:

- name: hal_espressif
  revision: 15789ec576bdb3397c6fc39542b0d9b534d98d2d
  path: modules/hal/espressif
  west-commands: west/west-commands.yml
  groups:
    - hal

这说明 hal_espressif 这个项目除了提供代码,还额外提供了一份 west 命令描述文件。
也就是说,某些 west 命令并不一定全部内建在主程序里,也可能由某个项目通过 west-commands.yml 注册进来。

self

self 描述的是 manifest 仓库自己

这个示例末尾这一段很典型:

self:
  path: zephyr
  west-commands: scripts/west-commands.yml
  import: submanifests

其中:

这一点非常重要,因为它解释了为什么你在实际环境里能用到 west boardswest attachwest sdk 这类命令。

查看对应的 scripts/west-commands.yml 就会发现,Zephyr 在里面注册了很多扩展命令,比如:

所以更准确地说:

这样一来,west 才会真正变成一个覆盖构建、烧录、调试、板卡查询等工作流的统一入口

version

可选字段,用于声明 manifest 文件格式所依赖的 west 版本特性。

例如:

version: "0.10"

它的作用更偏向兼容性检查,一般在简单入门阶段不一定马上用到。

如何理解一个真实的 Zephyr west.yml

如果只想抓住主线,我觉得可以按下面这个顺序去读:

先看 self

先确认 manifest 仓库自己是谁、放在哪里。

在这个示例里:

self:
  path: zephyr

这说明当前这个 west.yml 就属于 zephyr 仓库本身。
也就是说,你现在用的是 Zephyr 官方默认工作流,而不是“我自己的 app 仓库作为 manifest 仓库”的那种玩法。

再看 defaults 和 remotes

这两块决定“默认从哪里拉仓库”。

在这个示例里,默认 remote 是 upstream,而 upstream 对应的是 https://github.com/zephyrproject-rtos

所以大部分没特别声明来源的模块,本质上都是从 Zephyr 官方维护的仓库组织里同步下来的。

再看 group-filter

这决定“默认启用哪些依赖,默认跳过哪些依赖”。

你这里把 babblesimoptionaltesting 默认排除了,所以这份工作空间更偏向普通开发使用,而不是一次性把测试和仿真生态全量拉满。

最后看 projects

这里决定“这个工作空间究竟包含哪些具体模块”。

你会发现里面有很多仓库路径:

这就说明 Zephyr 并不是“一个大仓库包打天下”,而是通过 manifest 把 HAL、库、工具、测试相关仓库组织成一个统一工作空间。

所以当我们执行 west update 时,本质上不是“更新 Zephyr 一个仓库”,而是:

这样理解以后,再去看工作空间目录结构就不会觉得乱了,因为这些目录并不是随便摆出来的,而是 manifest 明确规定好的。

工作空间、west、manifest、用户项目之间的关系

这一块其实是最容易绕晕的。

我自己的理解可以总结成一句话:

工作空间是容器,manifest 是清单,west 是管理者,用户项目是这个容器中的一个实际工程。

可以先看一个简化关系图:

west workspace/
├── .west/                 <- west 的工作空间配置
├── manifest repo/         <- 保存 west.yml 的仓库
├── zephyr/                <- Zephyr 内核源码
├── modules/...            <- 各类模块仓库
├── bootloader/...         <- 例如 mcuboot
└── app/                   <- 用户自己的应用工程

它们之间可以这样理解。

工作空间(workspace)

工作空间就是最外层的目录。

它把 .west/、manifest 仓库、Zephyr 源码、模块仓库、用户应用工程都收纳到一起。

只要一个目录里有 .west/west 就能把它识别成自己的工作空间。

west

west 是操作这个工作空间的工具。

它不负责“发明”依赖关系,而是 读取 manifest,再去操作 workspace 里的各个仓库

所以可以理解成:

manifest

manifest 是工作空间的结构说明书。

它描述了项目仓库集合以及它们的版本关系。

当你执行 west update 时,west 就按照这个说明书把每个仓库拉到指定位置、指定 revision。

用户项目

用户项目就是你自己写业务代码、应用代码的地方。

它通常也是工作空间中的一个目录,可以是:

你平时最常做的事情,一般就是在用户项目里写代码,然后让 west build 去结合工作空间中的 Zephyr 和各模块一起完成构建。

一个更贴近实际的理解

如果把它们类比成一次做饭:

没有工作空间,东西没地方放。
没有 manifest,west 不知道该拿哪些仓库。
没有 west,你就得自己手动 clone 一堆仓库、对齐版本、处理路径。
没有用户项目,那这些依赖拉下来也只是“环境准备好了”,还没有真正的应用要构建。

小结

学 Zephyr 的早期阶段,westmanifest 基本可以先抓住下面这几个点:

把这套关系想明白之后,后面再看 Zephyr 的模块组织、版本管理和自定义工程结构,就会顺很多。

参考资料