gpt4 book ai didi

go - Go 插件依赖是如何工作的?

转载 作者:IT王子 更新时间:2023-10-29 01:27:27 26 4
gpt4 key购买 nike

Go 1.8 支持 Go 插件。

我创建了两个插件如下。

据我了解,该插件仅公开了 main 中的函数和变量。包裹。即 plugin.Lookup()对于非 main 将失败变量/函数。

但是我想测试一个插件是否可以在内部调用另一个插件的方法,类似于 C++ 库如何调用另一个库。

所以我测试如下:

插件 1 github.com/vimal/testplugin

$ cat myplugin.go
package main

import "C"
import "fmt"
import help "github.com/vimal/testplugin1/plug"

func init() {
fmt.Printf("main.init invoked\n")
}
// TestPlugin
func TestPlugin() string {
return help.Help()
}

plugin2 github.com/vimal/testplugin1
$ cat myplugin.go
package main

import "C"

func HelperFunc() string {
return "help"
}
$ cat plug/helper.go
package help

func Help() string {
return "help234"
}

这里的想法是 plugin1 调用一个内部的、非 main plugin2的功能

主程序

主程序加载一些作为参数给出的插件,并调用 TestPlugin()从最后一个插件。

测试 1:

构建两个插件,加载两个插件,然后调用 TestPlugin() ,输出包含 "help234" ,即调用内部函数。这个可以理解,因为两个插件都加载了,一个插件可以调用另一个插件的内部代码。

测试 2:

仅加载 plugin1,并调用 TestPlugin() ,输出包含 "help234" ,即调用内部函数。观察到与 test1 中相同的输出。或许这次的方法是从 GOPATH找到的.

测试 3:

重命名文件夹 "github.com/vimal/testplugin1""github.com/vimal/junk1" ,删除plugin2,只加载plugin1,调用 TestPlugin() .输出仍然包含 "help234" ,即调用内部函数。

我无法理解 test3 如何产生相同的输出。 plugin1 是否也包含 plugin2 代码?如何理解 Go 插件对其他 Go 插件的依赖?

转到版本: go version go1.8rc3 linux/amd64

最佳答案

你并没有完全按照你的想法去做。

您的 插件 1 导入并使用一个包,即 github.com/vimal/testplugin1/plug .这不“等于” plugin2 !

这里发生的情况是,当您构建 时插件 1 ,它的所有依赖都内置在插件文件中,包括 .../testplugin1/plug包裹。当你加载 插件 1 ,它的所有依赖项也是从插件文件中加载的,包括 plug包裹。在此之后,无论 的加载状态如何,它都可以工作也就不足为奇了。 plugin2 .这两个插件相互独立。
-buildmode=plugin指示编译器您要构建插件而不是独立应用程序,但这并不意味着不能包含依赖项。它们必须是,因为插件无法保证 Go 应用程序将加载它,以及 Go 应用程序将具有哪些包。因为一个可运行的应用程序也只包含来自应用程序本身明确引用的标准库的包。

保证插件将拥有它需要的一切的唯一方法,如果它还包含它的所有依赖项,包括那些来自标准库的依赖项,它就会工作。 (这就是为什么构建简单的插件会生成相对较大的文件,类似于构建简单的 Go 可执行文件会生成大文件。)

例如,很少需要添加到插件中的东西包括 Go 运行时,因为一个正在运行的将加载插件的 Go 应用程序已经运行了一个 Go 运行时。 (请注意,您只能从使用相同版本的 Go 编译的应用程序加载插件。)但除此之外,插件必须包含它需要的一切。

Go 是一种静态链接语言。一旦 Go 应用程序或插件被编译,它们就不会依赖或检查 GOPATH 的值。 ,它仅在构建它们时由 Go 工具使用。

更深入的洞察

您的主应用程序和插件可能引用同一个包(导入路径为“相同”)。在这种情况下,将只使用包的一个“实例”。

如果此通常引用的包具有“状态”,例如全局变量,则可以对此进行测试。让我们假设一个名为 mymath 的通用共享包:

package mymath

var S string

func SetS(s string) {
S = s
}

还有一个名为 pg 的插件使用它:
package main

import (
"C"
"mymath"
"fmt"
)

func Start() {
fmt.Println("pg:mymath.S", mymath.S)
mymath.SetS("pghi")
fmt.Println("pg:mymath.S", mymath.S)
}

以及使用 mymath 的主要应用程序并加载 pg (使用它):
package main

import (
"plugin"
"mymath"
"fmt"
)

func main() {
fmt.Println("mymath.S", mymath.S)
mymath.SetS("hi")
fmt.Println("mymath.S", mymath.S)

p, err := plugin.Open("../pg/pg.so")
if err != nil {
panic(err)
}

start, err := p.Lookup("Start")
if err != nil {
panic(err)
}

start.(func())()

fmt.Println("mymath.S", mymath.S)
}

构建插件:
cd pg
go build -buildmode=plugin

运行主应用程序,输出为:
mymath.S 
mymath.S hi
pg:mymath.S hi
pg:mymath.S pghi
mymath.S pghi

分析:首先主应用播放 mymath.S ,将其设置为 "hi"最终。然后是插件,它打印它(我们看到主应用程序设置的值 "hi" ),然后将其更改为 "pghi" .然后再次出现主应用程序并打印 mymath.S ,再次看到插件设置的最后一个值: "pghi" .

所以只有一个“实例” mymath .现在,如果您继续更改 mymath ,例如重命名 myMath.SetS()mymath.SetS2() ,然后更新主应用程序中的调用(到 mymath.SetS2("hi") ),无需重新构建插件,只需运行主应用程序,您将获得以下输出:
mymath.S 
mymath.S hi
panic: plugin.Open: plugin was built with a different version of package mymath

goroutine 1 [running]:
main.main()
<GOPATH>/src/play/play.go:16 +0x4b5
exit status 2

如您所见,在构建主应用程序和插件时,会记录包版本(很可能是散列),如果它们的导入路径在主应用程序和插件中匹配,则包版本必须匹配。

(请注意,如果您不更改使用的 mymath 包的导出标识符(和签名),仅更改实现,您也会收到上述错误;例如 func SetS(s string) { S = s + "+" } 。)

关于go - Go 插件依赖是如何工作的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42218472/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com