Go Modules使用
Go Modules在Go 1.12版本后默认支持。如果是1.12之前的版本,需要设置一下GO111MODULE
环境变量。
1. 为什么要用Go Modules?
在没有Go Modules之前,使用的是GOPATH
。在使用GOPATH
时,感觉非常不适的是:如果你的工程有自建包,则一定要将工程放到GOPATH
下,然后import
才能引用到这些包。
helloword
├── code
│ ├── unicode.go
│ └── utf8.go
├── lang
│ ├── ch.go
│ └── es.go
└── main.go
如上图,有一个helloword
工程,有两个自建包:code
和lang
,如果想在main.go文件中引用这两个包,必须要将整个helloworld工程放入GOPATH
路径下,不然会找不到这两个包,尽管它们在一个工程目录下(Go在编译时默认只会从GOPATH和GOROOT下寻找包)。
还有一点就是第三方包版本管理,GOPATH
时期所有的第三方包也都是放在GOPATH
路径下的,这个路径对所有的工程是共享的,假设有A,B两个工程,都引用了一个第三方包packageXXX,这时如果A工程突然要用到packageXXX的新版本,这时它将packageXXX包更新,并自已测试通过上线,A工程没事,但B工程却有很大的风险。
------- -------
| A | | B |
------- -------
| |
| |
|
------------
| packageXXX |
------------
在1.12版本引入Go Modules后,彻底废弃了GOPATH
,通过Go Modules管理的工程不在需要放入GOPATH路径下,而是可以放在任意路径下,并且每个工程引用包都私有化,不再会互相影响,最重要的是操作非常简单。
2. Go Modules使用
还是以上面的helloworld
工程为例来一步步展示Go Modules是如何使用的。
首先建立一个工程目录,目录可以放在任意位置:
mkdir helloworld
实现工程,最终的目录结构是:
helloword
├── code
│ ├── unicode.go
│ └── utf8.go
├── lang
│ ├── ch.go
│ └── es.go
└── main.go
工程实现完了,就可以开始编译工程了,这时开始使用Go Modules包工具,首先初始化工程:
go mod init helloworld
helloworld
模块名,具体的意义我会在后面详细描述。
执行init命令后,会在helloworld工程下生成一个go.mod文件,这个文件记录了模块名和使用的Go的版本号。
module helloworld
go 1.14
执行go mod vendor
将helloworld依赖的第三方工程包全部私有化到本工程的vendor目录下:
go mod vendor
执行完后,会在helloworld目录下生成一个vendor目录,第三方工程包就全部在里面了。同时也会生成一个go.sum文件,这个文件描述了引用的每个第三方包的名称、版本号以及包的md5值。
引用三方包的问题解决了,那Go Modules是如何引用本地包的呢。上面提到过Go编译器会默认从GOPATH
和GOROOT
去寻找包,现在整个工程已经不放在GOPATH路径下了,这时该如何引用本地包?
其实很简单,只需用我们在初始化工程时的模块名再加本地包名即可,举例如下:
helloworld本地包有两个code
和lang
,模块名为helloworld
,这时在main.go文件中代码如下:
import (
"helloworld/code"
"helloworld/lang"
)
这样就可以成功的引用到本地包。
最后整个工程的目录结构为:
helloworld
├── code
├── lang
├── vendor
├── main.go
├── go.sum
└── go.mod
3 directories, 3 files
3. 模块名的使用方式
上面的示例中,工程目录名和模块名是同一个名字,会认人误以为工程目录名就应该是模块名,但其实两者并没有任何关系。下面看下etcd是如何用Go Modules的。
可以直接看下etcd源码下的go.mod文件,如下:
module go.etcd.io/etcd/v3
go 1.14
require (
...
)
它在引用它本地包时,代码为:
import (
"go.etcd.io/etcd/mvcc"
...
)