gopherdata / gophernotes

The Go kernel for Jupyter notebooks and nteract.
MIT License
3.82k stars 265 forks source link

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

Closed kesmit13 closed 5 years ago

kesmit13 commented 5 years ago

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?

cosmos72 commented 5 years ago

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

kesmit13 commented 5 years ago

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.

cosmos72 commented 5 years ago

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"

kesmit13 commented 5 years ago

I see the problem now.

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

That's unfortunate...

cosmos72 commented 5 years ago

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

kesmit13 commented 5 years ago

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.

cosmos72 commented 5 years ago

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.