diff options
Diffstat (limited to 'pluginCheck.go')
| -rw-r--r-- | pluginCheck.go | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/pluginCheck.go b/pluginCheck.go new file mode 100644 index 0000000..b7657f6 --- /dev/null +++ b/pluginCheck.go @@ -0,0 +1,130 @@ +package gui + +import ( + "debug/buildinfo" + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + "plugin" + "runtime/debug" + + "go.wit.com/log" +) + +// CheckPluginCompatibility verifies that the plugin .so file was built +// with the same Go version and dependency versions as the host binary. +func CheckPluginCompatibility(pluginPath string) error { + pluginInfo, err := buildinfo.ReadFile(pluginPath) + if err != nil { + return log.Errorf("failed to read plugin build info: %w", err) + } + + mainInfo, ok := debug.ReadBuildInfo() + if !ok { + log.Info("CHECK FAILED: failed to read main binary build info", pluginPath) + log.Info("CHECK FAILED: failed to read main binary build info", pluginPath) + return errors.New("CHECK FAILED: failed to read main binary build info") + } + + if pluginInfo.GoVersion != mainInfo.GoVersion { + return log.Errorf("Go version mismatch: plugin=%s, host=%s", + pluginInfo.GoVersion, mainInfo.GoVersion) + } + log.Info("CHECK OK: binary build info:", pluginPath) + + // Create a map of main binary dependencies for quick lookup + hostDeps := make(map[string]string) + for _, dep := range mainInfo.Deps { + hostDeps[dep.Path] = dep.Version + } + + log.Info("CHECK OK: binary build info:", hostDeps) + + // Compare plugin dependencies + for _, dep := range pluginInfo.Deps { + hostVer, ok := hostDeps[dep.Path] + if !ok { + return log.Errorf("dependency %s not found in host binary", dep.Path) + } + if dep.Version != hostVer { + return log.Errorf("dependency version mismatch for %s: plugin=%s, host=%s", + dep.Path, dep.Version, hostVer) + } + log.Printf("DEP HASH: version %s: plugin=%s, host=%s\n", dep.Path, dep.Version, hostVer) + } + + return nil +} + +// tests the plugin file will load +func testPluginOld() { + _, err := plugin.Open(argGui.GuiTest) + if err != nil { + log.Log(PLUG, "plugin.Open() FAILED =", argGui.GuiTest, err) + os.Exit(-1) + } + log.Log(PLUG, "plugin.Open() SUCCESS loading plugin =", argGui.GuiTest) + os.Exit(0) +} + +// loads the plugin, then exits with 0 or -1 +func TestPluginAndExit() { + log.Log(WARN, "TEST plugin START", argGui.GuiCheck) + + absPath, err := filepath.Abs(argGui.GuiCheck) + if err != nil { + log.Log(WARN, "TEST plugin FAILED", argGui.GuiCheck, absPath, err) + os.Exit(-1) + } + + log.Log(WARN, "TEST plugin START abs:", absPath, argGui.GuiCheck) + plug, err := checkPlug(absPath) + if plug == nil { + log.Log(WARN, "TEST plugin failed (returned nil):", argGui.GuiCheck, absPath, err) + os.Exit(-1) + } + if err == nil { + log.Log(WARN, "TEST plugin probably worked", argGui.GuiCheck, absPath) + os.Exit(0) + } + log.Log(WARN, "TEST plugin failed", argGui.GuiCheck, absPath, err) + os.Exit(-1) +} + +func CheckPluginViaSubprocess(path string) error { + exe, err := os.Executable() + if err != nil { + return fmt.Errorf("failed to get executable path: %w", err) + } + resolved, err := filepath.EvalSymlinks(exe) + if err != nil { + return fmt.Errorf("failed to resolve executable symlink: %w", err) + } + + cmd := exec.Command(resolved, "--gui-check-plugin", path) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} + +func checkPlug(pluginPath string) (*plugin.Plugin, error) { + // const pluginPath = "/tmp/gocui.so" + // ../../../toolkits/gocui/gocui.so + // /usr/lib/go-gui-toolkits/gocui.v0.22.46.so + + if err := CheckPluginCompatibility(pluginPath); err != nil { + log.Printf("Plugin check failed: %v\n", err) + return nil, err + } + + p, err := plugin.Open(pluginPath) + if err != nil { + log.Printf("plugin.Open failed: %v\n", err) + return nil, err + } + + log.Println("Plugin loaded successfully.") + return p, nil +} |
