We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
A while ago, I wanted to test how I could integrate the plugin
package in a project I'm working on.
As you probably know, the plugin
package is a way to load Go code from dynamic libraries. This allows you to extend and app with more code without having to recompile the main application.
Before we continue, the first thing you need to know is that Go currently only supports plugins on Linux and Mac, not on Windows.
The first step in using plugins is to define an interface to which the plugins need to conform. This is needed to ensure we can properly load the plugin.
For this example, we define the following interface:
type PluginGreeter interface {
Greet()
}
For each plugin we want to test, we then need to create a main package which implement the interface. This is a sample implementation:
package main
import (
"fmt"
"plugin01/uuid"
)
type greeting string
func (g greeting) Greet() {
fmt.Println("Hello Universe from plugin 1 - " + uuid.UUID())
}
// exported as symbol named "Greeter"
var Greeter greeting
Another plugin might do something different for the greeting:
package main
import (
"fmt"
)
type greeting string
func (g greeting) Greet() {
fmt.Println("Hello Universe from plugin 2")
}
// exported as symbol named "Greeter"
var Greeter greeting
This then needs to be compiled before we can use it. To compile a plugin, you need to specify plugin
as the buildmode. This is passed as an argument to the go build
command:
go build -buildmode=plugin -o plugin01.so plugin01/main
The result of this step is a .so
file which can be dynamically loaded from another Go application.
Loading a plugin is done by means of the plugin
package. Given that we know the path of the plugin we want to load, we can load it as follows:
package main
import (
"errors"
"path/filepath"
"plugin"
)
func loadPluginAndExecute(path string) error {
// Load the plugin from the file
plug, err := plugin.Open(path)
if err != nil {
return err
}
// Lookup the symbol called "Greeter"
symGreeter, err := plug.Lookup("Greeter")
if err != nil {
return err
}
// Cast the symbol to the PluginGreeter interface
var greeter PluginGreeter
greeter, ok := symGreeter.(PluginGreeter)
if !ok {
return errors.New("Unexpected type from module symbol")
}
// Perform the Greet function
greeter.Greet()
return nil
}
As you can see, using a plugin is pretty straightforward, but it takes some effort to get the project setup done.
What you should remember is that the .so
files are compiled the same way as executables, so if you build them on a
mac, they will not work on a Linux box. You can get around this by using cross compilation.
In the sample source code you can download here, I've provided a Makefile
to make the whole setup a bit easier. It also has support for using dep for managing the dependencies.
You can use the following make targets:
make build
: builds the main app and the two pluginsmake run
: builds and runs the main appmake clean
: removes the build filesmake dep-init
: performsdep init
for the main app and the two pluginsmake dep-ensure
: performsdep ensure
for the main app and the two plugins
I also added a Visual Studio Code tasks.json file so you can run these straight from your editor.
If this post was enjoyable or useful for you, please share it! If you have comments, questions, or feedback, you can email my personal email. To get new posts, subscribe use the RSS feed.