目录

Go-mod的使用

概述

本文主要是根据 Modules 的翻译和实践操作。

Go Modules 是从 Go 1.11 开始,就引入了依赖管理工具,并在最近发布的 Go 1.14 开始稳定了,可以用于生产环境。Go 官方鼓励大家迁移到 Go Mod 来。

Quick Start

1
2
3
4
mkdir -p /tmp/scratchpad/repo
cd /tmp/scratchpad/repo
git init -q
git remote add origin https://github.com/my/repo

操作如下。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
➜  repo ls
➜  repo git init -q
➜  repo git:(master) git remote add origin https://github.com/my/repo
➜  repo git:(master) go mod init github.com/my/repo
# 这里报告初始化的结果,创建了一个额 go.mod 文件,并且这个是一个 module
go: creating new go.mod: module github.com/my/repo
➜  repo git:(master) ✗ tree
.
└── go.mod

0 directories, 1 file
➜  repo git:(master) ✗ cat <<EOF > hello.go
heredoc> package main
heredoc>
heredoc> import (
heredoc>     "fmt"
heredoc>     "rsc.io/quote"
heredoc> )
heredoc>
heredoc> func main() {
heredoc>     fmt.Println(quote.Hello())
heredoc> }
heredoc> EOF
➜  repo git:(master) ✗ ls
go.mod  hello.go
➜  repo git:(master) ✗ go build -o hello.go
go: finding rsc.io/quote v1.5.2
go: downloading rsc.io/quote v1.5.2
go: extracting rsc.io/quote v1.5.2
go: downloading rsc.io/sampler v1.3.0
go: extracting rsc.io/sampler v1.3.0
go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: extracting golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
➜  repo git:(master) ✗
➜  repo git:(master) ✗ ls
go.mod  go.sum  hello.go
➜  repo git:(master) ✗ cat go.mod
module github.com/my/repo

go 1.12

require rsc.io/quote v1.5.2
➜  repo git:(master) ✗ go list -m all
github.com/my/repo
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0
➜  repo git:(master) ✗ go list -u -m all
github.com/my/repo
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c [v0.3.2]
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0 [v1.99.99]
➜  repo git:(master) ✗ go get  -u ./...
go: finding golang.org/x/tools latest
go: finding golang.org/x/xerrors latest
go: finding golang.org/x/sys latest
go: finding golang.org/x/sync latest
go: finding golang.org/x/crypto latest
go: finding golang.org/x/net latest
package github.com/my/repo: read /tmp/scratchpad/repo/hello.go: unexpected NUL in input
➜  repo git:(master) ✗ go build
can't load package: package github.com/my/repo: read /tmp/scratchpad/repo/hello.go: unexpected NUL in input
➜  repo git:(master) ✗ vim hello.go
➜  repo git:(master) ✗ ls
go.mod  go.sum  hello.go
➜  repo git:(master) ✗ rm hello.go
➜  repo git:(master) ✗ cat <<EOF > hello.go
package main

import (
    "fmt"
    "rsc.io/quote"
)

func main() {
    fmt.Println(quote.Hello())
}
EOF
➜  repo git:(master) ✗ go build -o hello
go: downloading rsc.io/sampler v1.99.99
go: extracting rsc.io/sampler v1.99.99
➜  repo git:(master) ✗ ls
go.mod  go.sum  hello  hello.go
➜  repo git:(master) ✗ ./hello
99 bottles of beer on the wall, 99 bottles of beer, ...
➜  repo git:(master) ✗ ./hello
99 bottles of beer on the wall, 99 bottles of beer, ...
➜  repo git:(master) ✗ vi hello.go
➜  repo git:(master) ✗
➜  repo git:(master) ✗
➜  repo git:(master) ✗ ls
go.mod  go.sum  hello  hello.go
➜  repo git:(master) ✗ go go.mod go
➜  repo git:(master) ✗ go mod tidy
➜  repo git:(master) ✗ ls
go.mod  go.sum  hello  hello.go
➜  repo git:(master) ✗ cat go.mod
module github.com/my/repo

go 1.12

require (
	golang.org/x/text v0.3.2 // indirect
	rsc.io/quote v1.5.2
	rsc.io/sampler v1.99.99 // indirect
)
➜  repo git:(master) ✗ cat go.sum
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3Y=
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
rsc.io/sampler v1.99.99 h1:7i08f/p5TBU5joCPW3GjWG1ZFCmr28ybGqlXtelhEK8=
rsc.io/sampler v1.99.99/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
➜  repo git:(master) ✗ go mod vendor
➜  repo git:(master) ✗ ls
go.mod  go.sum  hello  hello.go  vendor
➜  repo git:(master) ✗ cd vendor
➜  vendor git:(master) ✗ ls
golang.org  modules.txt  rsc.io
➜  vendor git:(master) ✗ tree

查看 go.mod 文件,可以发下增加了几行,主要是针对依赖的,通过 require 来指定了依赖和精确的版本。

1
2
3
4
5
$ cat go.mod

module github.com/my/repo

require rsc.io/quote v1.5.2

所以,整个过程,是不是没见到烦人的 go get?是的,所以用了 go mod 之后,你的日常工作可能会变成下面这样。

  1. 给你的 .go 源码引入依赖
  2. 在日常测试和开发中难免会需要 go buildgo test,通过这些日常操作,go mod 会自动去定位和下载满足条件的依赖
  3. 如果你需要非常精确的依赖版本,可以通过 go get foo@v1.23 或者 go get foo@master 又或者指定 commit id 比如 go get foo@44jbasf,甚至直接去改 go.mod 文件都可以
1
2
3
4
5
6
7
go list -m all — 可以看到所有会用于构建的直接和间接依赖的版本
go list -u -m all — 可以看到所有会用于构建的直接和间接依赖的版本,并且升级那些需要升级的依赖
go get -u ./... or go get -u=patch ./... (from module root directory) — 同上但是会忽略依赖的 pre-releases
go build ./... or go test ./... (from module root directory) — 构建和测试
go mod tidy — 检查 go.mod 去删除不需要的依赖,and add any dependencies needed for other combinations of OS, architecture, and build tags (details)
replace directive or gohack — 如果直接引用依赖有问题,那么可以使用本地的拷贝版本,或者自己的 fork
go mod vendor — 可以构建 vendor 目录

Modules

module 是一系列的 Go 包组成的单位结构。记录了精确的依赖。一个 repo 一般就是一个 module,但是也会有一个 repo 里包含多个 module 的情况(找出来例子)。

现在用户需要区分几个概念,repo, modules 和 packages。

Migration Topics

Go 有非常多的包管理工具,不过 go mod init 可以做到几乎所有原有的包管理工具的自动迁移。比如说 dep, glide, govendor, godep 等等。

module declares its path as

这需要用到 go modreplace 来调整,具体就是往 go.mod 插入一条记录。

1
replace github.com/coreos/bbolt v1.3.4 => go.etcd.io/bbolt v1.3.4

used for two different module paths

很神奇,replace 两次就好了,具体原因待排查。

1
go mod edit -replace=go.etcd.io/bbolt@v1.3.4=github.com/coreos/bbolt@v1.3.4

其实就会在 go.mod 文件插入两行。

1
2
replace github.com/coreos/bbolt v1.3.4 => go.etcd.io/bbolt v1.3.4
replace go.etcd.io/bbolt v1.3.4 => github.com/coreos/bbolt v1.3.4

410 Gone

这个通常是因为 Go 的版本不同导致的,如果你正在使用 Go 1.13 以上的版本,通过 GOSUMDB 的环境变量调整就可以了。

1
2
export GOSUMDB=off
go mod download

参考资料

  1. Modules
警告
本文最后更新于 2022年10月19日,文中内容可能已过时,请谨慎参考。