Of course Go has enabled the possibility to add a plugin feature to your Go application with the standard library.
By adding a plugin feature to your application you enable third-party development, extensibility, customization and more.
Here comes a simple example to use the plugin standard library in an application. We start out by creating the plugin first, which we will call, simple-plugin
.
mkdir simple-plugin && cd $_
touch plugin.go
go mod init simple.plugin
code .
The code will be super simple for this plugin, add this to the plugin.go
file.
package main
import "fmt"
func SimplePluginFunc() {
fmt.Println("The simple plugin has been called!")
}
So the plugin needs to be a main package with public functions and/or variables. Therefore we use the package name, main
, here and includes one public function, SimplePluginFunc
.
A plugin needs to be built with an extra compilation flag, buildmode
.
go build -buildmode=plugin -o simple-plugin.so ./plugin.go
This will compile our plugin and produce a simple-plugin.so
file as an output artifact of the compilation. The -o
flag is not needed, but then the output artifact would have been named plugin.so
instead. One drawback with this solution is that it's only supported on Linux, FreeBSD, and Mac. However you can use WSL on your Windows machine to do this too.
Now we have a plugin that we can consume in our main application, so lets create that application.
mkdir app && cd $_
mkdir plugins
cp ../simple-plugin/simple-plugin.so plugins
touch main.go
go mod init example.app
code .
Note that we are creating a directory, plugins
, that host our simple-plugin.so
plugin from the previous exercise.
In the main file we first create a function to load the plugin.
func loadPlugin() func() {
plugin, err := plugin.Open("plugins/simple-plugin.so")
if err != nil {
log.Fatal(err)
}
simplePluginFunc, err := plugin.Lookup("SimplePluginFunc")
if err != nil {
log.Fatal(err)
}
f, ok := simplePluginFunc.(func())
if !ok {
log.Fatal("unexpected type from module symbol")
}
return f
}
This function is first using the standard library, plugin
, to open our newly created plugin which is located in our plugin directory.
Then we are trying to Lookup
our public function in the plugin, which we called SimplePluginFunc
. The Lookup
function will return a Symbol
which is a pointer to the function SimplePluginFunc
.
With the help of type assertion we will get the plugin function in a more strongly typed form, which we are doing with simplePluginFunc.(func())
.
Finally we are returning the function from our loadPlugin
function. The complete app
will look like this.
package main
import (
"log"
"plugin"
)
func loadPlugin() func() {
plugin, err := plugin.Open("plugins/simple-plugin.so")
if err != nil {
log.Fatal(err)
}
simplePluginFunc, err := plugin.Lookup("SimplePluginFunc")
if err != nil {
log.Fatal(err)
}
f, ok := simplePluginFunc.(func())
if !ok {
log.Fatal("unexpected type from module symbol")
}
return f
}
func main() {
simplePlugin := loadPlugin()
simplePlugin()
}
And now when we execute our app we will get the output The simple plugin has been called!
, as expected.
This is a simple example on how to use this feature from the standard library, plugin
, but it's not harder than that. However, I encourage you to read the plugin
package documentation which also discuss other options depending on your needs for your application.
Happy coding!