Create plugins in Go

Stefan Alfbo - May 25 - - Dev Community

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 .


Enter fullscreen mode Exit fullscreen mode

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!")
}


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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 .


Enter fullscreen mode Exit fullscreen mode

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
}


Enter fullscreen mode Exit fullscreen mode

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()
}


Enter fullscreen mode Exit fullscreen mode

And now when we execute our app we will get the output The simple plugin has been called!, as expected.

output

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!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .