Go Modules
Go 1.11 引入了新特性,一个新的依赖管理系统。Google 引入了 Go 模块作为GOPATH 的替代方案,用于版本控制和包分发。
Go modules(这就是 Go 使用环境变量名称的原因GO111MODULE
:表示使用 Go 1.11 模块)。
模块是相关Go包的集合。modules是源代码交换和版本控制的单元。go命令直接支持使用modules,包括记录和解析对其他模块的依赖性。modules替换旧的基于GOPATH的方法来指定在给定构建中使用哪些源文件。
- GO111MODULE有三个值:off、on 和 auto(默认值) 在使用模块的时候,
GOPATH
是无意义的,不过它还是会把下载的依赖储存在$GOPATH/src/mod
中,也会把go install
的结果放在$GOPATH/bin
中。GO111MODULE=off
,无模块支持,go 会从 GOPATH 和 vendor 文件夹寻找包GO111MODULE=on
,模块支持,go 会忽略 GOPATH 和 vendor 文件夹,只根据go.mod
下载依赖GO111MODULE=auto
,在$GOPATH/src
外面且根目录有go.mod
文件时,开启模块支持
- GOPROXY 由于网络监管系统,Go 生态系统中有着许多中国 Gopher 们无法获取的模块,比如最著名的
golang.org/x/...
。并且在中国大陆从 GitHub 获取模块的速度也有点慢。因此需要配置GOPROXY来加速Module依赖下载,这里使用goproxy.cn代理,详细介绍:传送门 注: 推荐将 GO111MODULE 设置为on 而不是auto- Go 1.13及以上版本
go env -w GOPROXY=https://goproxy.cn,direct
- Go 1.13以下的版本
export GOPROXY=https://goproxy.cn
- Go 1.13及以上版本
Go mod
Golang 1.11 版本引入的 go mod ,其思想类似maven:摒弃vendor和GOPATH,拥抱本地库。从 Go 1.11 开始,Go 允许在 $GOPATH/src
外的任何目录下使用 go.mod 创建项目。在$GOPATH/src
中,为了兼容性,Go 命令仍然在旧的 GOPATH 模式下运行。从 Go 1.13 开始,Module模式将成为默认模式。
1.go mod 命令
go mod
The commands are:
download download modules to local cache (下载依赖的module到本地cache))
edit edit go.mod from tools or scripts (编辑go.mod文件)
graph print module requirement graph (打印模块依赖图))
init initialize new module in current directory (再当前文件夹下初始化一个新的module, 创建go.mod文件))
tidy add missing and remove unused modules (增加丢失的module,去掉未用的module)
vendor make vendored copy of dependencies (将依赖复制到vendor下)
verify verify dependencies have expected content (校验依赖)
why explain why packages or modules are needed (解释为什么需要依赖)
2.新项目
你可以在GOPATH
之外创建新的项目
go mod init packagename
可以创建一个空的go.mod
,然后你可以在其中增加
require github.com/smallnest/rpcx latest
依赖,或者像上面一样让go自动发现和维护。
go mod download
可以下载所需要的依赖,但是依赖并不是下载到$GOPATH
中,而是
$GOPATH/pkg/mod
中,多个项目可以共享缓存的module。
3.老项目
假设你已经有了一个go 项目, 比如在
$GOPATH/src/github.com/smallnest/rpcx
下, 你可以使用
go mod init github.com/smallnest/rpcx
在这个文件夹下创建一个空的go.mod
(只有第一行 module github.com/smallnest/rpcx
)。 然后你可以通过 go get ./...
让它查找依赖,并记录在go.mod
文件中(你还可以指定 -tags
,这样可以把tags的依赖都查找到)。 通过
go mod tidy
也可以用来为go.mod
增加丢失的依赖,删除不需要的依赖,但是我不确定它怎么处理tags
。 执行上面的命令会把go.mod
的latest
版本换成实际的最新的版本,并且会生成一个go.sum
记录每个依赖库的版本和哈希值。
GO111MODULE:行为
到目前为止,GO111MODULE
为每个 Go 操作明确设置了环境变量。无变量集,缺省值GO111MODULE
是auto
,它的行为如下:
Golang环境变量
- GOROOT:go的安装路径 在
~/.bash_profile
中添加下面语句配置GOROOT
环境变量
要执行go命令和go工具, 就要配置go的可执行文件的路径:GOROOT=/usr/local/go export GOROOT
注:$PATH windows用export $PATH:$GOROOT/bin
;
符号分割, mac和类unix用:
符号分割 - GOPATH: go的工作路径 可以在自己的用户目录下面创建一个目录, 如go
在cd ~ mkdir go
~/.bash_profile
中添加如下语句:
不要把export GOPATH=/Users/username/go
GOPATH
设置成go的安装路径,GOPATH
下主要包含三个目录:bin
pkg
src
注:Go 1.8 版本之前,GOPATH 环境变量默认是空的;1.8版本之后,默认路径是:
$HOME/go
- src: 存放源代码(比如:.go .c .h .s等)
- pkg: 编译后生成的文件(比如:.a)
- bin: 编译后生成的可执行文件, 为了方便,可以把此目录加入到
$PATH
变量中,如果有多个gopath,那么使用${GOPATH//://bin:}/bin
添加所有的bin目录
GOPATH
GOPATH 是 Go语言中使用的一个环境变量,它使用绝对路径提供项目的工作目录(Go Workspace)。
例如下面的使用GOPATH 例子
GO111MODULE=off go get github.com/google/go-cmp/cmp
tree -d -L 5 $GOPATH # assume tree is installed
/go
├── bin
├── pkg
└── src
└── github.com
└── google
└── go-cmp
GO111MODULE=off 表示Go 运行时使用就的GOPATH 模式,而不是Go modules。
因此,go get
github.com/google/go-cmp/cmp
按照 GOPATH 的模式下载包到$GOPATH/src目录。
GO111MODULE=on go get github.com/google/go-cmp/cmp
tree -d -L 5 $GOPATH
/go
`-- pkg
|-- mod
| |-- cache
| | `-- download
| | |-- github.com
| | |-- golang.org
| | `-- sumdb
| `-- github.com
| `-- google
| `-- go-cmp@v0.5.6
`-- sumdb
`-- sum.golang.org
你会发现启用了go modules后的目录结构和没有启用Go modules的目录结构不同。
GOPATH 已经过时了
Go 1.11 is released Go 1.11 is released - go.dev
2018-8-24 发布
提到新的moudules ,是GOPATH的替代方案。modules 提供对版本控制和包分发的支持
在 Go 1.11 之前,所有项目,而不仅仅是依赖包,都必须在$GOPATH
目录中。在这里,我尝试在$GOPATH 环境执行
实战:
工作场景:一个没有启用Go moudules,在$GOPATH
外面 执行
例如,我在执行一个项目命名testproject
,有一个包main
有两个文件main.go
和test_func.go
如下:
main.go:
package main
func main() {
TestFunc()
}
test_func.go:
package main
import "k8s.io/klog"
func TestFunc() {
klog.Infoln("Hello Go Modules!")
}
下面运行
#先直接运行,会报错,没有找到对应包
GO111MODULE=off go run .
test_func.go:3:8: cannot find package "k8s.io/klog" in any of:
/usr/local/go/src/k8s.io/klog (from $GOROOT)
/Users/YourUserName/go/src/k8s.io/klog (from $GOPATH)
下载对应的包:
/anywhere/outside/gopath/testproject $GO111MODULE=off go get k8s.io/klog
/anywhere/outside/gopath/testproject $GO111MODULE=off go run .
I1201 15:32:25.189443 28174 test_func.go:7] hello go Modules!
不过,确实可以运行,整个项目在$GOPATH 外面。实际上,所有项目都必须在的原因$GOPATH
是由于subpackages。一个main
包不是这样的,因此它不受此限制的限制。
有问题的场景:Go 有多个包的项目,在外面 $GOPATH
让我们修改它以获得一个子包test
:
$ tree /anywhere/outside/gopath/testproject
/anywhere/outside/gopath/testproject
├── main.go
└── test
└── func.go
main.go:
package main
import "test"
func main(){
test.TestFunc()
}
test/func.go
package test
import "k8s.io/klog"
func TestFunc(){
klog.Infoln("hello go Modules!")
}
O111MODULE=off go run .
main.go:2:8: cannot find package "test" in any of:
/usr/local/go/src/test (from $GOROOT)
/Users/yourUserHome/go/src/test (from $GOPATH)
如果没有启用go modules,我们无法在main package中找到test的这个包,让它工作的唯一办法是,将项目的移动到$GOPATH,
% tree $GOPATH/src/mytest.io/testproject/
/go/src/mytest.io/testproject/
├── main.go
└── test
└── func.go
现在我们可以指定test
包的路径:mytest.io/testproject/test
基于$GOPATH/src
. 所以 main.go 可以修改为:
package main
import "mytest.io/testproject/test"
func main(){
test.TestFunc()
}
运行项目
% GO111MODULE=off go run main.go
I1201 15:44:39.271735 29038 func.go:7] hello go Modules!
注意main.go在GOPATH外也是没有问题的。主要是子package的目录要在GOPATH下
回到Go modules模式
Go Modules
在$GOPATH外,
/anywhere/outside/gopath/testproject % go mod init mytest.io/testproject
main.go
package main
import "mytest.io/testproject/test"
func main() {
test.TestFunc()
}
test/func.go
package test
import "k8s.io/klog"
func TestFunc(){
klog.Infoln("hello go Modules!")
}
/anywhere/outside/gopath/% tree testproject
testproject
├── go.mod
├── go.sum
├── main.go
└── test
└── func.go
运行
/anywhere/outside/gopath/testproject % GO111MODULE=on go run .
I1201 16:01:43.240510 30265 func.go:7] hello go Modules!
如上,我们将项目初始化为 mytest.io/testproject (go mod init mytest.io/testproject)也就是go.mod的目录定义为mytest.io/testproject ,即使在$GOPATH外面,go run 命令搜索,mytest.io/testproject/test ,即项目根目录下面的test子目录搜索包,而不是$GOPATH中。
即搜索
/anywhere/outside/gopath/testproject/test
目录,test/func.go的 package 需要为test 不能为其他的。
参考:
-
如何编写 Go 代码(使用 GOPATH)How to Write Go Code (with GOPATH) https://golang.org/doc/gopath_code.html ↩︎
-
Go 中的版本控制原则 The Principles of Versioning in Go:research!rsc: The Principles of Versioning in Go (Go & Versioning, Part 11) ↩︎
-
Go 1.13 发布说明 Go 1.13 Release Note:https://golang.org/doc/go1.13#modules ↩︎