首页
/ Flutter 引擎 Android SDK 升级实战:从 packages.txt 到 CIPD 多平台包发布

Flutter 引擎 Android SDK 升级实战:从 packages.txt 到 CIPD 多平台包发布

2026-09-05 11:18:26作者:韦蓉瑛

在 Flutter 引擎的 Android 构建体系中,SDK 组件(platforms、build-tools、NDK 等)并非在构建机现场从网络拉取,而是由维护者预先打包并上传到 CIPD(Chrome Infrastructure Package Deployment),再由 DEPS 按版本标签锁定分发。本文以仓库内的技能文档 .agents/skills/updating-android-sdk/SKILL.md 为主线,完整讲解如何把 Flutter 的 Android SDK 依赖升级到新的 Android API 版本(或 Preview/Canary 版本):配置 packages.txt、校验 CIPD 版本标签唯一性、运行 create_cipd_packages.sh 完成 Linux/macOS/Windows 跨平台打包上传,并逐条对照脚本源码验证其执行机制,最后给出常见故障的排查方案。

背景:上传的 SDK 包在引擎构建中如何被消费

在开始操作前,先明确这些 CIPD 包的消费方,这样才能理解“为什么要保证包完整、标签准确”。

仓库根目录的 DEPS 中,引擎将 flutter/android/sdk/all/${{platform}} 这个 CIPD 包固定到 version:37v2,并解压到 engine/src/flutter/third_party/android_tools 目录(受 download_android_deps 条件控制):

'engine/src/flutter/third_party/android_tools': {
   'packages': [
     {
      'package': 'flutter/android/sdk/all/${{platform}}',
      'version': 'version:37v2'
     }
   ],
   'condition': 'download_android_deps',
   'dep_type': 'cipd',
 },

GN 构建配置 engine/src/build/config/android/config.gni 则直接以该目录作为 SDK 根:default_android_sdk_root 默认为 //flutter/third_party/android_tools/sdk,并在 第 53–69 行 拼出 android_sdkplatforms/android-<version>)、android_sdk_build_toolszipalign_path 等路径供各 Android 编译目标引用。因此,一旦 CIPD 包内容缺失(比如少了某个 build-tools 版本),下游构建就会因为找不到 build-tools/<version> 而失败——这正是本文流程中“按平台逐一校验”的必要性来源。

前置条件:环境与 CIPD 写权限校验

在修改 packages.txt 或触发任何 CIPD 上传之前,先确认本地环境:

  1. Depot Tools 可用:脚本强依赖 depot_tools 提供的 cipd 命令行。create_cipd_packages.sh 第 48–52 行 会直接执行 which cipd 检查,找不到即报错退出:

    'cipd' command not found. depot_tools should be on the path.
    
  2. 写权限校验:本操作需要对 flutter/android/sdk/all/ 前缀拥有写权限,继续执行前先验证:

    cipd acl-check flutter/android/sdk/all/ -writer
    

    脚本自身的使用说明(print_usage)同样提示:“To confirm you have write permissions run cipd acl-check flutter/android/sdk/all/ -writer.”。若校验失败,需申请 flutter-cipd-writers 角色,然后通过 cipd auth-login 完成认证后再重试。

Step 1:配置目标 SDK 组件(packages.txt)

create_cipd_packages.sh 从同目录下的 packages.txt 读取要打包的组件与版本。

  • 位置engine/src/flutter/tools/android_sdk/packages.txt
  • 格式<package_name>:<subdirectory_to_upload>,其中包名部分用逗号分隔多个包,上传目录部分用 : 分隔(支持多个目录上传)。

脚本对这一格式的实际解析逻辑见 第 111–126 行:先按 : 拆成包名列表与上传目录列表,再按 , 拆出逐个包名,依次调用 sdkmanager --sdk_root=<临时目录> <包名> 安装,然后把指定目录 cp -a 拷贝到上传目录。因此 packages.txt 中写错的包名或目录名都会在下载/拷贝阶段暴露。

当前仓库中的实际内容如下(可见它已包含 Android 37.0 预览版平台与多版本 build-tools):

platforms;android-37.0,platforms;android-36,platforms;android-35,platforms;android-34:platforms
cmdline-tools;latest:cmdline-tools
build-tools;37.0.0,build-tools;36.1.0,build-tools;36.0.0,build-tools;35.0.0,build-tools;34.0.0,build-tools;33.0.1:build-tools
platform-tools:platform-tools
cmake;3.22.1:cmake
ndk;28.2.13676358:ndk

查询官方包标识符

升级时不要凭记忆写包名,务必先查询 Android SDK 仓库中真实发布的标识符:

sdkmanager --list --include_obsolete

脚本内置了等价便捷入口(第 91–94 行):

./create_cipd_packages.sh list

重要:Canary / Preview 与 Stable 的 API 级命名差异 当接入预览版或 Canary 版 Android(例如 Android 37 / Cinnamon Bun)时,sdkmanager 经常将平台组件发布为带 .0 后缀的标识符(如 platforms;android-37.0)。不要自行假设或强制使用整型 API 级字符串(platforms;android-37)——如果 sdkmanager 明确要求 platforms;android-37.0,写错包字符串会导致后续下载直接失败。当前仓库 packages.txt 第 1 行 保留的正是 platforms;android-37.0 这种带后缀的写法,可以作为佐证。

packages.txt 升级示例

以新增 Android 37 平台与 build-tools 为例,目标写法如下:

platforms;android-37.0,platforms;android-36,platforms;android-35,platforms;android-34:platforms
cmdline-tools;latest:cmdline-tools
build-tools;37.0.0,build-tools;36.1.0,build-tools;36.0.0,build-tools;35.0.0:build-tools
platform-tools:platform-tools
tools:tools
cmake;3.22.1:cmake
ndk;28.2.13676358:ndk

升级时通常同时保留若干历史 API 级与 build-tools 版本,以兼容仍指向旧版本的下游构建;config.gni 中当前默认使用的 default_android_sdk_version = "36"default_android_sdk_build_tools_version = "36.1.0"engine/src/build/config/android/config.gni 第 10–11 行)都要求对应组件必须包含在新上传的包内。

Step 2:校验标签唯一性并运行上传脚本

版本标签规范 使用干净的版本描述符,例如 37v137v2。既然 create_cipd_packages.sh 已是标准上传管线,不要再附加 unmodified 这类历史猜测性后缀。脚本对标签还有硬性约束:第 41–46 行 只接受小写字母与数字(^[[:lower:][:digit:]]+$),否则直接拒绝执行。

CIPD 的 tag 与 ref 是不可变的。执行脚本前,先确认拟用的版本标签(如 37v1尚未被注册

cipd describe flutter/android/sdk/all/mac-arm64 -version version:<VERSION_TAG>
  • 标签未被使用:命令以退出码 1 返回 Error: no such tag.,可以继续;
  • 标签已存在:命令会输出已有的 Package:Instance ID:,此时必须换一个全新的唯一标签。

确认唯一后执行:

cd engine/src/flutter/tools/android_sdk
./create_cipd_packages.sh <VERSION_TAG> <PATH_TO_LOCAL_SDK>

第二个参数可省略,脚本默认回退到 ANDROID_SDK_ROOT 环境变量(第 54 行)。脚本还会校验该目录存在且包含 cmdline-tools第 56–68 行),并优先使用 cmdline-tools/latest/bin/sdkmanager,找不到时再在 cmdline-tools 下逐个 find第 73–88 行)。

脚本执行机制(对照源码)

  1. 干净工作区第 99 行mktemp -d -t android_sdkXXXX 创建全新的临时目录。注释解释了原因:默认工作目录在包“正在被使用”时往往不会更新/重新下载,临时目录能保证一次干净的 SDK 安装,避免缓存污染。
  2. 跨平台拉取第 70 行 定义 platforms=("linux" "macosx" "windows"),循环中通过 export REPO_OS_OVERRIDE=$platform第 108 行)让同一份脚本在 Linux/macOS 主机上也能下载其他平台的 SDK 组件包;循环结束后 unset 还原环境。
  3. 许可证打包第 128–130 行 自动执行 sdkmanager --licenses 接受全部 Android SDK 许可,并把 licenses 目录一并拷入上传目录——引擎构建机上下载的 SDK 因此无需再人工点击许可协议。
  4. CIPD 创建第 137–145 行 调用 cipd create,以 -install-mode copy 将包拷贝并打上 version:<标签> 标签、绑定同名 ref 到 flutter/android/sdk/all/<cipd_name>。其中 cipd_name 的命名规则是:Linux/Windows 用 <platform>-amd64,macOS 因 gn 平台名为 mac 而写成 mac-amd64/mac-arm64第 138–142 行),且 macOS 额外上传 arm64 版本以支持 M1 机型(第 132–136 行)。

按上述逻辑,一次完整运行总共会注册 4 个 CIPD 包实例:linux-amd64mac-amd64mac-arm64windows-amd64——这也解释了后续验证为何需要逐一检查这 4 个目标。

Step 3:验证 CIPD 上传与标签绑定

上传完成后、滚动引擎依赖(roll DEPS 中的 version 字段)之前,确认所有架构目标都已成功注册:

# macOS Apple Silicon (arm64)
cipd describe flutter/android/sdk/all/mac-arm64 -version version:<VERSION_TAG>

# macOS Intel (amd64)
cipd describe flutter/android/sdk/all/mac-amd64 -version version:<VERSION_TAG>

# Linux (amd64)
cipd describe flutter/android/sdk/all/linux-amd64 -version version:<VERSION_TAG>

# Windows (amd64)
cipd describe flutter/android/sdk/all/windows-amd64 -version version:<VERSION_TAG>

确保每条命令的输出都显示有效的 Instance ID,并确认所请求的 tag 已绑定到对应 ref。全部通过后再修改 DEPSflutter/android/sdk/all/${{platform}}version 字段到新标签(当前仓库锁定为 version:37v2),即可让引擎构建消费到新的 SDK 组件。

故障排查与失败处置

按技能文档给出的四类典型故障逐一对照:

1. 权限被拒(cipd acl-checkcipd create 失败)

  • 现象cipd acl-check 报告无任何角色,或上传在中途以授权错误中止。
  • 处置:申请 flutter-cipd-writers 角色;授权通过后执行 cipd auth-login 刷新本地凭据再重试。

2. 包解析失败(sdkmanager 下载阶段中止)

  • 现象create_cipd_packages.shWarning: Failed to find package '<package_name>' 退出。
  • 处置:不要猜测或强加整型命名规则(如 platforms;android-37)。运行 sdkmanager --list --include_obsolete 查看远端真实发布的包标识,然后修正 packages.txt 中的包字符串——例如保留 platforms;android-37.0 这类带 .0 的预览后缀。

3. 上传中断与标签废弃

  • 现象:网络中断导致多平台上传进行到一半停止,或误上传了不正确的包内容。
  • 处置:CIPD 上传是最终性的,无法覆盖。若某标签被部分上传或需要废弃,应按内部 LuCI Playbook 中“删除重复 CIPD 标签”的流程处理;在失败后重试上传时,必须选择新的唯一版本标签(例如从 v1 提升到 v2)。

4. 缺少命令行工具

  • 现象:脚本输出 SDK directory does not contain cmdline-tools
  • 处置:确认传入路径指向包含 cmdline-tools/latest/bin/sdkmanager 的有效 Android SDK 根目录(macOS 上通常是 ~/Library/Android/sdk)。该检查对应脚本 第 63–68 行cmdline-tools 目录存在性校验。

小结

这条“packages.txt → 标签唯一性校验 → create_cipd_packages.sh → 四平台 describe 验证 → 滚动 DEPS”的流水线,把 Android SDK 升级从一次性的手工同步变成了可重复、可验证的标准化操作。其关键纪律在于:包名以 sdkmanager --list 的真实输出为准(尤其是预览版的 .0 后缀)、版本标签不可复用、上传结果必须四个平台逐一确认——因为下游 DEPS 锁定的 flutter/android/sdk/all/<platform> 包是引擎 Android 构建唯一的 SDK 来源(engine/src/build/config/android/config.gni)。完整操作细节可对照仓库内技能文档 .agents/skills/updating-android-sdk/SKILL.md 与脚本 create_cipd_packages.sh 逐行阅读。

登录后查看全文
热门项目推荐
相关项目推荐