Create plugins in Go
Tags: 100daystooffload, go, plugin, programming
Published: 2024-05-25 19:19:06

Of course Go has enabled the possibility to add a [plugin](https://pkg.go.dev/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`.

```console
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.

```golang
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`.

```console
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.

```console
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.

```golang
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.

```golang
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.

![output](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wi8afakg74gc0b0l0ri5.png)

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](https://pkg.go.dev/plugin) which also discuss other options depending on your needs for your application.

Happy coding!