wink

有的鸟是不会被关住的, 因为它们的羽毛太耀眼了.

  • main

wink

有的鸟是不会被关住的, 因为它们的羽毛太耀眼了.

  • main

golang vendor

2016-04-07

golang vendor是为了解决golang依赖的问题的, 因为golang的依赖都是以源码的形式存在的, 所以如果别的项目变更了API, 这个时候很有可能会让你的代码无法通过编译. 在vendor出来之前, 一般都是用godep来解决的, godep是把这些依赖的源代码复制到当前的Godeps目录, 然后go build 变成 godep go build

godep算是一个妥协的产物, 但它毕竟更改了go build的原义, 所以这个方案还是不够满意, 所以vendor来了. go1.5需要通过GO15VENDOREXPERIMENT=1环境变量开启, go1.6默认开启

golang文档是这样说的:

Go 1.6 includes support for using local copies of external dependencies to satisfy imports of those dependencies, often referred to as vendoring.

Code below a directory named “vendor” is importable only by code in the directory tree rooted at the parent of “vendor”, and only using an import path that omits the prefix up to and including the vendor element.

Here’s the example from the previous section, but with the “internal” directory renamed to “vendor” and a new foo/vendor/crash/bang directory added:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/home/user/gocode/
src/
crash/
bang/ (go code in package bang)
b.go
foo/ (go code in package foo)
f.go
bar/ (go code in package bar)
x.go
vendor/
crash/
bang/ (go code in package bang)
b.go
baz/ (go code in package baz)
z.go
quux/ (go code in package main)
y.go

The same visibility rules apply as for internal, but the code in z.go is imported as “baz”, not as “foo/vendor/baz”.

Code in vendor directories deeper in the source tree shadows code in higher directories. Within the subtree rooted at foo, an import of “crash/bang” resolves to “foo/vendor/crash/bang”, not the top-level “crash/bang”.

Code in vendor directories is not subject to import path checking (see ‘go help importpath’).

When ‘go get’ checks out or updates a git repository, it now also updates submodules.

Vendor directories do not affect the placement of new repositories being checked out for the first time by ‘go get’: those are always placed in the main GOPATH, never in a vendor subtree.

In Go 1.5, as an experiment, setting the environment variable GO15VENDOREXPERIMENT=1 enabled these features. As of Go 1.6 they are on by default. To turn them off, set GO15VENDOREXPERIMENT=0. In Go 1.7, the environment variable will stop having any effect.

See https://golang.org/s/go15vendor for details.

举个例子

假设有如下的目录:

1
2
3
4
5
6
7
8
9
10
11
./main.go
./vendor
./vendor/a
./vendor/a/a.go
./vendor/b
./vendor/b/b.go
./vendor/c
./vendor/c/c.go
./vendor/c/vendor
./vendor/c/vendor/a
./vendor/c/vendor/a/a.go

main.go 内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main
import (
"a"
"b"
"c"
"fmt"
)
func main() {
fmt.Println("main.main()")
fmt.Println("main: a.A ==", a.A)
fmt.Println("main: a.Inc() ==", a.Inc())
fmt.Println("main: b.B ==", b.B)
fmt.Println("main: c.C ==", c.C)
}

vendor/a/a.go 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package a
import "fmt"
var A = 1
func Inc() int {
A++
return A
}
func init() {
fmt.Println("Init package a")
fmt.Println("a: A ==", A)
}

vendor/b/b.go looks like:

1
2
3
4
5
6
7
8
9
10
11
12
package b
import "a"
import "fmt"
var B = 2
func init() {
fmt.Println("Init package b")
fmt.Println("b: a.A ==", a.A)
fmt.Println("b: a.Inc() ==", a.Inc())
}

vendor/c/c.go looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
package c
import "a"
import "fmt"
var C = 3
func init() {
fmt.Println("Init package c")
fmt.Println("c: C ==", C)
fmt.Println("c: a.A ==", a.A)
fmt.Println("c: a.Inc() ==", a.Inc())
}

最后 vendor/c/vendor/a/a.go looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package a
import "fmt"
var A = 100
func Inc() int {
A++
return A
}
func init() {
fmt.Println("Init c/vendor/a")
fmt.Println("c/vendor/a: A ==", A)
}

如果不带上vendor特性, 运行main.go, 你会得到:

1
2
3
4
5
6
7
8
9
10
$ go run main.go
main.go:4:2: cannot find package “a” in any of:
/usr/local/go/src/a (from $GOROOT)
/Users/emuller/go/src/a (from $GOPATH)
main.go:5:2: cannot find package “b” in any of:
/usr/local/go/src/b (from $GOROOT)
/Users/emuller/go/src/b (from $GOPATH)
main.go:6:2: cannot find package “c” in any of:
/usr/local/go/src/c (from $GOROOT)
/Users/emuller/go/src/c (from $GOPATH)

但是如果开启vendor:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ GO15VENDOREXPERIMENT=1 go run main.go
Init package a
a: A == 1
Init package b
b: a.A == 1
b: a.Inc() == 2
Init c/vendor/a
c/vendor/a: A == 100
Init package c
c: C == 3
c: a.A == 100
c: a.Inc() == 101
main.main()
main: a.A == 2
main: a.Inc() == 3
main: b.B == 2
main: c.C == 3

总结一下就是, b和main都会使用vendor/a包, 而c本身会使用它自己的vendor, vendor/c/vendor/a

那怎么保存我的依赖到vendor目录呢?

官方并没有提供这种工具, 显然是要我们自己来处理了, 我们可以用这个工具https://github.com/kardianos/govendor就可以啦

govendor Quick Start

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Setup your project.
cd "my project in GOPATH"
govendor init
# Add existing GOPATH files to vendor.
# 执行完这一步, 就会把我们的依赖package复制一份到vendor目录了
govendor add +external
# View your work.
govendor list
# Look at what is using a package
govendor list -v fmt
# Specify a specific version or revision to fetch
govendor fetch golang.org/x/net/context@a4bbce9fcae005b22ae5443f6af064d80a6f5a55
govendor fetch golang.org/x/net/context@v1 # Get latest v1.*.* tag or branch.
govendor fetch golang.org/x/net/context@=v1 # Get the tag or branch named "v1".
# Update a package to latest, given any prior version constraint
govendor fetch golang.org/x/net/context
赏

thanks

  • golang
  • tech

扫一扫,分享到微信

微信分享二维码
etcd client DNS Discovery
tidb笔记2
© 2019 wink