summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--argv.go16
-rw-r--r--init.go32
-rw-r--r--plugin.go32
-rw-r--r--pluginCheck.go130
4 files changed, 176 insertions, 34 deletions
diff --git a/argv.go b/argv.go
index b7e75df..fd34701 100644
--- a/argv.go
+++ b/argv.go
@@ -11,11 +11,12 @@ This struct can be used with the go-arg package. These
are the generic default command line arguments for the 'GUI' package
*/
type ArgsGui struct {
- NoGui bool `arg:"--no-gui" help:"ignore all these gui problems"`
- GuiPlugin string `arg:"--gui" help:"Use this gui toolkit [andlabs,gocui,nocui,stdin]"`
- GuiFile string `arg:"--gui-file" help:"Use a specific plugin.so file"`
- GuiTest string `arg:"--gui-test" help:"test a specific plugin.so will load"`
- GuiVerbose bool `arg:"--gui-verbose" help:"enable all logging"`
+ NoGui bool `arg:"--no-gui" help:"ignore all these gui problems"`
+ GuiPlugin string `arg:"--gui" help:"Use this gui toolkit [andlabs,gocui,nocui,stdin]"`
+ GuiFile string `arg:"--gui-file" help:"Use a specific plugin.so file"`
+ GuiTest string `arg:"--gui-test" help:"test a specific plugin.so will load"`
+ GuiVerbose bool `arg:"--gui-verbose" help:"enable all logging"`
+ GuiCheck string `arg:"--gui-check-plugin" help:"used to check if the plugin loads"`
}
/*
@@ -32,6 +33,11 @@ func ArgToolkit() string {
func InitArg() {
arg.Register(&argGui)
+
+ if argGui.GuiCheck != "" {
+ // does os.Exec() and does not return
+ TestPluginAndExit()
+ }
}
/*
diff --git a/init.go b/init.go
index 1fabbb7..80d9d42 100644
--- a/init.go
+++ b/init.go
@@ -2,7 +2,6 @@ package gui
import (
"errors"
- "fmt"
"os"
"runtime/debug"
@@ -38,7 +37,7 @@ func initNew() {
me.guiChan = make(chan widget.Action, 1)
version, err := getGuiVersion()
- fmt.Println("GO GUI version", version, showVersion()+"<Bromeliaceae>", err)
+ log.Println("GO GUI version", version, showVersion()+"<Bromeliaceae>", err)
if version == "" {
log.Warn("Warning: compiled without version", err)
log.Sleep(1)
@@ -77,16 +76,16 @@ func getGuiVersion() (string, error) {
if tmp == nil {
return "", errors.New("compiled without go module support")
}
- // fmt.Println("mod.Path = ", tmp.Path)
- // fmt.Println("mod.Main.Path = ", tmp.Main.Path)
- // fmt.Println("mod.Main.Version = ", tmp.Main.Version)
- // fmt.Println("mod.Main.Sum = ", tmp.Main.Sum)
+ // log.Println("mod.Path = ", tmp.Path)
+ // log.Println("mod.Main.Path = ", tmp.Main.Path)
+ // log.Println("mod.Main.Version = ", tmp.Main.Version)
+ // log.Println("mod.Main.Sum = ", tmp.Main.Sum)
for _, value := range tmp.Deps {
if value.Path == "go.wit.com/gui" {
found = value.Version
}
- // fmt.Println("\tmod.Path = ", value.Path)
- // fmt.Println("\tmod.Version = ", value.Version)
+ // log.Println("\tmod.Path = ", value.Path)
+ // log.Println("\tmod.Version = ", value.Version)
}
if found != "" {
// log.Println("GUI build version:", found)
@@ -282,7 +281,8 @@ func New() *Node {
}
*/
if argGui.GuiTest != "" {
- testPlugin()
+ // does os.Exec() and does not return
+ TestPluginAndExit()
}
initNew()
@@ -296,6 +296,13 @@ func NoGui() bool {
// try to load andlabs, if that doesn't work, fall back to the console
func (n *Node) Default() *Node {
var err error
+ // used to check if plugins load or not
+ if argGui.GuiCheck != "" {
+ // does os.Exec() and does not return
+ TestPluginAndExit()
+ os.Exit(0)
+ }
+
if argGui.NoGui {
log.Log(WARN, "--no-gui chill Winston. I don't need no gui")
return n
@@ -415,12 +422,13 @@ func trapStdout() {
origStderr = os.Stderr
os.Stderr = guierrf
+ // maybe all this should be deleted now?
println("TEST println")
println("TEST println")
println("TEST println")
- fmt.Println("TEST fmt")
- fmt.Println("TEST fmt")
- fmt.Println("TEST fmt")
+ // maybe test fmt? or, maybe all this should be deleted now?
+ // fmt.Println("TEST fmt")
+ // fmt.Println("TEST fmt")
log.Info("TEST log")
log.Info("TEST log")
log.Info("TEST log")
diff --git a/plugin.go b/plugin.go
index a1322ee..a599269 100644
--- a/plugin.go
+++ b/plugin.go
@@ -8,6 +8,7 @@ package gui
import (
"embed"
"errors"
+ "fmt"
"os"
"path/filepath"
"plugin"
@@ -233,29 +234,26 @@ func searchPaths(name string) *aplug {
return nil
}
-// tests the plugin file will load
-func testPlugin() {
- _, 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)
-}
-
// load module
// 1. open the shared object file to load the symbols
func initToolkit(name string, filename string) *aplug {
- if _, err := os.Stat(filename); err != nil {
- if os.IsNotExist(err) {
- log.Log(PLUG, "initToolkit() missing plugin", name, "as filename", filename)
- return nil
+ /*
+ if _, err := os.Stat(filename); err != nil {
+ if os.IsNotExist(err) {
+ log.Log(PLUG, "initToolkit() missing plugin", name, "as filename", filename)
+ return nil
+ }
}
+ log.Log(PLUG, "initToolkit() Found plugin", name, "as filename", filename)
+
+ plug, err := plugin.Open(filename)
+ */
+ if err := CheckPluginViaSubprocess(filename); err != nil {
+ fmt.Printf("initToolkit() subprocess load plugin failed: %v\n", err)
+ return nil
}
- log.Log(PLUG, "initToolkit() Found plugin", name, "as filename", filename)
- plug, err := plugin.Open(filename)
+ plug, err := checkPlug(filename)
if err != nil {
// turn on PLUG debugging if something goes wrong
PLUG.SetBool(true)
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
+}