撸了一段时间的 Go,受不了 Go 的包管理知识点,觉得非常混乱,特意写下本文章进行梳理!
Go 的 workspace
官方文档说:
The go tool requires you to organize your code in a specific way.
翻译过来就是:如果要使用 go tool(go 命令行),那么就必须将代码组织成一种特殊的形式。
特殊形式如下:
1 | $GOPATH |
src 这个目录一般由我们自己手动创建。pkg 和 bin 不用我们手动创建,在执行特定的 go 命令时会自动创建。
在使用 go get 命令时:
1 | go get github.com/rogpeppe/godef |
执行这条命令时,会将 godef 的源码下载到 src 中,然后进行编译,
由于 godef 是一个工具(tool),所以会在 bin 目录生成 godef.exe 可执行的二进制文件。
(项目源码中 有 package main 和 main 函数?)
1 | go get github.com/sony/sonyflake |
执行这条命令时,会将 sonyflake 的源码下载到 src 中,然后进行编译,
由于 sonyflake 是一个类库(library),所以会在 bin 目录生成 sonyflake.a 包的二进制文件。
(项目源码中 无 package main 和 main 函数?)
生成包的二进制文件是 为了避免重复编译 和 为了平台交叉编译。
1 | $GOPATH |
1 | package main |
如果 hello.go 是上面代码,
那么执行 go install 则会在 bin 生成 ProjectA.exe 可执行二进制文件。
1 | package ProjectA |
如果 hello.go 是上面代码,
那么执行 go install 则会在 pkg 生成 ProjectA.a 包的二进制文件。
随便总结一下,go run、 go build、 go install的区别
go run:编译并直接运行程序(所以只能对有 main 函数的文件使用),它会产生一个临时文件(但不会生成 .exe 文件),
直接在命令行输出程序执行结果,方便用户调试。
go build:用于测试编译包,主要检查是否会有编译错误,
如果是一个可执行文件的源码(即是 main 包),就会直接生成一个可执行文件。
go install:作用有两步:
第一步是编译导入的包文件,所有导入的包文件编译完才会编译主程序;
第二步是将编译后生成的可执行文件放到 bin 目录下,编译后的包二进制文件放到 pkg 目录下。
多项目包依赖问题
我们再次来看看 Go 的 workspace
1 | $GOPATH |
如果 ProjectA 和 ProjectB 已经进行了一段时间,进入维护阶段,
然后我们开始新的 ProjectZ,导入包的时候,包的作者进行了一些更新,
因此导致 ProjectA 和 ProjectB 编译不通过了,需要进行修改和回归测试…
多么蛋疼的一件事…
或者可以给 $GOPATH 设置多个变量 (目录路径),
1 | $GOPATH_A |
1 | $GOPATH_Z |
但用 go get 获取获取的包只会存在 $GOPATH_A 的 src 中
Vendor 解决方案
注意,本方案是是指 go 的版本在 1.6 - 1.11 之间,go1.11 会有新的解决方案。
而写这博文的时候,go 的版本是 1.11.4,所以 Vendor 解决方案只做了解。
官方提供的解决方案是:每个项目下有自己的 vendor 文件夹,这样就能独立依赖包的引用。
然后在使用编译命令的时候,go查找依赖包路径的规则如下:
- 当前包下的vendor目录。
- 向上级目录查找,直到找到src下的vendor目录。
- 在GOPATH下面查找依赖包。
- 在GOROOT目录下查找。
1 | $GOPATH |
如果我们用 go get 命令,依赖包还是会下载到src目录中,
那么怎么将包下载到项目中的 vendor 目录中和进行管理 (查看,更新,删除) 呢?
有三个工具:
- dep (这个是官方提供的工具)
- glide
- govendor
由于 go1.11 有新的解决方案,而且上面三个工具的作者也表明不再更新,最多维护一段时间。
所以我们来看看新的 Modules 解决方案。
为了更加详细地讲解 Modules,所以决定用开多一篇新的博文~