-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
6 changed files
with
196 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
all: plugin-go/plugin.so plugin-c/plugin.so | ||
go test | ||
|
||
clean: | ||
rm -f plugin-go/plugin.so plugin-go/plugin.h | ||
rm -f plugin-c/plugin.so | ||
|
||
plugin-go/plugin.so: | ||
go build -buildmode=c-shared -o $@ ./plugin-go | ||
|
||
plugin-c/plugin.so: | ||
gcc -fPIC -shared -o $@ ./plugin-c/plugin.c | ||
|
||
.PHONY: clean all |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
# C Shared Libraries as Go plugins | ||
|
||
Package plugin allows you to easily define a plugin for your Go application | ||
and have it call out at runtime, to C shared libraries fully or partially | ||
implementing the user-defined plugin. | ||
|
||
The advantage of this is that the implementation of the plugin is language-agnostic. | ||
|
||
Tested only on 64bit Linux. | ||
|
||
## Installation | ||
|
||
Go 1.5+ is needed | ||
|
||
``` | ||
go get -u github.com/tiborvass/go-plugin | ||
``` | ||
|
||
## Example 1 | ||
|
||
```Go | ||
package main | ||
|
||
import "github.com/tiborvass/go-plugin" | ||
|
||
type MyPlugin struct { | ||
plugin.Plugin | ||
Hello() | ||
Goodbye() | ||
} | ||
|
||
func main() { | ||
var p MyPlugin | ||
if err := plugin.Open(&p, "path/to/shared/lib/plugin"); err != nil { | ||
panic(err) | ||
} | ||
defer p.Close() | ||
|
||
p.Hello() | ||
p.Goodbye() | ||
} | ||
``` | ||
|
||
## Example 2 | ||
|
||
You'll need gcc installed for this example to work. | ||
|
||
``` | ||
$ cd $GOPATH/src/github.com/tiborvass/go-plugin | ||
$ make clean | ||
$ make | ||
go build -buildmode=c-shared -o plugin-go/plugin.so ./plugin-go | ||
gcc -fPIC -shared -o plugin-c/plugin.so ./plugin-c/plugin.c | ||
go test | ||
Hello from main | ||
Hello from Go plugin | ||
This is a function implemented only in Go | ||
Hello from C plugin! | ||
This is a function implemented only in C | ||
Goodbye from main | ||
PASS | ||
ok toto 0.034s | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#include <stdio.h> | ||
|
||
void HelloWorld() { | ||
printf("Hello from C plugin!\n"); | ||
} | ||
|
||
void OnlyInC() { | ||
printf("This is a function implemented only in C\n"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package main | ||
|
||
import "fmt" | ||
|
||
import "C" | ||
|
||
//export HelloWorld | ||
func HelloWorld() { | ||
fmt.Println("Hello from Go plugin") | ||
} | ||
|
||
//export OnlyInGo | ||
func OnlyInGo() { | ||
fmt.Println("This is a function implemented only in Go") | ||
} | ||
|
||
func main() { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// Package plugin allows you to easily define a plugin for your Go application | ||
// and have it call out at runtime, to C shared libraries fully or partially | ||
// implementing the user-defined plugin. | ||
// | ||
// The advantage of this is that the implementation of the plugin is language-agnostic. | ||
// | ||
// Tested only on 64bit Linux. | ||
package plugin | ||
|
||
import ( | ||
"errors" | ||
"reflect" | ||
|
||
"github.com/tiborvass/dl" | ||
) | ||
|
||
type Plugin struct { | ||
dl *dl.DL | ||
} | ||
|
||
var _plugin = reflect.TypeOf(Plugin{}).Name() | ||
|
||
func (p Plugin) Close() error { | ||
if p.dl != nil { | ||
return p.dl.Close() | ||
} | ||
return nil | ||
} | ||
|
||
var nopFn = func([]reflect.Value) []reflect.Value { return nil } | ||
|
||
func Open(plugin interface{}, path string) error { | ||
v := reflect.ValueOf(plugin) | ||
t := v.Type() | ||
if t.Kind() != reflect.Ptr { | ||
return errors.New("plugin needs to be a pointer to a struct") | ||
} | ||
v = v.Elem() | ||
t = v.Type() | ||
if t.Kind() != reflect.Struct { | ||
return errors.New("Open expects a plugin of type struct{ interface{ Method() } }") | ||
} | ||
lib, err := dl.Open(path, 0) | ||
if err != nil { | ||
return err | ||
} | ||
for i := 0; i < v.NumField(); i++ { | ||
tf := t.Field(i) | ||
if tf.Name != _plugin { | ||
sym := v.Field(i).Interface() | ||
if err := lib.Sym(tf.Name, &sym); err != nil && tf.Type.Kind() == reflect.Func { | ||
fn := reflect.MakeFunc(tf.Type, nopFn) | ||
v.Field(i).Set(fn) | ||
} else { | ||
v.Field(i).Set(reflect.ValueOf(sym)) | ||
} | ||
} else { | ||
p := Plugin{lib} | ||
v.Field(i).Set(reflect.ValueOf(p)) | ||
} | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package plugin | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
) | ||
|
||
func TestOpen(t *testing.T) { | ||
type MyPlugin struct { | ||
Plugin | ||
HelloWorld func() | ||
OnlyInC func() | ||
OnlyInGo func() | ||
} | ||
|
||
fmt.Println("Hello from main") | ||
defer fmt.Println("Goodbye from main") | ||
|
||
for _, path := range []string{"./plugin-go/plugin", "./plugin-c/plugin"} { | ||
var myPlugin MyPlugin | ||
if err := Open(&myPlugin, path); err != nil { | ||
panic(err) | ||
} | ||
defer myPlugin.Close() | ||
myPlugin.HelloWorld() | ||
myPlugin.OnlyInGo() | ||
myPlugin.OnlyInC() | ||
} | ||
} |