Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
Signed-off-by: Tibor Vass <[email protected]>
  • Loading branch information
tiborvass committed Feb 10, 2016
0 parents commit be66363
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 0 deletions.
14 changes: 14 additions & 0 deletions Makefile
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
63 changes: 63 additions & 0 deletions README.md
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
```
9 changes: 9 additions & 0 deletions plugin-c/plugin.c
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");
}
18 changes: 18 additions & 0 deletions plugin-go/plugin.go
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() {
}
63 changes: 63 additions & 0 deletions plugin.go
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
}
29 changes: 29 additions & 0 deletions plugin_test.go
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()
}
}

0 comments on commit be66363

Please sign in to comment.