背景
在 CI 流水线或离线工控场景下,工程师常需要“把一个 .TapPackage 文件丢到目标机,一键装完所有依赖”。OpenTAP 的 tap package install 看似只是 unzip,背后却有一套并行下载、版本协商、冲突降级的完整流程。今天把镜头对准 Package 子系统,拆解本地安装链路。
框架分析
Package 子系统核心角色:
- Installation —— 表示一个 OpenTAP 根目录,维护已安装列表 (
/Packages/*.PackageDef.xml) - IPackageRepository —— 抽象源,可以是文件夹、HTTP、缓存
- DependencyResolver —— 已废弃,逻辑已迁至
Image/PackageDependencyQuery.cs - Installer —— 真正落盘的“安装工”,负责解压、文件锁定、回滚
- PackageDef —— 描述包元数据,含
Dependencies列表与Architecture/OS约束
安装入口统一在 tap.exe package install xxx.tappackage,CLI 动作类 PackageInstallAction 把参数转成 PackageInstallStep,再交给 Installer.Install。
实现过程
前置检查
Installer构造时即校验目标目录是否被占用:若tap.exe正在运行,会抛出InstallationLockedException;否则在%LocalAppData%\OpenTap\InstallLock\{Installation.Id}.lock创建 Mutex。元数据反序列化
把.TapPackage视为 ZIP,先读头文件Package/package.xml得到PackageDef;此时并不解压 payload,仅做兼容性预检(CPU、OS、MinOpenTapVersion)。依赖展开
调用PackageDependencyQuery.ResolveDependencies:- 以请求包为根,广度优先遍历依赖树
- 对已安装节点,采用最大满足策略(已装版本 ≥ 所需版本则复用)
- 对缺失节点,按 Repo 优先级依次下载;若存在版本冲突,则选最低兼容版本降低爆炸半径
最终得到List<PackageDef> toInstall,并按拓扑排序保证依赖先装。
并发下载 & 缓存
使用HttpPackageRepository时,会先把包写入%LocalAppData%\OpenTap\PackageCache\{hash}.tappackage;后续同名安装直接走缓存,避免重复拉取。原子落盘
Installer.InstallPackage逐条执行:- 解压到临时目录
OpenTap\Packages\.staging\{name}\ - 若存在同名旧版,先重命名为
.backup - 将
.staging移入正式目录,同时写入PackageDef.xml - 若全部成功,删除
.backup;否则触发回滚,把.backup还原并删除新目录
- 解压到临时目录
后置钩子
如果包内含install.bat或install.sh,Installer会在事务外调用,失败仅警告不回滚,保证脚本副作用可控。
可复现命令
1 | # 离线安装本地包,跳过下载阶段,强制覆盖同版本 |
注意事项
- 同一进程内禁止并发安装;CI 中若并行调用
tap package install,需外加文件锁 - 若目标机无网络,把依赖包事先放到
Repositories: - path: ./local-repo文件夹,即可当作本地源 - 降级场景下,
Installer不会自动卸载高版;如需干净环境,先tap package uninstall --all再批量装 - Windows 长路径问题:包内若嵌套
node_modules深度 > 8,需提前启用长路径组策略或改用 Linux 构建机
小结
OpenTAP 把“安装”拆成元数据读取→依赖协商→缓存下载→事务落盘→钩子触发五步,每一步都可单测、可回滚。理解这一链路的意义在于:当离线交付或国产化编译器出现“装得上跑不起来”时,你能迅速定位是版本协商失败,还是脚本钩子写错。下一篇我们把镜头转向 Engine/TestStep,看看执行引擎如何把 TestPlan 转成线程树。
关键源码路径
Package/Installer.cs—— 安装事务主循环Package/Installation.cs—— 已安装列表与架构检测Package/Image/PackageDependencyQuery.cs—— 新版依赖解析器Package/PackageDef.cs—— 包元数据 POCOPackage/PackageManagerSettings.cs—— 源优先级与缓存开关