mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-24 19:08:10 +00:00
plugin: 删除 C ABI 通道(Part 6.2 完成,-3198 行)
外部插件统一走子进程 + stdio RPC,三套独立 ABI 实现收敛为单一 RPC 实现。 用户决策:彻底舍弃 .so 能力,不保留双通道回退。 ## 删除清单 internal/plugin/cabi/ 1156 行(loader.go/loader.c/types.go/output_test.go) internal/plugin/dynamic_dll_windows.go 272 行(§9.2 记录的能力退化实现) internal/plugin/dynamic_loader_unix.go 79 行(唯一 cabi 引用点) internal/plugin/dynamic_dll_test.go 32 行 internal/plugin/dynamic_dll_stub.go 11 行 internal/plugin/dynamic_loader_windows.go 11 行 internal/plugin/bridge_e2e_test.go (测的是 cabi 路径) third_party/.../plugindev/templates.go 1296 行(取消跟踪,SDK 仓才是权威副本) dynamic.go:entryCABI 通道删除,soEntry/dllEntry 常量删除。 registry.go:tryDynamic 探测顺序从 .so → .dll → .lua 变成 proc → lua。 ## 旧 .so 给明确错误,不静默跳过 静默跳过会让「插件目录在但没加载」看起来像配置问题,而实际原因是需要 用新版 plugindev 重编。故保留 legacyCABIEntries 表专门用于识别残留: plugin legacy: 检测到旧 C ABI 产物(plugin.so/.dll/.dylib)。 外部插件已改为子进程模式,请用新版 plugindev 重编产出 plugin.bin (业务代码无需修改) 错误消息里「业务代码无需修改」这句是有测试守着的——迁移的核心承诺就是它。 ## pluginmgr 安装逻辑跟进 bundle 命名 子进程模式下各平台产物统一叫 plugin.bin(进程边界即 ABI 边界),故 zip 内 按平台加后缀 plugin.bin.<goos>.<goarch>,解包时挑当前平台那一份重命名。 platformBinary 改为按 runtime.GOOS+GOARCH 生成条目名;platformBinaries 固定表 换成 isPlatformBinary 前缀判断(平台组合会增长:linux/arm64、darwin/arm64…, 按前缀判断无需维护清单)。 新增 chmod 0755:zip 保留了原权限位,但经某些工具链/传输后可能丢失, 内核加载时会因缺执行位报错。提前补上比事后让用户 chmod 更好。 ## 测试 entry_dispatch_test.go 重写(12 项): - classifyEntry 对 .so/.dll/.dylib 现在返回 unknown - LegacyManifestFallsBackToProbe:存量插件 manifest 仍写 "plugin.so" (17 个插件没人去改),须靠目录探测找到 plugin.bin —— 这是 「外部插件零改动」的直接后果 - LegacyCABIGivesActionableError:错误消息须含 plugindev / plugin.bin / 业务代码 - PluginEntryHash_IgnoresLegacyCABI:.so 不参与 hash(内核已不认它) upgrade_test.go 的 .hmap 构造改用 plugin.bin。 验证:go build ./... 通过;go test ./... 全仓无失败; go test -race ./internal/plugin/... 全绿;三平台构建通过。 Ref: docs/zh/架构迁移评估.md §3.1/§9.2、docs/zh/plugin-migration-plan.md Part 6
This commit is contained in:
@ -23,16 +23,16 @@ import (
|
||||
sdk "gitcode.com/JianFeeeee/HomeAgent/internal/sdk"
|
||||
)
|
||||
|
||||
// platformBinary 按当前 OS 选择正确的插件二进制文件名。
|
||||
// platformBinary 按当前 OS/ARCH 选择正确的插件二进制文件名。
|
||||
// 返回 (zip内文件名, 安装后重命名).
|
||||
//
|
||||
// 子进程模式下各平台产物统一叫 plugin.bin(进程边界即 ABI 边界,
|
||||
// 不存在 .so/.dylib/.dll 的区分),故 zip 内按平台加后缀区分,
|
||||
// 解包时挑当前平台那一份重命名为 plugin.bin。
|
||||
func platformBinary() (zipName, canonicalName string) {
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
return "plugin.so", "plugin.so"
|
||||
case "darwin":
|
||||
return "plugin.dylib", "plugin.so" // dlopen 兼容 .so 名称
|
||||
case "windows":
|
||||
return "plugin.dll", "plugin.dll"
|
||||
case "linux", "darwin", "windows", "freebsd":
|
||||
return fmt.Sprintf("plugin.bin.%s.%s", runtime.GOOS, runtime.GOARCH), "plugin.bin"
|
||||
default:
|
||||
return "", ""
|
||||
}
|
||||
@ -40,18 +40,17 @@ func platformBinary() (zipName, canonicalName string) {
|
||||
|
||||
// validBinaries 是 .hmap 中所有可识别的文件入口(平台二进制或脚本)。
|
||||
var validBinaries = map[string]bool{
|
||||
"plugin.so": true,
|
||||
"plugin.dylib": true,
|
||||
"plugin.dll": true,
|
||||
"main.lua": true,
|
||||
"SKILL.md": true,
|
||||
"plugin.bin": true,
|
||||
"main.lua": true,
|
||||
"SKILL.md": true,
|
||||
}
|
||||
|
||||
// platformBinaries 是平台特定的二进制,bundle 模式下仅当前平台的被解压。
|
||||
var platformBinaries = map[string]bool{
|
||||
"plugin.so": true,
|
||||
"plugin.dylib": true,
|
||||
"plugin.dll": true,
|
||||
// isPlatformBinary 判断 zip 条目是否为平台特定二进制(bundle 模式下仅当前平台的被解压)。
|
||||
//
|
||||
// 形式:plugin.bin.<goos>.<goarch>。不用固定表是因为平台组合会增长
|
||||
// (linux/arm64、darwin/arm64 等),按前缀判断无需维护清单。
|
||||
func isPlatformBinary(name string) bool {
|
||||
return strings.HasPrefix(name, "plugin.bin.")
|
||||
}
|
||||
|
||||
var downloadClient = &http.Client{
|
||||
@ -705,17 +704,19 @@ func validatePackage(data []byte) (*pluginPackage, error) {
|
||||
}
|
||||
|
||||
if len(pkg.Platforms) > 0 {
|
||||
// bundle mode: check each declared platform has a matching binary
|
||||
// bundle mode:每个声明的平台都要有对应二进制。
|
||||
// 子进程模式下条目形式为 plugin.bin.<goos>.<goarch>,
|
||||
// 故按前缀匹配而不枚举架构(同一 OS 可能有 amd64/arm64 两份)。
|
||||
for _, plat := range pkg.Platforms {
|
||||
bin, ok := map[string]string{
|
||||
"linux": "plugin.so",
|
||||
"darwin": "plugin.dylib",
|
||||
"windows": "plugin.dll",
|
||||
}[plat]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported platform: %q", plat)
|
||||
prefix := "plugin.bin." + plat + "."
|
||||
found := false
|
||||
for name := range zipEntries {
|
||||
if strings.HasPrefix(name, prefix) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if zipEntries[bin] {
|
||||
if found {
|
||||
hasBinary = true
|
||||
}
|
||||
}
|
||||
@ -795,11 +796,11 @@ func extractPackage(data []byte, pluginDir string) error {
|
||||
}
|
||||
|
||||
// bundle mode: skip other platforms' platform-specific binaries
|
||||
if isBundle && platformBinaries[f.Name] && f.Name != zipBin {
|
||||
if isBundle && isPlatformBinary(f.Name) && f.Name != zipBin {
|
||||
continue
|
||||
}
|
||||
|
||||
// rename platform binary to canonical name (e.g. plugin.dylib → plugin.so)
|
||||
// 平台二进制重命名为规范名(plugin.bin.linux.amd64 → plugin.bin)
|
||||
dest := fpath
|
||||
if isBundle && f.Name == zipBin && canonicalName != zipBin {
|
||||
dest = filepath.Join(target, canonicalName)
|
||||
@ -808,6 +809,15 @@ func extractPackage(data []byte, pluginDir string) error {
|
||||
if err := copyZipEntry(f, dest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 子进程插件必须可执行。
|
||||
// zip 保留了原文件权限位,但经某些工具链/传输后可能丢失;
|
||||
// 内核加载时会因缺执行位报错(带 chmod +x 提示),在此提前补上。
|
||||
if filepath.Base(dest) == "plugin.bin" {
|
||||
if err := os.Chmod(dest, 0o755); err != nil {
|
||||
return fmt.Errorf("chmod %s: %w", dest, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user