ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Dart SDK pkg/ 包发布校验机制全解析:pubspec 验证工具与 CI 实践指南

2026/9/23 17:48:30 拓冰建站 浏览量
Dart SDK pkg/ 包发布校验机制全解析:pubspec 验证工具与 CI 实践指南 Dart SDK pkg/ 包发布校验机制全解析pubspec 验证工具与 CI 实践指南【免费下载链接】sdkThe Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.项目地址: https://gitcode.com/gh_mirrors/sdk1/sdk导读本文以 Dart SDK 仓库中pkg/目录的包校验机制为切入点系统讲解 SDK 内部对pkg/下数百个 Dart 包的自动验证流程。你将掌握dart tools/package_deps/bin/package_deps.dart这一核心校验工具的运行方式、publish_to: none标记的语义、发布包与未发布包的差异化校验规则以及新增包和测试包的标准操作流程。文中所有结论均对应 pkg/README.md 文档及 package_deps.dart 源码实现。背景为什么 SDK 仓库需要对 pkg/ 包做专项校验Dart SDK 采用单仓库monorepo模式pkg/目录下汇集了analyzer、analysis_server、compiler、dart2js_info、async_helper等大量 Dart 包它们共同支撑着 SDK 的构建、分析与测试体系。这些包与普通 pub 生态包存在一个关键差异开发期间它们并不通过 pub 工具解析依赖而是从仓库根目录的 DEPS 文件中获取依赖版本。文档明确指出Its very easy for the dependencies specified in a packages pubspec file to get out of date wrt the packages and versions actually used.pubspec 中声明的依赖很容易与实际使用的包和版本脱节。正因如此SDK 在 LUCI CI 机器人上对所有pkg/包执行自动校验核心校验逻辑由tools/package_deps包承担。这也是 Package validation 一节的立意所在。本地运行校验工具基本命令文档给出的校验命令非常直接在仓库根目录执行dart tools/package_deps/bin/package_deps.dart从 入口文件 的源码可以看到工具启动时首先做两项前置检查当前目录下必须存在DEPS文件且存在pkg目录否则输出Please run this tool from the root of the Dart repo.并以退出码 1 结束支持-v/--verbose参数第 16 行开启后会在校验时打印每个依赖解析到的具体版本。工具执行流程工具会扫描pkg/下所有包含pubspec.yaml的子目录通过_hasPubspec判断见 第 85-86 行逐个构建Package对象并调用validate()完成校验。校验结束后若有任一包失败进程退出码置为 1第 80-82 行方便 CI 直接依据退出码判定构建是否通过。依赖来源解析SdkDeps类第 422-503 行负责解析 SDK 内部真实可用的依赖版本从 DEPS 文件中以正则/third_party/pkg/(\S)提取第三方包清单递归扫描pkg/、third_party/devtools、third_party/pkg三个目录下的pubspec.yaml读取每个包的name与version字段建立包名 → 版本映射表。这意味着校验的基准并非 pub.dev 上的最新版本而是仓库当前实际检入的依赖版本从而保证声明与实现严格一致。依赖声明与实际使用的双向一致性校验这是校验工具的核心逻辑实现在 Package._validatePubspecDeps。工具通过_parseImports第 152-180 行扫描包内全部.dart文件用正则提取import/export语句中的package:URI 前缀得到实际使用的依赖集合再与 pubspec 中dependencies:、dev_dependencies:声明的集合做比对主要检查四类问题使用了但未声明lib/中引用的包未列入dependencies:或测试/工具目录中引用的包未列入dev_dependencies:声明了但未使用dependencies:或dev_dependencies:中声明的包在源码中从未被导入依赖放错位置只在开发目录中使用的包被错误声明到了dependencies:misplacedDeps检查第 247-253 行包名与目录名不一致pubspec 的name字段必须与所在目录名相同第 185-188 行。需要特别说明的是源码对dev_dependencies的未使用检查有白名单例外第 227-238 行lints、dart_flutter_team_lints用于引入analysis_options配置、build_runner与build_web_compilerswebdev 项目必需即使未被直接 import 也不会报错。此外扫描器对// skip_package_deps_validation注释、以及以class、typedef、mixin、enum、extension、void、Future、final、const开头的行会停止解析第 375-386 行这是为了避免误解析方法体或字符串中的伪 import 语句。publish_to: none未发布包的标记方式文档将pkg/下的包分为两类判定依据是 pubspec 中是否含有以下标记# This package is not intended for consumption on pub.dev. DO NOT publish. publish_to: none未发布不面向 pub.dev的包必须在 pubspec 中写入上述注释与publish_to: none字段。这类包在仓库中大量存在例如 pkg/async_helper/pubspec.yaml、pkg/dart2js_info/pubspec.yaml、pkg/_js_interop_checks/pubspec.yaml以及校验工具自身的 tools/package_deps/pubspec.yaml 都采用了这一标记。工具的Package构造函数会读取publish_to字段第 108 行publishablegetter第 139 行据此区分两类包。文档还强调未发布包的 pubspec 虽然同样会被校验工具检查但contents are more informational即其结果更多是信息性参考因为这类 pubspec 不会被 pub 工具或生态消费。未发布包的校验规则对publish_to: none的包除了前述双向一致性检查外还要求对 pkg/ 内部包的引用必须使用相对路径依赖relative path dependency而非any或版本约束。发布包的额外校验规则对准备发布到 pub 的包校验更严格第 255-284 行普通dependencies必须使用语义化版本约束semver constraint不能写any——例外是 SDK 内置的 vendored 包SdkPubDepdev_dependencies则推荐使用any因为开发依赖在开发期固定即可若依赖的包在仓库中解析不到版本resolvedVersion null发布包必须报错——只有未发布包才允许依赖无版本声明的包源码注释以pkg/dartdev依赖package:pub为例。版本范围与实际检入版本的一致性检查工具还会将 pubspec 声明的 semver 范围与 DEPS 解析出的实际版本比对第 288-324 行若声明范围不包含仓库实际版本例如声明^1.0.0而仓库检入的是0.9.x校验即失败并输出类似PackageX depends on package:foo with a range of ^1.0.0, but the version of foo in the repo is 0.9.5的错误信息。未发布包的相对路径依赖要求对于publish_to: none的包文档列出其必须满足的三条校验pubspec 中声明的依赖都在包中被实际使用源码用到的所有包都已在 pubspec 中声明对 pkg/ 包的引用必须通过相对路径依赖。这一约束与发布包必须用 semver形成对照未发布包内部互相依赖时使用path:相对路径如dependencies: { expect: { path: ../expect } }既避免误发布也确保开发期间直接使用工作区源码。注意校验工具会将路径依赖解析为PathPubDep第 556-563 行从而在规则分支中正确放行。新增包的标准流程文档对新增包给出了明确指引添加新包后必须运行gclient sync以重新生成.dart_tool/package_config.json。这是因为新包的加入会改变仓库的包解析结果只有重新执行 gclient sync 才能让 IDE、分析器与构建工具感知到新包的存在。同时建议在新增包时遵循本仓库的既有惯例面向 pub.dev 的包声明明确的 semver 版本约束仅内部使用的包写入publish_to: none注释块并使用any/ 相对路径依赖保证目录名与 pubspecname一致。测试包的标准方式文档特别指出一个容易踩坑的点目前无法用dart test直接运行 pkg/ 下包的测试必须沿用仓库统一的test.py测试入口。例如./tools/test.py -n...path该命令会运行所有以path为前缀的测试。test.py位于仓库根目录的 tools/test.py它负责按测试矩阵调度各目录的测试任务这也解释了为何包内测试文件如 pkg/analysis_server/test/ 下的用例统一由仓库级测试框架驱动而不是各自独立的dart test。CI 集成与验证闭环整个校验机制在 LUCI CI 机器人上自动运行构成本地可复现 → CI 强制执行的闭环开发者在本地运行dart tools/package_deps/bin/package_deps.dart即可复现 CI 的大部分检查包括依赖双向一致性、版本范围匹配、发布/未发布规则CI 对每个 pkg 包执行同样的校验任一失败即终止构建退出码 1依赖版本的唯一事实来源是 DEPS 文件配合tools/manage_deps.dart做版本滚动更新见 DEPS 文件头部注释。常见校验失败示例与排查思路结合 package_deps.dart 的输出文案整理常见失败场景供排查参考失败场景典型输出修复方向目录名与包名不一致Package name is different from the directory name.统一 pubspecname与目录名lib/ 用到未声明的包package:foo used in lib/ but not declared in dependencies:.将 foo 加入dependencies:声明未使用的依赖package:bar declared in dependencies: but not used in lib/.从 pubspec 移除 bar开发依赖放错位置package:baz declared in dependencies: but only used in dev dirs.移到dev_dependencies:发布包用any约束Published packages should use semver deps:改为^x.y.z版本范围不含仓库版本...range of ^1.0.0, but the version of foo in the repo is 0.9.5对齐 DEPS 中的实际版本结语Dart SDK 对pkg/包的自动校验机制本质上是用一套可本地复现、CI 强制的静态检查守住pubspec 声明与DEPS 事实之间的平衡。无论是发布包还是内部包package_deps工具都能在早期发现依赖漂移、误声明和版本错配。理解publish_to: none语义、双向依赖校验逻辑与gclient sync/test.py的配套流程是每个向 SDK 仓库贡献或维护pkg/包的开发者必备的实战技能。【免费下载链接】sdkThe Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.项目地址: https://gitcode.com/gh_mirrors/sdk1/sdk创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考