如何排查 Eclipse feature 导出为 p2 repository 时的错误?
结论
这个错误出现在 p2 解析依赖或“安装到当前 Eclipse”的阶段。即使出现错误,plugin JAR 也可能已经生成。workspace 中有依赖项目,只能说明 PDE 编译和 Eclipse Application 启动时可以找到它,无法保证导出的 p2 repository 或目标 IDE 的 p2 profile 中也有对应的 bundle。
建议按以下方式处理:
- 核对
MANIFEST.MF中依赖 bundle 的 ID 和版本范围。 - 确认目标 IDE 已通过 p2 正式安装依赖,且版本符合要求。
- 导出包含 plugin 的 feature,并以
feature.group作为可安装单元。 - 如果目标环境缺少依赖,将依赖一起发布到 repository,或者让 p2 同时访问依赖所在的 repository。
- 确认依赖满足要求后,再通过
dropins或Install New Software…安装。
不要绕过 p2 的依赖检查。强行放入 JAR 后,bundle 仍可能停留在 INSTALLED 状态,相关扩展也不会生效。
为什么创建 repository 会出现“complete the install”
Install into host. Repository 除了生成 repository,通常还会执行两个操作:
- 将导出的 bundle 和元数据写入指定 repository。
- 调用 p2,将生成的 Installable Unit 安装到当前执行导出操作的 Eclipse 中。
错误中的:
Cannot complete the install
表示第二步失败。目录中仍可能留下:
artifacts.jar
content.jar
plugins/com.company.myplugin_1.0.0.201312091256.jar
这些文件说明发布过程至少完成了一部分,但无法证明安装测试已经通过,也无法证明 repository 中有可在安装界面显示的顶层 feature。
如果只需要生成 repository,应选择导出到目录或归档,并启用生成 p2 metadata 的相关选项,不要选择 Install into host。不同 Eclipse/PDE 版本中的向导名称可能略有差异。
为什么 workspace 中有源代码,p2 仍然找不到依赖
PDE 启动配置和 p2 使用不同的依赖来源。
在开发环境中,Eclipse Application 可以直接把 workspace project 加入运行时 target platform,因此下面的项目结构可以正常运行:
workspace
├── com.company.myplugin
└── com.company.product.otherplugin
进入导出和安装阶段后,p2 不会自动把 workspace 中的其他项目视为可安装单元。它只会检查:
- 当前 p2 profile 中已安装的 Installable Unit;
- 本次安装使用的 repository;
- 安装期间同时启用的其他 repository。
即使另一个 plugin 的源码就在 workspace 中,只要它没有发布到 p2 repository,也没有通过 p2 安装到目标环境,解析器仍可能报告:
requires 'bundle com.company.product.otherplugin 1.0.1' but it could not be found
依赖 JAR 也可能是手工复制到 dropins 或 plugins 目录的。OSGi 运行时或许能发现它,但 p2 profile 未必会把它当作已安装的 IU,导致运行时依赖与安装时依赖不一致。
先检查依赖声明
打开当前 plugin 的 META-INF/MANIFEST.MF,检查 Require-Bundle:
Bundle-SymbolicName: com.company.myplugin;singleton:=true
Bundle-Version: 1.0.0.qualifier
Require-Bundle: org.eclipse.core.runtime,
com.company.product.otherplugin;bundle-version="[1.0.1,2.0.0)"
需要核对:
com.company.product.otherplugin是否与依赖 plugin 的Bundle-SymbolicName完全一致;- ID 的大小写是否一致;
- 目标 IDE 中的版本是否在声明范围内;
- 是否给实现 bundle 设置了过窄的精确版本;
- 是否存在重复 bundle,且实际选中的版本不符合要求。
示例中的 com.company.myplugin 和 com.company.MyPlugin 大小写不同,需要单独核对。OSGi 和 p2 的标识符区分大小写。
如果要求最低版本为 1.0.1,并允许使用同一主版本内的后续版本,可以声明明确的范围:
Require-Bundle: com.company.product.otherplugin;bundle-version="[1.0.1,2.0.0)"
不要只为通过安装而删除版本约束。确认代码兼容更宽的版本范围后,才能放宽约束。
如果依赖提供稳定的 Java package,也可以考虑使用 Import-Package:
Import-Package: com.company.product.otherplugin.api;version="[1.0,2.0)"
这样可以减少对具体 bundle 实现的绑定。能否采用这种方式,要看对方是否正确导出了 package,以及当前项目是否依赖该 bundle 的扩展、片段或内部实现。
确认目标 IDE 中的依赖是否满足要求
在目标 IDE 中打开:
Help
→ About Eclipse IDE
→ Installation Details
→ Plug-ins
搜索 com.company.product.otherplugin,核对以下信息:
- Symbolic Name;
- Version;
- Provider;
- 是否存在多个版本。
如需检查 OSGi 的解析状态,可以通过控制台启动 Eclipse:
eclipse -console -consoleLog
进入 OSGi 控制台后执行:
ss com.company.product.otherplugin
ss com.company.myplugin
如果自己的 bundle 已经被发现,但尚未解析,可以继续执行:
diag <bundle-id>
headers <bundle-id>
diag 通常会列出尚未满足的 package 或 bundle 约束。这里的 <bundle-id> 是 ss 输出中的数字,不是 symbolic name。
目标 IDE 中有一个名称相似的 plugin,并不能说明依赖已经满足。p2 和 OSGi 根据 Bundle-SymbolicName 与版本进行匹配,不会根据 JAR 文件名或产品界面中的显示名称匹配。
正确导出 feature repository
先为 plugin 创建 feature,并在 feature.xml 中加入该 plugin:
<?xml version="1.0" encoding="UTF-8"?>
<feature
id="com.company.MyPlugin.feature"
label="MyPlugin Feature"
version="1.0.0.qualifier"
provider-name="Company">
<plugin
id="com.company.myplugin"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
</feature>
导出时选择 feature,不要只导出单个 plugin。成功生成的 repository 通常包含类似结构:
repository/
├── artifacts.jar
├── content.jar
├── features/
│ └── com.company.MyPlugin.feature_1.0.0.xxxxxxxx.jar
└── plugins/
└── com.company.myplugin_1.0.0.xxxxxxxx.jar
安装界面通常显示 feature 对应的:
com.company.MyPlugin.feature.group
单独导出的 bundle IU 不一定会显示为顶层软件项。插件 JAR 已写入 repository,并不等于 Install New Software 中一定会出现可安装项。
如果 repository 没有 category,可以在安装界面取消:
Group items by category
如果 repository 中根本没有 feature.group,取消分类后也未必会出现适合用户安装的顶层条目。面向 IDE 发布时,使用 feature 通常更稳妥。
依赖 plugin 应该怎么处理
目标 IDE 尚未安装依赖
如果有权重新发布依赖 plugin,可以将它加入同一个 feature:
<plugin
id="com.company.product.otherplugin"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
也可以将依赖放在另一个 feature 或 repository 中,安装时同时启用两个 repository。重新分发该 bundle 前,还需要检查它的许可证和交付方式。
只在 feature 中增加 <requires> 无法提供缺失的依赖。例如:
<requires>
<import
plugin="com.company.product.otherplugin"
version="1.0.1"
match="greaterOrEqual"/>
</requires>
这段配置只声明了依赖要求。p2 仍要从已安装的 profile 或可访问的 repository 中找到对应 bundle。
目标 IDE 已安装依赖
确认该依赖由 p2 安装,而且版本符合约束。如果依赖是手工复制进去的,建议将它制作成 p2 IU 后正式安装,或者与当前 feature 一起发布。
还要区分目标 IDE 和执行 Install into host 的开发 IDE。即使目标 IDE 已经安装依赖,开发 IDE 也可能没有。Install into host 检查的是当前运行导出向导的 Eclipse,不会读取另一套 IDE 的安装状态。
用 p2 director 检查 repository
可以通过 p2 director 列出 repository 中的 IU。可执行文件名取决于平台,例如 Linux 上可能是 eclipse,Windows 上可能是 eclipsec.exe:
eclipse \
-nosplash \
-application org.eclipse.equinox.p2.director \
-repository file:/absolute/path/to/repository \
-list
正常情况下,输出中应有类似条目:
com.company.myplugin=1.0.0.201312091051
com.company.MyPlugin.feature.group=1.0.0.201312091051
com.company.MyPlugin.feature.jar=1.0.0.201312091051
如果输出中只有 bundle IU,没有 feature.group,说明 feature 没有正确发布,或者当前检查的是此前导出 plugin 时生成的 repository。
也可以通过 director 安装 feature:
eclipse \
-nosplash \
-application org.eclipse.equinox.p2.director \
-repository file:/absolute/path/to/repository \
-installIU com.company.MyPlugin.feature.group \
-destination /absolute/path/to/target-eclipse
操作正式 IDE 前,应先备份或使用测试副本。某些 Eclipse 版本还需要指定 profile、profile properties 或目标环境参数,具体取决于产品的 p2 配置。
为什么复制到 dropins 后没有生效
dropins 只能发现 bundle,无法补齐缺失的依赖。常见原因包括:
- 依赖 bundle 不存在或版本不匹配;
Bundle-SymbolicName拼写错误或大小写不一致;- plugin 已被发现,但依赖未满足,因此停留在
INSTALLED状态; - plugin 提供了扩展,但对应的宿主 bundle 或 extension point 不存在;
- JAR 打包不完整,例如缺少
plugin.xml、类文件或资源; - 当前产品禁用了相应的 dropins reconciliation 机制;
- Eclipse 仍在使用旧的 bundle 缓存状态。
可以先用 -console -consoleLog 和 diag 查明原因。确认依赖与打包内容都正确后,再尝试:
eclipse -clean -consoleLog
-clean 用于刷新缓存,不能代替依赖检查。手工将 JAR 放进 plugins 目录也不可靠,因为现代 Eclipse 产品通常通过 p2 和 simple configurator 管理 bundle 列表。
此外,Installation Details 的不同页面显示的内容并不相同。通过 p2 安装的 feature 通常会出现在 Installed Software 中;通过 dropins 发现的 bundle 更适合在 Plug-ins 页面或 OSGi 控制台中查看。
推荐的排查顺序
- 在依赖 plugin 的
MANIFEST.MF中确认实际的Bundle-SymbolicName和Bundle-Version。 - 检查当前 plugin 的
Require-Bundle或Import-Package,重点核对大小写和版本范围。 - 使用接近目标 IDE 的 Target Platform 验证,不要只依赖 workspace 中的源代码项目。
- 分别确认开发 host 和目标 IDE 的 p2 profile 是否包含依赖。
- 创建包含当前 plugin 的 feature。
- 将缺少的依赖发布到同一 repository,或提供依赖所在的 repository。
- 导出后通过 p2 director 的
-list检查feature.group和 bundle IU。 - 使用
Install New Software…或 p2 director 安装。 - 安装后通过
ss、diag和 Error Log 检查 bundle 状态。
当前错误说明 p2 无法从自己的依赖来源中找到 com.company.product.otherplugin。将这个 bundle 发布为版本匹配且可被 p2 解析的 IU 后,feature 才能顺利导出并完成安装。
备注:内容仅供参考。
