Plugins in Go 1.8
A look at the new
plugin package coming in Go 1.8 - a bit of background, a look on how to make use of it and some of its (current) limitations.
Recent Go releases have introduced a number of different ways to compile Go code. With Go 1.5 for example came the option to link Go code into non-Go programs using a C-style API, allowing non-Go programs to run pieces of Go code. The same 1.5 release also introduced the option to build Go packages as shared libraries instead of single statically-linked binaries1.
(This can be useful if, for example, you use the same package(s) in a set of different applications and want a way to update them without having to recompile all those applications individually. Linux distros like Debian and Red Hat have traditionally made use of this a lot in their packaging for C/C++ applications for example.)
Go 1.8 will introduce yet another way to compile Go code, one that is likely more interesting to the average Go programmer than the other above-mentioned build modes. Coming with this release is the ability to build a shared library out of some Go code to then load and use this library from inside another Go program.
In other words, Go 1.8 will allow you to compile Go code as a plugin to be loaded into and used from another Go application.
Before we look at how to use this latest new shiny though, here’s some caveats you should be aware of;
plugin buildmode is currently only supported on Linux.
Darwin support was originally included but it has since been pulled because of stability issues2 which cannot be resolved within the 1.8 release window.
Another limitation is the fact that plugins must be built with the same version of the Go toolchain as the main application3. That means you can’t compile a plugin with, say, a future Go version 1.9 and expect it to work with an application built with Go 1.8 or vice versa.
So how does it all work in practice?
In order to support this new plugin buildmode, the Go standard libray has gained a new package (unsurprisingly called plugin) and the go compiler now understands
To build a plugin with these there’s really not much special stuff you need to do.
The only requirement here is that your plugin needs to be built from a
main package4, such as the following
plugin.go for example:
You can then build a plugin out of it by using the aforementioned
-buildmode=plugin argument to
go build, which will give you a shared object (
➔ go build -buildmode=plugin plugin.go ➔ ls -l total 1544 -rw-rw-r-- 1 zoni zoni 60 Jan 8 23:44 plugin.go -rw-rw-r-- 1 zoni zoni 1576680 Jan 8 23:45 plugin.so ➔ file plugin.so plugin.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=fc2ec03a4fca7e2b78aa1cb8bd82a9e1c99a0554, not stripped
In your main application you can now use the Open function from the
plugin package to get a reference to this plugin and on that you can use Lookup to get a reference to any exported symbol, such as a variable or function:
Compiling and running this program as usual should result in the string “Hello world” being printed:
➔ go run main.go Hello world
Plugins are simple to build and load into applications which means they are easily accessible to the average Go programmer - no magic build system required.
True to form, the
plugin package included in the standard library is trimmed down only to the bare essentials, leaving it up to developers to think about and implement suitable higher-level things such as plugin discovery themselves.
With all that said though, it will probably be a while before plugins see much adoption and I currently would not recommend using them in any sizable project just yet. The current lack of support for any platform other than Linux means plugins will only be usable for a very limited target audience for a while to come.
This requirement feels somewhat arbitrary to me but I’m sure there are good reasons for this design. I haven’t been able to find out why this is but I would love to know. ↩︎