modules
是Go 1.11
版本提出的一个依赖包管理系统。
之前在Go1.1.1新功能module的介绍及使用文中中介绍了module
功能,以及如何使用别人已经制作好的module
。这一节我们来看看如何自己创建module
来让别人使用。
首先创建一个包,命名为testmod
。但是需要注意:testmod
目录必须在$GOPATH
目录之外。默认情况下,$GOPATH
目录下module
功能是无法使用的。
然后进入testmod
目录,创建一个简单的testmod.go
文件。
package testmod
import "fmt"
// SayHello returns a friendly greeting
func SayHello(name string) string {
return fmt.Sprintf("Hello, %s", name)
}
目前testmod
包不能称为一个module
,现在我们来改变它。在命令行执行下面的命令(前提是你有github
仓库或者可以存放你代码的仓库),下面go命令中的github.com/benben2015/testmod
是我为testmod
创建的仓库地址。
go mod init github.com/benben2015/testmod
# go: creating new go.mod: module github.com/benben2015/testmod
上面将会在testmod
目录下创建一个go.mod
的文件,内容如下:
module github.com/benben2015/testmod
这样我们就把testmod
包变成了module
,现在我们将代码push
到远程仓库,这样其他人就可以使用go get
命令来使用它了。
git push -u origin master
Go modules
的版本控制遵循语义化版本,划重点 尤其是Go
会使用仓库的标签来查找版本。例如:版本2应该和版本1,版本0有很大不同。默认情况下,Go
会fetch
仓库中最新的带有标签的可用版本。
使用git tag
命令来发布我们的1.0.0
版本。
git tag v1.0.0
git push --tags
上面的命令将会在仓库中的当前代码上创建tag
,作为我们的版本1.0.0
。虽然Go
没有强制创建个新分支来发布主版本的一些修复版本,但是建议这样。
git checkout -b v1
git push -u origin v1
现在我们可以使用我们之前创建的module
,新建一个工程,然后创建main.go
文件。
package main
import (
"fmt"
"github.com/benben2015/testmod"
)
func main() {
fmt.Println(testmod.SayHello("China"))
}
在没有module
功能之前,我们可能使用上面的包时,需要通过go get github.com/benben2015/testmod
命令先下载包。但是使用modules
,就没那么麻烦。
首先我们需要在程序中使modules
功能可用,然后使用命令go mod init mod
。执行完毕后,将会创建一个新的文件go.mod
,内容为:
module mod
现在执行构建命令,你将会发现Go命令会自动的拉取程序中导入的包。
go build
go: finding github.com/benben2015/testmod v1.0.0
go: downloading github.com/benben2015/testmod v1.0.0
这时我们再检查go.mod
文件,你将会看到变化:
module mod
require github.com/benben2015/testmod v1.0.0
同时,你也可以看到一个新的文件go.sum
,里面包含依赖包的hash
值来确保正确的版本和文件。
github.com/benben2015/testmod v1.0.0 h1:kiEBYKHX5VR04NwYHo4QTZfd5F6nkeCpvDi5tr5UuBo=
github.com/benben2015/testmod v1.0.0/go.mod h1:nJ45ppreUJfCWss5GtOwRC5FVfGv0p0ii1rrh7Sqdi0=
如果版本有小的改动,一般主版本号不用变,只是次版本改变。例如我们之前创建的testmod
模块变成这样
func SayHello(name string) string {
return fmt.Sprintf("Hi, %s!", name)
}
然后重新建立标签,并push
到远程仓库。
git tag v1.0.1
git push --tags origin master
默认情况下,Go不会主动的去更新modules
。所以需要我们告诉Go去更新,使用命令go get
。
go get -u
,将会使用依赖包的最新版本。例如更新1.0.0到1.0.1或者1.1.0。go get -u=patch
将会更新依赖包的版本到最新的修订版本。例如更新1.0.0到1.0.1。go get package@version
将会更新依赖包的版本到特定版本。因此,当执行go get -u
后,我们的go.mod
文件将会变成
module mod
require github.com/benben2015/testmod v1.0.1
当版本有大的更新,主版本号要变。Go modules
认为主版本号不同的modules
是完全不同的包。例如我们将testmod
包中的文件做如下变化:
package testmod
import (
"errors"
"fmt"
)
// SayHello returns a friendly greeting in language lang
func SayHello(name, lang string) (string, error) {
switch lang {
case "en":
return fmt.Sprintf("Hi, %s!", name), nil
case "pt":
return fmt.Sprintf("Oi, %s!", name), nil
case "es":
return fmt.Sprintf("¡Hola, %s!", name), nil
case "fr":
return fmt.Sprintf("Bonjour, %s!", name), nil
default:
return "", errors.New("unknown language")
}
}
版本2更改导入路径,通过在go.mod
文件中追加新的版本路径module github.com/benben2015/testmod/v2
。然后和之前一样,添加标签并push
。
git tag v2.0.0
git push --tags origin v2
前面已经提到过,使用go get -u
不会将版本v1.0.1
更新到2.0.0
。那么应该怎么做呢。首先将程序修改成这样:
package main
import (
"fmt"
"github.com/benben2015/testmod/v2"
)
func main() {
g, err := testmod.SayHello("China", "pt")
if err != nil {
panic(err)
}
fmt.Println(g)
}
然后使用go build
命令进行构建,Go将会拉取2.0.0
的版本。注意: 尽管我们的导入路径后面有v2,但是Go在使用模块时还是会使用testmod
。
正如之前提到的,主版本号不同,Go认为是不同的包,所以你可以在相同的文件里引用不兼容的版本。
package main
import (
"fmt"
"github.com/benben2015/testmod"
testmodML "github.com/benben2015/testmod/v2"
)
func main() {
fmt.Println(testmod.SayHello("China"))
g, err := testmodML.SayHello("China", "pt")
if err != nil {
panic(err)
}
fmt.Println(g)
}