何为 Go Modules?

go modules 是 Golang 1.11 新加的特性,现在1.14 已经发布了,已经可以考虑投入生产使用了。

Go wiki

In Go 1.14, module support is considered ready for production use, and all users are encouraged to migrate to modules from other dependency management systems.

官方对 Go Modules 的介绍:

A module is a collection of Go packages stored in a file tree with a go.mod file at its root. The go.mod file defines the module’s module path, which is also the import path used for the root directory, and its dependency requirements, which are the other modules needed for a successful build. Each dependency requirement is written as a module path and a specific semantic version.

简而言之,Go Modules 是为了替换旧的基于 GOPATH 的方法来指定项目依赖的一个方案。

官方博客有一个介绍 Go Modules 的系列文章:

Go 1.11 开始,当当前目录或任何父目录具有 go.mod 文件时,只要该目录位于 $GOPATH/src 之外,go 命令就可以使用 Modules。(在 $GOPATH/src 内部,出于兼容性考虑,即使找到了 go.mod,go 命令仍以旧的 GOPATH 模式运行。有关详细信息,请参见 go 命令文档。)从 Go 1.13 开始,模块模式为默认的依赖管理模式。

Go mod

Golang 提供了 go mod 命令来管理包

Go mod 常用命令:

命令说明
downloaddownload modules to local cache(下载依赖包)
editedit go.mod from tools or scripts(编辑go.mod)
graphprint module requirement graph(打印模块依赖图)
initinitialize new module in current directory(在当前目录初始化mod)
tidyadd missing and remove unused modules(拉取缺少的模块,移除不用的模块)
vendormake vendored copy of dependencies(将依赖复制到vendor下)
verifyverify dependencies have expected content (验证依赖是否正确)
whyexplain why packages or modules are needed (解释为什么需要依赖)

项目中使用 Go mod 的常见操作

创建新的模块

GOPATH 目录之外 新建一个目录,并使用 go mod init 初始化生成 go.mod 文件

1
2
3
4
5
6
7
8
mkdir hello
cd hello
go mod init hello
go: creating new go.mod: module hello
ls
go.mod
cat go.mod
module hello

go.mod文件一旦创建后,它的内容将会被 Go toolchain 全面掌控。Go toolchain 会在各类命令执行时,比如 go get、go build、go mod 等修改和维护 go.mod 文件。

go.mod 提供了 module, requirereplaceexclude 四个命令

  • module 语句指定包的名字(路径)
  • require 语句指定的依赖项模块
  • replace 语句可以替换依赖项模块
  • exclude 语句可以忽略依赖项模块

添加依赖

新建一个 server.go 文件,写入以下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import (
    "net/http"
    
    "github.com/labstack/echo"
)

func main() {
    e := echo.New()
    e.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "Hello, World!")
    })
    e.Logger.Fatal(e.Start(":1323"))
}

执行 go run server.go 运行代码会发现 go mod 会自动查找依赖自动下载。

现在查看 go.mod 内容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
module hello

go 1.12

require (
    github.com/labstack/echo v3.3.10+incompatible // indirect
    github.com/labstack/gommon v0.2.8 // indirect
    github.com/mattn/go-colorable v0.1.1 // indirect
    github.com/mattn/go-isatty v0.0.7 // indirect
    github.com/valyala/fasttemplate v1.0.0 // indirect
    golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect
)

go 也会自动生成一个 go.sum 文件来记录 dependency tree。

再次执行脚本 go run server.go 发现跳过了检查并安装依赖的步骤。

可以使用命令 go list -m -u all 来检查可以升级的package,使用 go get -u need-upgrade-package 升级后会将新的依赖版本更新到go.mod

也可以使用 go get -u 升级所有依赖

关于最新版本的定义,官方的博客中是这样解释的:

Latest” is defined as the latest tagged stable (non-prerelease) version, or else the latest tagged prerelease version, or else the latest untagged version.

使用 replace 替换无法直接获取的 package

由于一些不可抗拒的原因,某些 package 可能不能下载成功,如: golang.org

modules 可以通过在 go.mod 文件中使用 replace 指令替换成github上对应的库,比如:

1
2
3
replace (
    golang.org/x/package version => github.com/golang/package version
)

或者:

1
replace golang.org/x/package version => github.com/golang/package version