Please answer these questions before submitting your issue. Thanks!
go version)?go version go1.8rc3 linux/amd64
go env)?GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/fsenart/projects"
GORACE=""
GOROOT="/home/fsenart/tools/go1.8rc3"
GOTOOLDIR="/home/fsenart/tools/go1.8rc3/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build101924921=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"
PKG_CONFIG="pkg-config"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
A link on play.golang.org is best.
Scenario:
Result:
// $GOPATH/src/issues/issue_vendor/main .go
package main
import (
"foo"
"plugin"
"reflect"
)
func main() {
p, err := plugin.Open("plugin.so")
if err != nil {
panic(err)
}
f, err := p.Lookup("F")
if err != nil {
panic(err)
}
println(reflect.TypeOf(f).Elem() == reflect.TypeOf((*foo.Bar)(nil)))
}
// $GOPATH/src/issues/issue_vendor/plugin.go
package main
import "C"
import "foo"
var F *foo.Bar
// Copy the file one time in the GOPATH and one time in the vendor directory
// $GOPATH/src/foo/bar.go
// $GOPATH/src/issues/issue_vendor/vendor/foo/bar.go
package foo
type Bar struct{}
# $GOPATH/src/issues/issue_vendor/Makefile
test1: build1
@./main
test2: build2
@./main
build1: clean
@go build -o main main.go
@go build -buildmode=plugin -o plugin.so plugin.go
build2: clean
@mv vendor _vendor
@go build -o main main.go
@mv _vendor vendor
@go build -buildmode=plugin -o plugin.so plugin.go
clean:
@rm -rf main
make test1 should print truemake test2 should print truemake test1 prints truemake test2 prints false@ianlancetaylor is there any chance to add this issue to the 1.8.1 milestone as it makes the usage of vendoring nearly impossible in the context of plugins?
Adding the 1.8.1 milestone to this issue won't fix the problem. When and if we have a fix on tip, then if the fix is simple and safe, we can consider adding it to 1.8.1.
As mentioned in https://github.com/golang/go/issues/19233, this sounds like the behavior described at https://github.com/golang/go/issues/12432. Unless this behavior is set to change in 1.9, this seems like a non-issue, albeit annoying in the context of plugins.
@edaniels it really seems like the behavior you mentioned but the nuance resides in the fact that this issue happens on run time. I understand that this is the "plugin" version of the behavior, but this is also far more annoying for end users as it can't be detected at compile time.
Plugins come with a lot of advantages but also open the pandora's box. IMHO, the main advantage is to allow the separation between a core program and a myriad of run time functionalities developed by different folks but this kind of issues constraint both parties to know about each others and conform to a set of common dependencies rules.
Maybe we should come up with a freshen look about vendoring in the context of plugins.
I totally agree. It's probably a discussion for another issue but it seems like this is an issue for vendoring in general and maybe the go tool chain should be more aware of package identity in the future. To make plugins more useful something will need to be done.
If you built this as a single program, there would be two separate packages for foo. There would be two instances of any global variables, and the types you are comparing would be different.
It seems very strange to me that plugins would de-duplicate vendored packages. What if a plugin has vendored a different version of a package because it wants to use a different version? The only thing we could do in plugin.Open is fail in this case, because the packages wouldn't be compatible.
It would also be impossible to build a plugin that vendors two different versions of a package under different subtrees. I can't say I've ever done this, but all of this is possible with the vendor directory in usual programs, so I think it should work in plugins also.
Sorry that we didn't look at this issue in the Go1.10 cycle, but I think we might have to move this to Go1.11. What do you think @crawshaw?
@crawshaw is on leave, so that's a yes: moving to Go 1.11.
Oh, right, I had forgotten. Thanks @bradfitz.
As this issue has been assigned to a milestone, what exactly is proposed to be changed/addressed?
@crawshaw says:
It would also be impossible to build a plugin that vendors two different versions of a package under different subtrees.
If this is the case, I'm struggling to find the relevance in the Plugin package entirely. If plugins are to always be tightly coupled with the dependencies of the project that loads it, why would I not just include that code in the parent project's source.
I am not sure if this behaviour is a bug or a feature. On one hand it prevents a base application and a plugin from passing shared types to each other, but on the other hand it allows two plugins to vendor different versions of the same dependency and still be loaded. Without this behaviour, the plugin loader will complain that the second plugin was built with a different version of the shared dependency.
The issue with a shared type being returned by a plugin function to the base could be worked around by either pulling that specific dependency out of vendor/ into the GOPATH or by using a combination of interfaces, type assertion and reflection to convert the two types.
Also should the discussion on this issue happen here or in #20481?
I tried using plugins a while ago and gave up. I had 2+ plugins (and the main application too) that use kubernetes libraries and kubernetes uses glog. So, 2+ glog copies, each trying to add a flag to the flag package. A flag cannot be added more than once -> glog/stdlib panics. Plugins are completely unuseable in such situation.
I think shared mutable state should be removed from standard library in Go 2.
A similar thing happens for plugins that import x/net/trace which in its init() adds some http handlers to the http.DefaultServeMux. So if you load 2 plugins that have vendored x/net/trace, the init() will panic because it gets executed twice and you can't have two handlers registered at the same route.
The only way to "resolve" this right now seems to be to reassign http.DefaultServeMux before loading each plugin.
Are there any updates on this? I agree with the commenters above that this renders plugins useless for a lot of use cases. There should at least be a warning at the top of the documentation of the plugins package that when working with vendored code you cannot share types between different packages.
Is there any progress? It's still very difficult to use plugins
It seems like plugins are effectively dead, support wise. They've received little mention since their release.
then are there any other alternative we can leverage?
Most helpful comment
As this issue has been assigned to a milestone, what exactly is proposed to be changed/addressed?
@crawshaw says:
If this is the case, I'm struggling to find the relevance in the
Pluginpackage entirely. If plugins are to always be tightly coupled with the dependencies of the project that loads it, why would I not just include that code in the parent project's source.