Flutter 引擎 Android SDK 升级实战:从 packages.txt 到 CIPD 多平台包发布
在 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_sdk(platforms/android-<version>)、android_sdk_build_tools、zipalign_path 等路径供各 Android 编译目标引用。因此,一旦 CIPD 包内容缺失(比如少了某个 build-tools 版本),下游构建就会因为找不到 build-tools/<version> 而失败——这正是本文流程中“按平台逐一校验”的必要性来源。
前置条件:环境与 CIPD 写权限校验
在修改 packages.txt 或触发任何 CIPD 上传之前,先确认本地环境:
-
Depot Tools 可用:脚本强依赖 depot_tools 提供的
cipd命令行。create_cipd_packages.sh 第 48–52 行 会直接执行which cipd检查,找不到即报错退出:'cipd' command not found. depot_tools should be on the path. -
写权限校验:本操作需要对
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:校验标签唯一性并运行上传脚本
版本标签规范 使用干净的版本描述符,例如
37v1、37v2。既然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 行)。
脚本执行机制(对照源码)
- 干净工作区:第 99 行 用
mktemp -d -t android_sdkXXXX创建全新的临时目录。注释解释了原因:默认工作目录在包“正在被使用”时往往不会更新/重新下载,临时目录能保证一次干净的 SDK 安装,避免缓存污染。 - 跨平台拉取:第 70 行 定义
platforms=("linux" "macosx" "windows"),循环中通过export REPO_OS_OVERRIDE=$platform(第 108 行)让同一份脚本在 Linux/macOS 主机上也能下载其他平台的 SDK 组件包;循环结束后unset还原环境。 - 许可证打包:第 128–130 行 自动执行
sdkmanager --licenses接受全部 Android SDK 许可,并把licenses目录一并拷入上传目录——引擎构建机上下载的 SDK 因此无需再人工点击许可协议。 - 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-amd64、mac-amd64、mac-arm64、windows-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。全部通过后再修改 DEPS 中 flutter/android/sdk/all/${{platform}} 的 version 字段到新标签(当前仓库锁定为 version:37v2),即可让引擎构建消费到新的 SDK 组件。
故障排查与失败处置
按技能文档给出的四类典型故障逐一对照:
1. 权限被拒(cipd acl-check 或 cipd create 失败)
- 现象:
cipd acl-check报告无任何角色,或上传在中途以授权错误中止。 - 处置:申请
flutter-cipd-writers角色;授权通过后执行cipd auth-login刷新本地凭据再重试。
2. 包解析失败(sdkmanager 下载阶段中止)
- 现象:
create_cipd_packages.sh以Warning: 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 逐行阅读。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00