On Mon, Mar 20, 2017 at 11:53 PM, Basile Starynkevitch
<bas...@starynkevitch.net> wrote:
>
> On Monday, March 20, 2017 at 8:36:04 AM UTC+1, Basile Starynkevitch wrote:
>>
>> Plugins and packages in Go
>>
>> The package concept is a core concept of Go since every source file
>> belongs to some package (with main being a special case) and often imports
>> several other ones.
>> Practically speaking, a Go plugin is likely to call functions from some
>> package defined by the main program and having itself (the plugin) some
>> packages. So the
>>  tiny example in plugin documentation is a bit too naive (even if it is
>> calling fmt.Printf). A realistic example would be a program having not only
>> some main function
>> in its main package, but also defining some purple package having some Foo
>> public function called as purple.Foo from main and having a Bar public
>> function
>> (with purple.Bar called from the plugin). The plugin would have not only
>> some public F function in its main package (which should call purple.Bar
>> from the main
>> plugin-loading program) but also some plugin specific yellow package with
>> a public Y function called (as yellow.Y) from F. I hope that such a
>> realistic example will be
>> given in the documentation of future Go 1.9.
>>
>>
>
> To be more concrete, Here is the scenario I am thinking of:
>
> the main (plugin loading) program has some purple package in a purple.go
> file (probably in some purple/ directory):
> /// file purple.go of the main program
> package purple
> import "fmt"
>
> func Foo(x int) { // called from main
>   fmt.Printf("purple.Foo has x=%d\n", x)
> }
>
> func Bar(s string) { // called from plugin
>   fmt.Printf("purple.Bar has s=%q\n", s)
> }
>
> /// eof purple.go
>
>
> Let's assume we have a /tmp/plugin.so binary plugin. Is is made from
> plugintop.go and pluginyellow.go and it has a yellow package and it calls
> purple.Bar. So here is pluginyellow.go :
> //file pluginyellow.go of the plugin
> package yellow // the yellow package is in the plugin
> import "fmt"
>
> // we import the purple package from the main program
> import "purple"
>
> // in real life, yellow is importing a lot more of existing packages (e.g.
> sql)....
>
> function F() { // this function is called from plugintop.go
>   fmt.Printf("in yellow.F\n")
>   purple.Bar("from yellow.F")
>   fmt.Printf("ending yellow.F\n")
> }
>
> function Gee () {
>   fmt.Printf("in yellow.Gee calling Foo with 345\n")
>   purple.Foo(345);
>   fmt.Printf("end yellow.Gee")
> }
>
> function GG() {  // this ls looked up by the main program
>   fmt.Printf("in yellow.GG\n")
> }
>
> // eof pluginyellow.go
>
>
> And here is the top file of the plugin, plugintop.go (I don't know in what
> directory it should go exactly):
> // file plugintop.go
> package main
>
> import "fmt"
> import "yellow"
> func init() {
>   fmt.Printf("init of plugintop\n")
>   yellow.F()
>   fmt.Printf("end of init of plugintop\n")
> }
>
> func Pub() { // this is looked up by the main program
>   fmt.Printf("in Pub of plugintop calling GG\n")
>   yellow.GG()
>   fmt.Printf("in Pub of plugintop ending\n")
> }
> // eof plugintop.go
>
> Our main program (which is loading the plugin) defines a purple package in
> some purpleprog.go file
> // file purpleprog.go
> package purple
> import "fmt"
> func Foo(x int) {
>   fmt.Printf("in purple.Foo x=%d\n", x)
> }
>
> func Bar(m string) {
>   fmt.Printf("in purple.Bar m=%q\n", m)
> }
> // eof purpleprog.go
>
>
> Of course we need a main in our program, file mainprog.go is loading the
> /tmp/plugin.so plugin
> // file mainprog.go
> package main
> import "fmt"
> import "plugin"
> import "purple"
>
> func main() {
>   fmt.Printf("start of main in mainprog.go\n")
>   plug, err := plugin.Open("/tmp/plugin.so")
>   fmt.Printf("in mainprog plug=%v err=%v\n", plug, err)
>   if err != nil {
>     panic(fmt.Errorf("plugin.Open /tmp/plugin.so failed in mainprog with
> %v", err))
>   }
>   fmt.Printf("mainprog before call purple.Foo with 12751\n")
>   purple.Foo(12751)
>   fmt.Printf("mainprog after call purple.Foo with 12751\n")
>   symbPub, err := plug.Lookup("Pub")
>   fmt.Printf("in mainprog symbPub=%v err=%v\n", symbPub, err)
>   if err != nil {
>     panic(fmt.Errorf("Lookup of pub failed with %v", err))
>   }
>   funpub := symbPub.(func())
>   fmt.Printf("mainprog before calling funpub=%v\n", funpub)
>   funpub()
>   symbGG, err := plug.Lookup("yellow.GG")
>   fmt.Printf("in mainprog symbGG=%v err=%v\n", symbGG, err)
>   if err != nil {
>     panic(fmt.Errorf("Lookup of yellow.GG failed with %v", err))
>   }
>   funGG := symbPub.(func())
>   fmt.Printf("mainprog before calling funGG=%v\n", funGG)
>   funGG()
>   fmt.Printf("end of mainprog\n")
> }
> // end of file mainprog.go
>
> In real life, the purple package inside the main program which is also used
> by the plugin is actually using a lot of code in many other packages (e.g.
> go-sqlite, os/exec, and so on).
>
> So here are my questions:
>
> What is (or are) the exact build command of the main program (we need to
> compile both mainprog.go & purpleprog.go files...). I suspect that
> -buildmode=shared would be useful when compiling purpleprog.go but I am not
> sure. I suspect that -linkshared should be used when compiling mainprog.go
>
> What is (or are) the exact build command of the plugin (we need to compile
> both pluginyellow.go & plugintop.go files...). I expect the plugin to be
> compiled failrly quickly so I would like the purple package (and all its
> dependencies) to be external to it, and not be compiled with the plugin
> (just linked to it). I really want to avoid compiling twice the purple
> package (which actually is quite big and using many other packages).
>
>
> I might have misunderstood how plugins work. IMHO there should be some way
> to indicate to the compiler -when compiling the plugin- that the purple
> package is available in the main program. Perhaps the -buildmode=plugin mode
> might also accept a -loaded-from-program argument giving the main executable
> program that is loading it (so that the compiler can reuse and inspect
> packages available in the main program, like purple). Perhaps there should
> be some way (I was suggesting import "purple" // import "*" in my previous
> message) to indicate inside the source code of a plugin that a package
> should be imported and available from the main program.
>
> (perhaps I might have identified an issue in the current Go1.8
> implementation of plugins; if I did, please tell, and say me if I could hope
> that issue to be covered in Go1.9)
>
> Regards, thanks for reading!
>
> Basile Starynkevitch (France); my email is bas...@starynkevitch.net if you
> need to answer me privately.
>
> PS. I really hope that some Go plugin guru would answer my messages!


Well, there are no Go plugin gurus, and to be honest I'm starting to
think it was a mistake to let the plugin package into 1.8.  I know
that is not what you want to hear.  It's in because the API seems OK,
but it has become clear that there are many related issues that we
haven't thought about.

If I understand your example, your plugin imports a package, and your
main program also imports the same package, and it's unfortunate that
the package gets linked into both the plugin and the main program.
That is true.  I think the only way to deal with is going to be to
compile the shared package with -buildmode=shared, and build both the
plugin and the main program with -linkshared.  In fact that is what
you suggest.  Does it work?  I don't know whether it does or not.

Ian

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to