大部分内容截取官方文档,官方文档才是最权威的!
go modules 发布已经有一段时间了,之前 gopher china 也推荐用这种方式来开发,准备弃用 glide,虽然也很好用-,-/。为了用 modules,已经把 go 升级到 1.12,这里推荐一个工具 gvm,golang 多版本管理工具,类似 node 的 nvm,挺好用的。为什么要用 modules 呢?最大的好处当然是摆脱了 GOPATH 这个比较反人类的设计,其次官方支持。
基本使用
好了。先来快速过一下 modules 的基本使用,详细信息将在后面部分介绍,先从头开始创建模块的简单示例。
示例
在 GOPATH 路径之外,创建一个目录,
$ mkdir -p /tmp/scratchpad/hello
$ cd /tmp/scratchpad/hello
初始化一个新的 module
$ go mod init github.com/you/hello
go: creating new go.mod: module github.com/you/hello
加上代码逻辑。
$ cat <<EOF > hello.go
package main
import (
"fmt"
"rsc.io/quote"
)
func main() {
fmt.Println(quote.Hello())
}
EOF
编译并且运行。
$ go build
$ ./hello
Hello, world.
go.mod 文件已更新项目所需的明确版本号的依赖项,其中 v1.5.2 是semver标记:
$ cat go.mod
module github.com/you/hello
require rsc.io/quote v1.5.2
日常工作流程
细心的读者可能发现上面的示例中并没有用到go get
。
我们典型的日常工作流程是:
- 根据需要在
.go
代码中添加 import 语句。 - 标准命令(如
go build
或go test
)将根据需要自动添加新的依赖项以满足导入(更新go.mod
并下载新的依赖项)。 - 在需要指定版本时,可以使用
go get foo@v1.2.3
,go get foo@master
,go get foo@e3702bed2
或者直接编辑go.mod
等命令选择指定的依赖项版本。
简要介绍我们可能使用的其他常用功能:
-
go list -m all
- 查看项目构建中所有的直接和间接依赖项的最终版本 -
go list -u -m all
- 查看项目构建中所有的直接和间接依赖项的次要版本升级和补丁 -
go get -u or go get -u=patch
- 更新所有直接和间接依赖项到最新的次要版本或补丁升级(预发布被忽略) -
go build ./... or go test ./...
- 从模块根目录运行时,用来编译或测试模块中所有的包 -
go mod tidy
- 从go.mod
中删除不再需要的依赖项,并自动添加操作系统,架构和构建标记的一些组合所需要的依赖项 -
go mod vendor
- 一个创建 vendor 目录的可选命令
新的概念
Modules 模块
module 是项目相关 Go 包的一个集合,它们作为一个独立单元将项目所需的依赖包版本化。
module 记录精确的依赖项并创建可重复的编译。
总结仓库,模块和包之间的关系:
- 仓库包含一个或多个 Go 模块。
- 每个模块包含一个或多个 Go 包
- 每个包由一个目录中的一个或多个 Go 源文件组成
module 必须根据semver
进行语义化版本,通常采用v(major).(minor).(patch)
的形式,例如 v0.1.0,v1.2.3 或 v1.5.0-rc.1。
go.mod
模块由 Go 源文件树定义,并在源文件树的根目录中包含 go.mod 文件。模块源代码可能位于 GOPATH 之外。go.mod 文件有四个指令:module
,require
,replace
,exclude
。
这是一个定义模块github.com/my/thing
的示例go.mod
文件:
module github.com/my/thing
require (
github.com/some/dependency v1.2.3
github.com/another/dependency/v4 v4.0.0
)
模块通过module
指令在 go.mod 中声明模块标识,该指令声明模块的路径。模块中所有依赖包的导入路径共享模块路径作为公共前缀。模块路径和 go.mod 所在目录到具体包的目录的相对路径共同决定了包的导入路径。
例如,如果要为仓库github.com/my/repo
创建一个模块,该模块包含两个包github.com/my/repo/foo
和github.com/my/repo/bar
,那么 go.mod 文件中的第一行通常会将模块路径声明为module github.com/my/repo
,相应的代码结构:
repo/
├── go.mod
├── bar
│ └── bar.go
└── foo
└── foo.go
在 Go 源代码中,使用包括模块路径在内的绝对路径导入包。例如,如果模块在其 go.mod 中声明为module example.com/my/module
,则可以执行以下操作:
import "example.com/my/module/mypkg"
这将从模块example.com/my/module
中导入包mypkg
。
如何使用 Module
如何安装和激活模块
使用模块的两个安装选项是:
- Go 版本 1.11+
- master 分支源码安装toolchain
安装完成后,可以通过以下两种方式之一激活模块:
- 在$GOPATH/src 之外的目录中调用 go 命令,在当前目录或其任何父目录中使用有效的 go.mod 文件,并且环境变量
GO111MODULE
未设置(或显式设置为auto
) - 设置环境变量
GO111MODULE=on
如何定义模块
为现有项目创建 go.mod:
- 切换到 GOPATH 之外的目录:
$ cd <project path outside $GOPATH/src> # e.g., cd ~/projects/hello
需要注意的是,在 GOPATH 目录之外无需设置GO111MODULE
环境变量来激活模块。
或者,如果你想在 GOPATH 中运行:
$ export GO111MODULE=on # manually active module mode
$ cd $GOPATH/src/<project path> # e.g., cd $GOPATH/src/you/hello
- 初始化模块:
$ go mod init
此步骤从现有的 dep Gopkg.lock 文件或其他九种支持的依赖文件(类似glide.lock
详细戳),为 go.mod 添加 require 语句以匹配现有配置。
go mod init
通常能够使用辅助数据(例如 VCS 元数据)来自动确定相应的模块路径,但是如果go mod init
表明它不能自动确定模块路径,则你需要以其他方式覆盖 path,你可以提供模块路径作为go mod init
的可选参数,例如:
$ go mod init github.com/my/repo
- 构建模块。在模块的根目录执行,
./...
模式匹配当前模块中的所有包。 go build 将根据需要自动添加没有的依赖项或未转换的依赖项
$ go build ./...
- 按配置测试模块,以确保它适用于所选版本:
$ go test ./...
- (可选)运行模块的测试以及所有直接和间接依赖项的测试以检查兼容性:
$ go test all
有疑问加站长微信联系(非本文作者)