3、West 基础概念:West 的工作区结构、manifest 文件、项目仓库与顶层目录的作用

West(Zephyr's meta-tool)是 Zephyr RTOS 生态中用于多仓库管理与构建调度的核心工具。它并非简单的包管理器,而是一套基于 Git 仓库拓扑的"工作区编排系统"。理解 West 的工作区结构、manifest 文件以及项目仓库与顶层目录的职责边界,是掌握 Zephyr 开发流程的基石。本章将深入剖析这三个核心概念,帮助你在实际项目中正确组织代码、管理依赖并规避常见的多仓库协作陷阱。

West 工作区拓扑与 manifest 驱动关系 West 工作区根目录(例如:~/zephyrproject) .west/ 目录(内部管理) config(本地配置) topdir(指向根目录) manifest.path(指向 manifest 仓库) manifest.file(默认 west.yml) remote 缓存与状态 manifest 仓库(如 zephyr) west.yml(manifest 文件) 定义 projects 列表 定义 remote 与 revision 定义 group-filter 与自更新 可包含子 manifest(west-*) 项目仓库(Projects)—— 由 west update 拉取 zephyr 主仓库(若作为项目) 路径:./zephyr mcuboot 引导加载程序 路径:./bootloader/mcuboot hal_stm32 硬件抽象层 路径:./modules/hal/stm32 cmsis CMSIS 核心 路径:./modules/cmsis west update 依据 manifest 拉取 指向 west update 可更新 manifest 自身 内部状态 manifest 仓库 项目仓库

3.1 West 工作区结构:从顶层目录到内部状态

核心原理:West 工作区(Workspace)是一个以顶层目录为边界的 Git 多仓库集合。顶层目录是所有项目仓库的物理容器,也是 West 命令的执行上下文。当你在终端中运行 west build 或 west update 时,West 会从当前目录向上查找 .west 目录,从而确定工作区的根位置。这个机制保证了无论你在工作区内的哪个子目录执行命令,West 都能正确定位到全局配置。

工作区结构的关键在于 .west/ 目录,它存储了 West 自身的状态信息,而非用户代码。该目录由 west init 命令创建,包含以下核心文件:

  • config 文件:记录工作区的关键路径配置,包括 topdir(顶层目录路径)、manifest.path(manifest 仓库的相对路径)以及 manifest.file(默认指向 west.yml)。
  • 状态缓存:West 会缓存已解析的 manifest 数据,以加速后续命令的执行。
  • 临时文件:用于存储更新过程中的锁文件或中间状态。

操作流程:初始化一个标准工作区的典型命令序列如下:

# 创建并进入工作区根目录
mkdir ~/zephyrproject && cd ~/zephyrproject

# 使用官方 manifest 仓库初始化(默认拉取 zephyr 仓库)
west init

# 拉取 manifest 中声明的所有项目仓库
west update

执行 west init 后,目录结构如下:

~/zephyrproject/
├── .west/               # West 内部状态(勿手动修改)
│   ├── config
│   └── ...
├── zephyr/              # manifest 仓库(默认是 zephyr 主仓库)
│   ├── west.yml
│   ├── CMakeLists.txt
│   └── ...
├── bootloader/
│   └── mcuboot/         # 项目仓库
├── modules/
│   ├── hal/
│   │   └── stm32/       # 项目仓库
│   └── ...
└── tools/               # 其他项目仓库
经验提醒:不要将 .west/ 目录纳入版本控制(应加入 .gitignore)。该目录是机器相关的,包含路径,不同开发者的路径可能不同。同时,避免手动编辑 .west/config 文件,所有配置修改应通过 west config 命令完成。

3.2 Manifest 文件:工作区的"宪法"

核心原理:Manifest 文件(默认名为 west.yml)是 West 工作区的核心声明文件,它定义了工作区中所有项目仓库的来源、版本和分组。Manifest 本身位于一个 Git 仓库中(即 manifest 仓库),这个仓库可以是 Zephyr 主仓库,也可以是你自己维护的独立仓库。Manifest 文件采用 YAML 格式,主要包含以下顶层键:

键名 作用 必填
manifest: Manifest 内容的根节点,包含 projects、self 等子键 是
projects: 项目仓库列表,每个项目需指定 name、url、revision 是
remotes: 定义远程仓库的别名,供 projects 引用 否(可直接在 project 中写 url)
group-filter: 启用或禁用项目组,用于选择性拉取 否
self: 描述 manifest 仓库自身的行为(如是否允许 west update 更新自身) 否

一个典型的 west.yml 片段如下:

manifest:
  remotes:
    - name: zephyrproject
      url-base: https://github.com/zephyrproject-rtos
  projects:
    - name: zephyr
      remote: zephyrproject
      revision: v3.7.0
      path: zephyr
    - name: mcuboot
      remote: zephyrproject
      revision: v2.1.0
      path: bootloader/mcuboot
    - name: hal_stm32
      remote: zephyrproject
      revision: 0a1b2c3d4e5f
      path: modules/hal/stm32
      groups:
        - hal
  self:
    path: zephyr
    west-commands: scripts/west-commands.yml

关键字段解析:

  • revision:可以是 Git 分支名、标签或 commit SHA。建议使用标签或固定 SHA 以保证构建可重复性。
  • path:项目在工作区中的相对路径。若不指定,默认使用项目名作为路径。
  • groups:将项目分组,配合 group-filter 实现选择性拉取。例如,group-filter: [-hal] 会排除所有 hal 组的项目。
  • self.path:指定 manifest 仓库自身在工作区中的路径。若省略,West 会尝试自动推断。
风险提示:当 revision 指向一个移动分支(如 main)时,不同开发者执行 west update 可能拉取到不同版本的代码,导致"在我机器上能编译"的问题。对于正式发布或团队协作,务必使用标签或固定 commit SHA。

3.3 项目仓库与顶层目录的职责边界

核心原理:项目仓库(Projects)是实际包含源代码、构建脚本和配置的 Git 仓库。它们被 west update 命令根据 manifest 文件拉取到工作区的指定路径。顶层目录则扮演"容器"和"上下文"的角色——它本身不是一个 Git 仓库(除非你手动初始化),而是多个独立 Git 仓库的集合点。

这种设计的核心优势在于关注点分离:

  • 顶层目录:只负责提供统一的文件系统命名空间,让所有项目共存于一个可预测的路径下。它不追踪任何代码变更。
  • 项目仓库:各自维护独立的 Git 历史,可以自由切换分支、提交代码、与远程同步,互不干扰。
  • Manifest 仓库:作为"控制平面",记录所有项目仓库的期望状态(版本、路径、分组),但不直接包含项目代码。

操作流程:当需要添加一个新的依赖仓库时,流程如下:

  1. 编辑 manifest 仓库中的 west.yml,在 projects 列表中添加新项目。
  2. 提交并推送 manifest 仓库的变更。
  3. 在工作区中执行 west update,West 会拉取新项目并更新现有项目到 manifest 指定的 revision。

案例拆解:假设你正在开发一个基于 STM32U5 的物联网设备,需要添加一个自定义的传感器驱动库 my_sensor_driver。该库托管在公司的 GitLab 上。

初始状态:工作区已初始化,包含 zephyr、mcuboot、hal_stm32 等标准项目。你的 west.yml 中 projects 列表已有 10 个项目。

操作步骤:

# 1. 编辑 manifest 仓库中的 west.yml
cd ~/zephyrproject/zephyr
# 在 projects 列表末尾添加:
#   - name: my_sensor_driver
#     remote: mycompany
#     revision: v1.2.0
#     path: modules/lib/my_sensor_driver
# 并在 remotes 中添加:
#   - name: mycompany
#     url-base: https://gitlab.mycompany.com/iot

# 2. 提交并推送
git add west.yml
git commit -m "Add my_sensor_driver dependency"
git push origin main

# 3. 更新工作区
cd ~/zephyrproject
west update

结果验证:执行 west update 后,~/zephyrproject/modules/lib/my_sensor_driver 目录被创建,且其 Git HEAD 指向 v1.2.0 标签。此时,你可以在 Zephyr 的 CMake 配置中通过 find_package 或直接添加路径来引用该库。

复盘与代价:这种方式的优点是依赖关系清晰、版本可控。但代价是:每次修改 manifest 后,所有开发者都需要执行 west update 才能同步。如果团队规模大,建议在 CI 中预先验证 manifest 变更,避免将错误的 revision 推送到共享分支。

3.4 常见误区与最佳实践

误区一:将顶层目录当作 Git 仓库管理。有些开发者会在 ~/zephyrproject 下执行 git init,试图将所有项目纳入单一版本控制。这会导致严重的冲突,因为每个子项目已经是独立的 Git 仓库。正确做法是:顶层目录仅作为工作区容器,不进行版本控制。

误区二:手动修改项目仓库的 Git 状态后执行 west update。如果某个项目仓库有未提交的本地修改,west update 可能会因为 checkout 冲突而失败。West 会尝试将项目切换到 manifest 指定的 revision,若本地有未提交变更,操作会被中断。此时应使用 git stash 或提交本地修改后再执行更新。

误区三:忽略 group-filter 的作用。对于大型项目,拉取所有仓库可能耗时且占用磁盘空间。通过合理使用 groups 和 group-filter,可以只拉取当前开发所需的子集。例如,开发 Zephyr 应用时不需要拉取所有 HAL 仓库,可以排除 hal 组。

最佳实践清单:
  • 始终使用 west init 和 west update 管理工作区,不要手动 clone 项目仓库。
  • 在 west.yml 中使用固定 revision(标签或 SHA),避免使用移动分支。
  • 定期执行 west update 保持工作区与 manifest 同步,但更新前先检查本地是否有未提交修改。
  • 为 manifest 仓库单独创建分支进行变更测试,通过 PR/MR 合入主干。
  • 利用 west topdir 命令快速获取工作区根路径,在脚本中避免硬编码路径。

总结复盘:West 工作区结构的设计哲学是"声明式依赖管理"。Manifest 文件是唯一的真相来源(Single Source of Truth),它决定了工作区的内容和状态。顶层目录提供物理隔离,项目仓库保持 Git 独立性,而 .west/ 目录则作为 West 自身的运行状态存储。理解这三者的关系,你就能高效地管理 Zephyr 开发环境,无论是个人项目还是团队协作,都能保持构建的一致性和可重复性。在实际操作中,遇到任何与仓库状态相关的问题,首先检查 west.yml 的配置和 west status 命令的输出,这能帮助你快速定位问题根源。