Go-bind-plugin generates API for exported plugin symbols (-buildmode=plugin)

go-bind-plugin Build Status Coverage Status GoDoc Go Report Card Say thanks!

TL;DR: See end-to-end example in go-bind-plugin-example.

go-bind-plugin is go:generate tool for building golang 1.8 plugins and generating wrappers around exported symbols (functions and variables).

What is it?

go-bind-plugin generates neat API around symbols exported by a *.so plugin built with go build -buildmode=plugin in upcoming go 1.8. plugin.Plugin holds information about exported symbols as map[string]interface{}.

go-bind-plugins uses reflection to find out actual types of symbols and generates typed API wrapping plugin with additional functionalities (like dereferencing exported variables and checking SHA256 sum).

Note: Basic usage does not require plugin sources as wrapper can be generated using only *.so file.

Why should I use it?

In example if plugin exports func AddTwoInts(a, b int) int and var BuildVersion string instead of using Plugin.Lookup directly:

plug, err := plugin.Open("plugin.so")

if err != nil {
  panic(err)
}

symbol, err := plug.Lookup("AddTwoInts")
if err != nil {
  panic("AddTwoInts was not found in a plugin")
}

if typed, ok := symbol.(func(int, int) int); ok {
  result := typed(10, 20)
} else {
  panic("AddTwoInts has different type than exported by plugin")
}

symbol, err := plug.Lookup("BuildVersion")
if err != nil {
  panic("BuildVersion was not found in a plugin")
}

if typed, ok := symbol.(*string); ok {
  fmt.Println(*typed)
} else {
  panic("BuildVersion is not a string reference")
}

you can just simply do:

plug, err := pluginapi.BindPluginAPI("plugin.so") // plug is *plugin_api.PluginAPI

if err != nil {
  panic(err)
}

result := plug.AddTwoInts(10, 20)
fmt.Println(plug.BuildVersion) // or fmt.Println(*plug.BuildVersion) if -dereference-vars is not used

pluginapi.BindPluginAPI() ensures that plugin exports required symbols and their types are correct.

Usage

go get -u github.com/wendigo/go-bind-plugin
go-bind-plugin -help

Usage of go-bind-plugin:
  -dereference-vars
    	Dereference plugin variables
  -format
    	Format generated output file with gofmt (default true)
  -hide-vars
    	Do not export plugin variables
  -interface
    	Generate and return interface instead of struct (turns on -hide-vars)
  -output-name string
    	Output struct name (default "PluginAPI")
  -output-package string
    	Output package (can be derived from output-path) (default "main")
  -output-path string
    	Output file path (default "plugin_api.go")
  -plugin-package string
    	Plugin package url (as accepted by go get)
  -plugin-path string
    	Path to plugin (.so file)
  -rebuild
    	Rebuild plugin on every run
  -sha256
    	Write plugin's sha256 checksum to wrapper and validate it when loading it

Example

//go:generate go-bind-plugin -format -plugin-package github.com/plugin_test/plug -rebuild -sha256 -dereference-vars -output-name TestPlugin -output-path tmp/plugin.go -plugin-path tmp/plugin.so -output-package pluginapi

go-bind-plugin will do following things on invocation:

  • build plugin to tmp/plugin.so (even if plugin exists it will be rebuilded) from github.com/plugin_test/plug source (must exist in $GOPATH or vendor/)
  • generate wrapper struct wrapper.TestPlugin in tmp/plugin.go
  • dereference variables in the generated wrapper
  • format generated wrapper with gofmt -s -w
  • write sha256 checksum to tmp/plugin.go that will be checked when loading plugin with pluginapi.BindTestPlugin(path string) (*TestPlugin, error)

Wrapper API example (for -output-name "PluginAPI")

BindPluginAPI(path string) (*PluginAPI, error) - loads plugin from path and wraps it with type PluginAPI struct:

  • all functions exported by the plugin are exposed as methods on struct PluginAPI
  • all variables exported by the plugin are exposed as fields on struct PluginAPI (if -dereference-vars is used fields are not references to plugin's variables)

func (*PluginAPI) String() string - provides nice textual representation of the wrapper

Wrapper as interface

When -interface is used instead of generating and returning struct interface containing all exported symbols is generated. This eases mocking and working with multiple plugins exporting the same API.

Note that -interface effectively enables -hide-vars so variables won't be exported from the plugin.

Generated code quality

Generated code passes both go vet and golint and can be formatted using gofmt -s -w. Exported symbols names are not changed in any way so names not following go naming convention will still be reported by golint as invalid.

Example generated wrapper information

Wrapper info:
	- Generated on: 2016-11-08 16:15:07.513150982 +0100 CET
	- Command: go-bind-plugin -plugin-path ./internal/test_fixtures/generated/basic_plugin/plugin.so -plugin-package ./internal/test_fixtures/basic_plugin -output-name TestWrapper -output-path ./internal/test_fixtures/generated/basic_plugin/plugin.go -output-package main -sha256 true -format true -rebuild true

Plugin info:
	- package: github.com/wendigo/go-bind-plugin/internal/test_fixtures/basic_plugin
	- sha256 sum: 55aa13402686f3200f5067604c04ce8d365e7cf2095d8278b2ff52ae26df7e6d
	- size: 1232572 bytes

Exported functions (3):
	- ReturningInt32 func() (int32)
	- ReturningStringSlice func() ([]string)
	- ReturningIntArray func() ([3]int32)

Plugin call overhead

Using -buildmode=plugin with generated wrapper seems not to add overhead when calling methods on a wrapper (creating plugin instance and loading *.so file is constant cost).

BenchmarkCallOverhead/plugin-8         	30000000	        58.0 ns/op	       0 B/op	       0 allocs/op
BenchmarkCallOverhead/plugin-8         	30000000	        59.3 ns/op	       0 B/op	       0 allocs/op
BenchmarkCallOverhead/plugin-8         	30000000	        54.8 ns/op	       0 B/op	       0 allocs/op
BenchmarkCallOverhead/native-8         	20000000	        59.3 ns/op	       0 B/op	       0 allocs/op
BenchmarkCallOverhead/native-8         	20000000	        59.8 ns/op	       0 B/op	       0 allocs/op
BenchmarkCallOverhead/native-8         	20000000	        59.7 ns/op	       0 B/op	       0 allocs/op
Owner
Mateusz "Serafin" Gajewski
Time isn’t slowing, just speed, it’s all we’re knowing.
Mateusz
Similar Resources

Go API Client for Metasploit RPC API

go-msf-rpc Golang based RPC client to communicate with Metasploit. Based on code initially developed by Wyatt Dahlenburg repo. Extended to include oth

Nov 18, 2022

iceportal-api is a Golang client implementation to interact with the REST API of iceportal.de when connected to the WiFi-Network offered in German ICE Trains.

iceportal-api is a Golang client implementation to interact with the REST API of iceportal.de when connected to the WiFi-Network offered in German ICE Trains.

iceportal-api is a Golang client implementation to interact with the REST API of iceportal.de when connected to the WiFi-Network offered in German ICE Trains.

Aug 20, 2022

Go-db-connection-api - API REST in Go that connect to SQL DB and manage task of projects

Go Todo REST API Example A RESTful API example for simple application with Go It

Jan 26, 2022

A Go library for connecting to HandlerSocket (github.com/ahiguti/HandlerSocket-Plugin-for-MySQL)

handlersocket-go Go library for connecting to HandlerSocket Mysql plugin. See github.com/ahiguti/HandlerSocket-Plugin-for-MySQL/ Installation $ go get

Jan 19, 2021

Yet another SIP003 plugin for shadowsocks, based on Xray-core

Yet another SIP003 plugin for shadowsocks, based on Xray-core Build go build Usage See command line args for advanced usages.

Jan 8, 2023

DipDup plugin for selective metadata indexing

DipDup metadata indexer DipDup service for indexing contract and token metadata. Based on TzKT indexer. For start synchronization of DipDup state TzKT

Nov 25, 2022

Tool for monitoring network devices (mainly using SNMP) - monitoring check plugin

Tool for monitoring network devices (mainly using SNMP) - monitoring check plugin

Thola Description A tool for monitoring network devices written in Go. It features a check mode which complies with the monitoring plugins development

Dec 29, 2022

Protoc plugin to generate contract tests for gRPC in Go

Deal - Go Introduction WE DO NOT SUPPORT THE SERVER SIDE YET This plugin allows us to write Consumer-Driver Contracts tests! Usage example Proto servi

Sep 3, 2022

This plugin allows you to start a local server with hot reloading with Esbuild

esbuild-dev-server This plugin allows you to start a local server with hot reloading with Esbuild Installation npm npm i esbuild-dev-server -D yarn y

Nov 4, 2022
Comments
  • Generates wrong code

    Generates wrong code

    with this plugin it seems to generate wrong code :(

    package main
    
    import (
    	"C"
    	"fmt"
    	"gitlab.com/IRSH/irshGoFramework/plugins/types"
    )
    
    // pluginName of the Web Plugin
    const PluginName = "web"
    
    type PluginStruct struct {
    	types.DefaultPlugin
    }
    
    func (p PluginStruct) Greet() {
    	fmt.Println("Hello Universe")
    }
    
    // Define Plugin (Web=false as it otherwise would create a dependency loop)
    var Plugin = PluginStruct{
    	DefaultPlugin: types.NewDefaultPlugin(PluginName, false),
    }
    

    The result code is

    package main
    
    // Autogenerated by github.com/wendigo/go-bind-plugin on 2017-08-10 16:28:47.266724012 +0000 UTC, do not edit!
    // Command: go-bind-plugin -plugin-path ../../../../plugins/web.so -plugin-package ./plugins -output-name WebPlugin -output-path ./plugin_api.go -output-package main -dereference-vars -rebuild
    //
    // Plugin ../../../../plugins/web.so info:
    // - package: gitlab.com/IRSH/irshGoFramework/plugins
    // - size: 3181200 bytes
    // - sha256: cbd1f0c407ac3405ea31b0ab692cc397c1ef4bbbe33c1d82d46059b9a761117c
    
    import (
            "fmt"
            "main"
            "plugin"
            "reflect"
            "strings"
    )
    
    // WebPlugin wraps symbols (functions and variables) exported by plugin gitlab.com/IRSH/irshGoFramework/plugins
    //
    // See docs at https://godoc.org/gitlab.com/IRSH/irshGoFramework/plugins
    type WebPlugin struct {
            // Exported functions
    
            // Exported variables (public references)
    
            // See docs at https://godoc.org/gitlab.com/IRSH/irshGoFramework/plugins#Plugin
            Plugin main.PluginStruct
    }
    
    // String returnes textual representation of the wrapper. It provides info on exported symbols and variables.
    func (p *WebPlugin) String() string {
            var lines []string
            lines = append(lines, "Struct WebPlugin:")
            lines = append(lines, "\t- Generated on: 2017-08-10 16:28:47.266724012 +0000 UTC")
            lines = append(lines, "\t- Command: go-bind-plugin -plugin-path ../../../../plugins/web.so -plugin-package ./plugins -output-name WebPlugin -output-path ./plugin_api.go -output-package main -dereference-vars -rebuild")
            lines = append(lines, "\nPlugin info:")
            lines = append(lines, "\t- package: gitlab.com/IRSH/irshGoFramework/plugins")
            lines = append(lines, "\t- sha256 sum: cbd1f0c407ac3405ea31b0ab692cc397c1ef4bbbe33c1d82d46059b9a761117c")
            lines = append(lines, "\t- size: 3181200 bytes")
            lines = append(lines, "\nExported functions (0):")
    
            lines = append(lines, "\nExported variables (1):")
            lines = append(lines, "\t- Plugin main.PluginStruct")
    
            return strings.Join(lines, "\n")
    }
    
    // BindWebPlugin loads plugin from the given path and binds symbols (variables and functions)
    // to the WebPlugin struct. All variables are derefenenced.
    func BindWebPlugin(path string) (*WebPlugin, error) {
            p, err := plugin.Open(path)
    
            if err != nil {
                    return nil, fmt.Errorf("could not open plugin: %s", err)
            }
    
            ret := new(WebPlugin)
    
            varPlugin, err := p.Lookup("Plugin")
            if err != nil {
                    return nil, fmt.Errorf("could not import variable 'Plugin', symbol not found: %s", err)
            }
    
            if typed, ok := varPlugin.(*main.PluginStruct); ok {
                    ret.Plugin = *typed
            } else {
                    return nil, fmt.Errorf("could not import variable 'Plugin', incompatible types 'main.PluginStruct' and '%s'", reflect.TypeOf(varPlugin))
            }
    
            return ret, nil
    }
    

    for some reason it tries to get PluginStruct from main but it needs to be more or less copied over.

requestgen generates the cascade call for your request object

requestgen requestgen generates the cascade call for your request object Installation go get github.com/c9s/requestgen Usage requestgen scans all the

Nov 30, 2022
Torrent-metainfo-parser - Generates a .torrent meta info from a file

torrent-metainfo-parser generates a .torrent meta info from a file required argu

Aug 23, 2022
Generates file.key file for IPFS Private Network.

ipfs-keygen Generates file.key file for IPFS Private Network. Installation go get -u github.com/reixmor/ipfs-keygen/ipfs-keygen Usage ipfs-keygen > ~/

Jan 18, 2022
The plugin serves as a starting point for writing a Mattermost plugin

Plugin Starter Template This plugin serves as a starting point for writing a Mattermost plugin. Feel free to base your own plugin off this repository.

Dec 10, 2021
Feb 10, 2022
Cf-cli-find-app-plugin - CF CLI plugin to find applications containing a search string

Overview This cf cli plugin allows users to search for application names that co

Jan 3, 2022
Twitter-plugin - Falco Plugin for Twitter Stream

Twitter Plugin This repository contains the twittter plugin for Falco, which fol

Mar 17, 2022
Matterbridge API plugin for Reddit Community Chat

Mattereddit 2 Mattereddit is a Matterbridge API plugin allowing you to connect Reddit group chats to the various chat services supported by Matterbrid

Sep 13, 2022
The Akita CLI for watching network traffic, automatically generating API specs, and diffing API specs.

Catch breaking changes faster Akita builds models of your APIs to help you: Catch breaking changes on every pull request, including added/removed endp

Jan 2, 2023
An API Client package for Studyplus for School SYNC API

Studyplus for School SYNC API Client This project is currently alpha, possibility having breaking changes. studyplus_for_school_sync_go is a API clien

Aug 2, 2021