go build & install

go build&go install
主要回顾 go build 以及 go install 的区别,以及golang项目的文件结构管理策略。

基本区别

运行go build时,package main中的main会被编译成二进制文件,默认的情况下二进制文件是放在当前目录下,使用的名字是.go 文件的名字。也可以通过-o参数来修改所输出的文件的名称。如果go build一个非package main的.go文件,build不会认为这是一个可以build的文件,也并不会产生二进制的文件。

go install命令除了编译当前main package之外还会将编译好的文件放在GOBIN目录下,同时在$GOROOT/pkg目录下还会有缓存(在go build的时候使用-i参数也会有缓存)

关于在容器中运行golang程序可能会比较多涉及到这个问题,在本地os上运行正常的程序放到容器中就无法执行,比如出现某些文件找不到的错误。

默认的情况下,go build是会按照 static linked 方式来运行,也就是说:this means it embed all the code that they need to run, including all dependencies. This contrasts withdynamically linked programs, which don’t contain some basic libraries (like the “libc”) and use a system-wide copy which is resolved at run time.

This means that we can drop our Go compiled program in a container, without anything else, and it should work.

但是在一些特殊的包被import进来时,可能会产生一个dynamic link,这个时候把二进制文件直接放在镜像中就是无法运行的,此时的解决方式如下:

1
2
3
4
By default, if using the net package a build will likely produce a binary with some dynamic linking, e.g. to libc. You can inspect dynamically vs. statically link by viewing the result of ldd output.bin
There are two solutions I've come across:
Disable CGO, via CGO_ENABLED=0
Force the use of the Go implementation of net dependencies, netgo via go build -tags netgo -a -v, this is implemented for a certain platforms

具体可以参考这个(https://stackoverflow.com/questions/36279253/go-compiled-binary-wont-run-in-an-alpine-docker-container-on-ubuntu-host)

此外还有一些参数可以加上,比如参考这个(http://matthewkwilliams.com/index.php/2014/09/28/go-executables-are-statically-linked-except-when-they-are-not/):

1
2
3
export GO_EXTLINK_ENABLED=0
export CGO_ENABLED=0
go run -x --ldflags '-extldflags "-static"' third_party.go install -x --ldflags '-extldflags "-static"' github.com/coreos/discovery.etcd.io

再回顾Golang的项目组织结构
如果总是对开源项目做一些工作,在自己的本地最好也是按照层级目录的方式来组织项目:

1
2
3
4
5
6
Go's standard directory layout looks like:
$GOPATH/src/project1
$GOPATH/src/project2
$GOPATH/src/github.com/me/myproject
...
etc

在这种情况下,GOPATH基本上不需要根据项目的切换而进行调整,不方便的地方就是每次go install的时候会将编译出来的文件安装在 根 src 目录下的 bin 文件夹中。一般都是通过一个run脚本来控制go build时候的一些相关操作。在每一个最底层的project中会存在有govendor的目录,理想情况下是优先从这个govendor的目录下来进行依赖获取。这种布局的好处就是本地的文件路径与git上面下载下来的package的路径保持一一对应的关系,不容易出现依赖的混乱。

具体对于不同依赖的位置,govendor中的相关命令已经标记的非常清楚,并且可以通过status的方式批量地对某一类包进行操作,使用之前需要安装hg工具,这里就不再赘述。

参考资料

这一篇(https://nanxiao.gitbooks.io/golang-101-hacks/content/posts/go-build-vs-go-install.html)详细比较了go build 与 go install的行为差别

推荐文章