├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md └── wtf.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | script: go build 4 | 5 | go: 6 | - 1.6 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Peter Bourgon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wtf [![Build Status](https://travis-ci.org/peterbourgon/wtf.svg?branch=master)](https://travis-ci.org/peterbourgon/wtf) 2 | 3 | wtf is a package that demonstrates what I think is a serious problem with how Go vendoring currently works. 4 | As a result of this problem, the package fails to build. 5 | 6 | ## Problem 7 | 8 | 1. package wtf is a library, designed to be imported by other code. 9 | Vendoring is the responsibility of binary (i.e. package main) authors. 10 | Therefore, package wtf doesn't (and shouldn't) vendor its dependencies. 11 | 12 | 2. package wtf provides a type that should satisfy a third party interface. 13 | Specifically, [etcdserverpb.KVServer](https://godoc.org/github.com/coreos/etcd/etcdserver/etcdserverpb#KVServer). 14 | 15 | 3. Methods in KVServer take [context.Context](https://godoc.org/golang.org/x/net/context) as a parameter. 16 | Therefore, wtf.go imports `"golang.org/x/net/context"`. 17 | Note that etcdserverpb [imports the same literal path](https://github.com/coreos/etcd/blob/1d698f093f490cade87b7e5b33e8525367f49f0c/etcdserver/etcdserverpb/rpc.pb.go#L18). 18 | 19 | 4. Note that etcd has [a vendor folder](https://github.com/coreos/etcd/tree/master/vendor) in the root of its repository, and that vendor folder includes golang.org/x/net/context. 20 | 21 | At build time, the go toolchain resolves the import path `"golang.org/x/net/context"` as specified in etcdserverpb to the copy stored at $GOPATH/src/github.com/coreos/etcd/vendor/golang.org/x/net/context. 22 | But, it resolves the same import path as specified in wtf.go to _a different copy_, by default the plain $GOPATH/src/golang.org/x/net/context. 23 | Note that it's _impossible_ for the import path specified in wtf.go to resolve to the vendored copy in github.com/coreos/etcd/vendor. 24 | 25 | Because the same import path resolves to two different concrete packages, and no type coercion is possible, the build fails: 26 | 27 | ``` 28 | ./wtf.go:38: cannot use Server literal (type Server) as type etcdserverpb.KVServer in assignment: 29 | Server does not implement etcdserverpb.KVServer (wrong type for Compact method) 30 | have Compact("golang.org/x/net/context".Context, *etcdserverpb.CompactionRequest) (*etcdserverpb.CompactionResponse, error) 31 | want Compact("github.com/coreos/etcd/vendor/golang.org/x/net/context".Context, *etcdserverpb.CompactionRequest) (*etcdserverpb.CompactionResponse, error) 32 | ``` 33 | 34 | ## More context 35 | 36 | - [My golang-nuts post](https://groups.google.com/d/msg/golang-nuts/AnMr9NL6dtc/UnyUUKcMCAAJ) 37 | - [A similar golang-dev post](https://groups.google.com/forum/#!msg/golang-dev/WebP4dLV1b0/Lhk4hpwJEgAJ) 38 | - https://github.com/coreos/etcd/issues/4913 39 | 40 | ## Solutions 41 | 42 | ### Restructure etcd repos 43 | 44 | coreos/etcd could be split into two repos: 45 | 46 | - coreos/etcdbin, containing all `package main` binaries, with vendoring 47 | - coreos/etcdlib, containing all non-`package main` libraries, without vendoring 48 | 49 | This imposes a burden on the etcd developers, but appears to be the best solution for consumers. 50 | 51 | ### Use a vendoring tool to build 52 | 53 | Daniel Theophanes notes that 54 | 55 | > The package won't [build] with a git pull or `go get`, but it will compile correctly when you run `govendor get github.com/peterbourgon/wtf`. Ensure you've run `go get -u github.com/kardianos/govendor` first. 56 | 57 | Requiring a third-party build tool to build a project that just happens to depend on a package that uses vendoring is obviously not an ideal outcome. Go developers have the expectation that `go get` will work in the general case. 58 | 59 | ### Others 60 | 61 | I'll be adding more solutions, with pros and cons, as they are discovered. 62 | -------------------------------------------------------------------------------- /wtf.go: -------------------------------------------------------------------------------- 1 | package wtf 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/coreos/etcd/etcdserver/etcdserverpb" 7 | "golang.org/x/net/context" 8 | ) 9 | 10 | // Server provides an alternate implementation of the etcd KVServer. 11 | type Server struct{} 12 | 13 | // Range implements KVServer. 14 | func (Server) Range(context.Context, *etcdserverpb.RangeRequest) (*etcdserverpb.RangeResponse, error) { 15 | return nil, errors.New("not implemented") 16 | } 17 | 18 | // Put implements KVServer. 19 | func (Server) Put(context.Context, *etcdserverpb.PutRequest) (*etcdserverpb.PutResponse, error) { 20 | return nil, errors.New("not implemented") 21 | } 22 | 23 | // DeleteRange implements KVServer. 24 | func (Server) DeleteRange(context.Context, *etcdserverpb.DeleteRangeRequest) (*etcdserverpb.DeleteRangeResponse, error) { 25 | return nil, errors.New("not implemented") 26 | } 27 | 28 | // Txn implements KVServer. 29 | func (Server) Txn(context.Context, *etcdserverpb.TxnRequest) (*etcdserverpb.TxnResponse, error) { 30 | return nil, errors.New("not implemented") 31 | } 32 | 33 | // Compact implements KVServer. 34 | func (Server) Compact(context.Context, *etcdserverpb.CompactionRequest) (*etcdserverpb.CompactionResponse, error) { 35 | return nil, errors.New("not implemented") 36 | } 37 | 38 | var _ etcdserverpb.KVServer = Server{} 39 | --------------------------------------------------------------------------------