概要
知っていると少し便利かもしれないTipsです。
Goには標準ライブラリで plugin というパッケージが用意されていて、シンプルにプラグイン処理が出来ます。
プラグインとして利用するための条件は以下です。
- mainパッケージに公開したい関数を定義しておく
go build -buildmode=plugin
でビルドする
ビルドすると so ファイルが生成されます。なので、windowsは残念ながらサポートされていません。
サンプル
実際の構造を見ていただいたほうがわかりやすいかもしれません。
以下にアップしています。
こんな感じのファイル構成にしています。
gitpod /workspace/try-golang (master) $ tree examples/singleapp/stdlib_plugin_pkg/ examples/singleapp/stdlib_plugin_pkg/ ├── lib │ ├── lib.go │ └── pkg │ └── strs │ └── upper.go ├── main.go └── Makefile 3 directories, 4 files
libディレクトリの下がプラグインとしてビルドするものです。
それを利用する側が直下の main.go 。
lib/pkg/strs/upper.go
package strs import ( "strings" ) func Upper(s string) string { return strings.ToUpper(s) }
lib/lib.go
// プラグインとして利用する処理は main パッケージに属している必要がある package main import ( "fmt" "github.com/devlights/try-golang/examples/singleapp/stdlib_plugin_pkg/lib/pkg/strs" ) func Fn(message string) { fmt.Println(strs.Upper(message)) }
main.go
package main import ( "log" "plugin" ) func exitOnErr(err error) { if err != nil { log.Fatalln(err) } } func main() { var ( plg *plugin.Plugin sym plugin.Symbol err error ) // プラグインをオープンして plg, err = plugin.Open("lib/lib.so") exitOnErr(err) // シンボルを取得 sym, err = plg.Lookup("Fn") exitOnErr(err) // シンボルは 対象となる値 (変数とか関数) へのポインタとなっている // 関数の場合は sym.(func()) で利用できるが、変数の場合は *(sym.(*int)) のようにする必要がある点に注意 if fn, ok := sym.(func(string)); ok { fn("hello world") } }
Taskfile.yml
go-task のタスクファイルです。Makeの代わり。
version: '3' tasks: build: cmds: - cd lib ; go build -buildmode=plugin - go build -o app clean: cmds: - rm -f ./app - find . -name "*.so" | xargs -I{} rm -f {} run: deps: [ build ] cmds: - ./app file-tree: cmds: - find . -print | sed -e "s;[^/]*/;|____;g;s;____|; |;g"
ビルドしてみる
ビルドすると以下のように生成物が出来上がります。
gitpod /workspace/try-golang (master) $ task -d examples/singleapp/stdlib_plugin_pkg/ build task: [build] cd lib ; go build -buildmode=plugin task: [build] go build -o app gitpod /workspace/try-golang (master) $ tree examples/singleapp/stdlib_plugin_pkg/ examples/singleapp/stdlib_plugin_pkg/ ├── app ├── lib │ ├── lib.go │ ├── lib.so │ └── pkg │ └── strs │ └── upper.go ├── main.go ├── Makefile └── Taskfile.yml 3 directories, 7 files
lib/lib.so と app が出来上がりました。
上の main.go の中で、lib/lib.so をプラグインとして読み込むようにしているので、実行すると
gitpod /workspace/try-golang (master) $ task -d examples/singleapp/stdlib_plugin_pkg/ run task: [build] cd lib ; go build -buildmode=plugin task: [build] go build -o app task: [run] ./app HELLO WORLD
ちゃんとプラグイン側の処理が呼ばれて結果を返してくれていますね。
参考情報
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。