golang生成Golang插件导出符号包装工具插件库go-bind-plugin的使用

go-bind-plugin 使用指南

什么是 go-bind-plugin?

go-bind-plugin 是一个 go:generate 工具,用于构建 Go 1.8+ 插件并为导出的符号(函数和变量)生成包装器。它通过反射确定符号的实际类型,并生成类型化的 API 包装器,同时提供额外功能(如解引用导出变量和检查 SHA256 校验和)。

为什么使用它?

使用 go-bind-plugin 可以简化插件 API 的调用。例如,如果插件导出 func AddTwoInts(a, b int) intvar BuildVersion string,你可以直接调用:

plug, err := pluginapi.BindPluginAPI("plugin.so") // plug 是 *plugin_api.PluginAPI
if err != nil {
  panic(err)
}

result := plug.AddTwoInts(10, 20)
fmt.Println(plug.BuildVersion) // 如果未使用 -dereference-vars 则是 fmt.Println(*plug.BuildVersion)

而不是手动使用 plugin.Lookup 进行类型断言。

安装与使用

安装

go get -u github.com/wendigo/go-bind-plugin

命令行选项

go-bind-plugin -help

Usage of go-bind-plugin:
  -dereference-vars
        解引用插件变量
  -format
        使用 gofmt 格式化生成的输出文件 (默认 true)
  -hide-vars
        不导出插件变量
  -interface
        生成并返回接口而不是结构体 (会启用 -hide-vars)
  -output-name string
        输出结构体名称 (默认 "PluginAPI")
  -output-package string
        输出包名 (可从 output-path 派生) (默认 "main")
  -output-path string
        输出文件路径 (默认 "plugin_api.go")
  -plugin-package string
        插件包路径 (go get 接受的格式)
  -plugin-path string
        插件路径 (.so 文件)
  -rebuild
        每次运行时重新构建插件
  -sha256
        将插件的 sha256 校验和写入包装器并在加载时验证

完整示例

1. 创建插件

首先创建一个简单的插件:

// basic_plugin/plugin.go
package main

import "fmt"

var BuildVersion = "1.0.0"

func AddTwoInts(a, b int) int {
    return a + b
}

func Greet(name string) string {
    return fmt.Sprintf("Hello, %s!", name)
}

func main() {} // 必须有 main 函数

2. 构建插件

go build -buildmode=plugin -o plugin.so basic_plugin/plugin.go

3. 生成包装器

创建一个主程序并使用 go-bind-plugin 生成包装器:

// main.go
package main

//go:generate go-bind-plugin -plugin-path ./plugin.so -output-name MyPlugin -output-path plugin_api.go -output-package main -dereference-vars

import (
    "fmt"
)

func main() {
    // 使用生成的包装器
    plug, err := BindMyPluginAPI("./plugin.so")
    if err != nil {
        panic(err)
    }
    
    sum := plug.AddTwoInts(5, 7)
    greeting := plug.Greet("Gopher")
    
    fmt.Println("Build Version:", plug.BuildVersion)
    fmt.Println("5 + 7 =", sum)
    fmt.Println(greeting)
}

运行 go generate:

go generate

4. 运行程序

go run .

输出示例:

Build Version: 1.0.0
5 + 7 = 12
Hello, Gopher!

高级用法

使用接口模式

如果你想使用接口而不是结构体:

//go:generate go-bind-plugin -plugin-path ./plugin.so -interface -output-name PluginInterface -output-path plugin_interface.go -output-package main

生成的代码将提供一个接口,便于模拟和测试。

校验插件完整性

使用 -sha256 选项可以确保插件未被篡改:

//go:generate go-bind-plugin -plugin-path ./plugin.so -sha256 -output-name SecurePlugin -output-path secure_plugin.go

性能考虑

使用 -buildmode=plugin 和生成的包装器在方法调用上似乎没有增加开销:

BenchmarkCallOverhead/plugin-8         30000000    58.0 ns/op    0 B/op    0 allocs/op
BenchmarkCallOverhead/native-8         20000000    59.3 ns/op    0 B/op    0 allocs/op

创建插件实例和加载 .so 文件是固定成本。

总结

go-bind-plugin 提供了一种类型安全、简洁的方式来使用 Go 插件,避免了手动类型断言和符号查找的繁琐工作。通过代码生成,它创建了易于使用的包装器,同时保持了良好的性能特性。


更多关于golang生成Golang插件导出符号包装工具插件库go-bind-plugin的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang生成Golang插件导出符号包装工具插件库go-bind-plugin的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用go-bind-plugin生成Golang插件导出符号包装工具

go-bind-plugin是一个用于生成Golang插件导出符号包装的工具,它可以帮助你更方便地使用Go插件系统。下面我将详细介绍如何使用这个工具。

基本概念

在Go中,插件是通过plugin包实现的编译好的.so文件。要使用插件中的函数或变量,你需要知道它们的名称和类型,这通常需要手动处理。go-bind-plugin可以自动生成这些包装代码。

安装go-bind-plugin

go get github.com/wendigo/go-bind-plugin

使用步骤

1. 创建插件项目

首先创建一个插件项目,例如:

mkdir myplugin
cd myplugin
go mod init github.com/yourname/myplugin

2. 编写插件代码

创建main.go文件,内容如下:

package main

import "fmt"

func Greet(name string) string {
    return fmt.Sprintf("Hello, %s!", name)
}

var Version = "1.0.0"

func main() {} // 必须要有main函数,但可以为空

3. 构建插件

go build -buildmode=plugin -o myplugin.so

4. 生成包装代码

使用go-bind-plugin生成包装代码:

go-bind-plugin -plugin myplugin.so -o myplugin_bindings.go -package main

这将生成一个myplugin_bindings.go文件,其中包含加载和使用插件所需的包装代码。

5. 使用生成的包装代码

创建一个主程序来使用插件:

package main

import (
    "fmt"
    "log"
)

func main() {
    // 使用生成的包装代码加载插件
    plugin, err := LoadMyplugin("myplugin.so")
    if err != nil {
        log.Fatal(err)
    }
    
    // 调用插件函数
    greeting := plugin.Greet("World")
    fmt.Println(greeting)
    
    // 访问插件变量
    fmt.Println("Plugin version:", plugin.Version)
}

高级用法

指定要导出的符号

你可以通过注释指定要导出的符号:

//export Greet
func Greet(name string) string {
    return fmt.Sprintf("Hello, %s!", name)
}

//export Version
var Version = "1.0.0"

然后使用-exported标志告诉go-bind-plugin只处理这些符号:

go-bind-plugin -plugin myplugin.so -o myplugin_bindings.go -package main -exported

自定义类型支持

如果你的插件使用自定义类型,确保这些类型在主程序和插件中都能访问。通常的做法是将这些类型放在一个共享包中。

完整示例

下面是一个完整的示例,展示如何创建插件和使用go-bind-plugin:

  1. 插件代码 (plugin/main.go):
package main

import "fmt"

//export Greet
func Greet(name string) string {
    return fmt.Sprintf("Hello, %s!", name)
}

//export Add
func Add(a, b int) int {
    return a + b
}

//export Version
var Version = "1.0.0"

func main() {}
  1. 构建插件:
go build -buildmode=plugin -o plugin.so ./plugin
  1. 生成包装代码:
go-bind-plugin -plugin plugin.so -o bindings.go -package main -exported
  1. 主程序 (main.go):
package main

import (
    "fmt"
    "log"
)

func main() {
    plugin, err := LoadPlugin("plugin.so")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Println(plugin.Greet("Gopher"))
    fmt.Println("1 + 2 =", plugin.Add(1, 2))
    fmt.Println("Version:", plugin.Version)
}

注意事项

  1. 插件和主程序必须使用相同的Go版本编译
  2. 在Linux上使用插件时,可能需要设置LD_LIBRARY_PATH环境变量
  3. 插件系统在Windows上不可用
  4. 插件和主程序之间的类型必须完全匹配

go-bind-plugin简化了Go插件的使用流程,特别是当你需要导出多个函数和变量时,它可以节省大量手动编写加载代码的时间。

回到顶部