summaryrefslogtreecommitdiff
path: root/pluginCheck.go
diff options
context:
space:
mode:
Diffstat (limited to 'pluginCheck.go')
-rw-r--r--pluginCheck.go130
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
+}