撸了一段时间的 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,所以决定用开多一篇新的博文~