gopherdata/gophernotes

repl.go:1:8: error executing "go build -buildmode=plugin" in directory "/Users/me/go/src/gomacro_imports/pkg": exit status 1

kesmit13 opened this issue · 7 comments

I'm getting an error when trying to do an 'import "pkg"' in my notebook. I'm not sure what is triggering the plugin build though since this package is simply a library, not a plugin. This is using Go 1.11.1 darwin. I've imported other libraries that work fine. Is there something within a package that makes gophernotes switch to plugin mode?

All packages outside Go standard library are currently imported by compiling and loading a plugin that wraps them, so it's the normal path.

In your specific case, compiling the plugin fails but the exact reason is not visible in the error message.

If the package you are trying to import is publicly available, please tell its full name so we can try to reproduce the problem.

Otherwise the only option coming to my mind is to download and install gomacro - the Go interpreter used internally by gophernotes - and try to import the same package from its command line: the error message produced by "go build -buildmode=plugin" should then be visible

It's not publicly available, but when I do try to build manually with plugin mode, I get the following:

-buildmode=plugin requires exactly one main package

Which is correct, I don't have a main package, but that's because it's simply a library.

That's correct and expected, the main package wrapping the library is autogenerated by gophernotes and written to "/Users/me/go/src/gomacro_imports/pkg" which is then compiled with "go -buildmode=plugin"

I see the problem now.

pkg.go:12:2: use of internal package pkg/internal/pb not allowed

That's unfortunate...

It means that your pkg exports an API that accepts/returns/publishes types from the internal package.

If you can avoid (indirectly) publishing the internal package, it should work.

Or in alternative, rename "internal" to some other name

This is really a limitation of gomacro. I was able to rework my package to not use internal variables in the API and it's working now.

I found the reason: when gomacro compiles a plugin in order to import a package, it declares a proxy for each interface found in the package.

For example, if package foo contains the following

package foo
import "foo/internal"
type Fooer interface {
  Bar() internal.Baz
}

Then gomacro creates a plugin containing

package main
import (
  . "reflect"
  foo "foo"
  internal "foo/internal"
)
type Package = struct {
  Binds map[string]Value
  Types map[string]Type
  // Untypeds ...
  Proxies map[string]Type
}
var Packages = make(map[string]Package)

Now, filling Package.Binds and Package.Types is always easy - in this case, we just need to run

func init() {
  Packages["foo"] = Package{
    Binds: nil,
    Types: map[string]Type{
      "Fooer": TypeOf((foo.Fooer *)(nil)).Elem(),
    },
    Proxies: map[string]Type{
      "Fooer": TypeOf((Proxy_Fooer *)(nil)).Elem(),
    },
  }
}

The difficult part is declaring a type Proxy_Fooer that implements the interface Fooer: it contains a function that returns an internal.Baz, but Go compiler rejects any attempt to directly refer internal.* so implementing such interface is not trivial, especially inside automatic code generation.

So yes, it's a gomacro limitation, and it's difficult to solve - except for the obvious solution of not declaring a proxy that implements an exported interface if such interface uses forbidden packages.