Go Modules 介绍与基本操作(下)

简介: Go Modules 介绍与基本操作(下)


04

升级依赖项


使用 Go modules,版本使用语义版本标记进行引用。语义版本由三个部分组成:主要版本、次要版本和修补程序版本。例如,对于 v0.1.2,主要版本为 0,次要版本为 1,修补程序版本为 2。让我们演练几个次要版本升级。


从 go list -m all

的输出中,我们可以看到我们使用的是未标记的 golang.org/x/text。让我们升级到最新的标记版本,并测试一切仍然有效:


$ go get golang.org/x/text
go: finding golang.org/x/text v0.3.0
go: downloading golang.org/x/text v0.3.0
go: extracting golang.org/x/text v0.3.0
$ go test
PASS
ok    example.com/hello  0.013s
$


运行成功,让我们再看看 go list -m all 和 go.mod 文件:


$ go list -m all
example.com/hello
golang.org/x/text v0.3.0
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0
$ cat go.mod
module example.com/hello
go 1.12
require (
    golang.org/x/text v0.3.0 // indirect
    rsc.io/quote v1.5.2
)
$


golang.org/x/text 已升级到最新的标记版本 (v0.3.0)。go.mod 文件已更新,指定 v0.3.0。注释「indirect」指示依赖项不直接由此模块使用,仅由其他模块依赖项间接使用。


现在,让我们尝试升级 rsc.io/sampler 版本。通过运行 go get 和运行 go test:


$ go get rsc.io/sampler
go: finding rsc.io/sampler v1.99.99
go: downloading rsc.io/sampler v1.99.99
go: extracting rsc.io/sampler v1.99.99
$ go test
--- FAIL: TestHello (0.00s)
    hello_test.go:8: Hello() = "99 bottles of beer on the wall, 99 bottles of beer, ...", want "Hello, world."
FAIL
exit status 1
FAIL  example.com/hello  0.014s
$


go test 运行失败表明最新版本的 rsc.io/sampler 与我们的用法不兼容。让我们列出该模块的可用标记版本:


$ go list -m -versions rsc.io/sampler
rsc.io/sampler v1.0.0 v1.2.0 v1.2.1 v1.3.0 v1.3.1 v1.99.99
$$ go list -m -versions rsc.io/samplerrsc.io/sampler v1.0.0 v1.2.0 v1.2.1 v1.3.0 v1.3.1 v1.99.99$


我们一直在使用v1.3.0,v1.99.99 明显不兼容。也许我们可以尝试使用 v1.3.1 代替:


$ go get rsc.io/sampler@v1.3.1
go: finding rsc.io/sampler v1.3.1
go: downloading rsc.io/sampler v1.3.1
go: extracting rsc.io/sampler v1.3.1
$ go test
PASS
ok    example.com/hello  0.022s
$


请注意在 go get 中的显式内容  @v1.3.1 指定 Module 版本。通常,传递给 go get 的每个参数都可以获取显式版本,默认值为@latest,解析为前面定义的最新版本。


05

添加对新的主版本的依赖


让我们在我们的包中添加一个新函数:func Proverb 返回 Go 并发原语,通过调用 quote.Concurrency,

由模块 rsc.io/quote/v3 提供。首先我们更新 hello.go 添加新函数:


package hello
import (
    "rsc.io/quote"
    quoteV3 "rsc.io/quote/v3"
)
func Hello() string {
    return quote.Hello()
}
func Proverb() string {
    return quoteV3.Concurrency()
}


然后,我们添加一个 hello_test.go:


func TestProverb(t *testing.T) {
    want := "Concurrency is not parallelism."
    if got := Proverb(); got != want {
        t.Errorf("Proverb() = %q, want %q", got, want)
    }
}


然后,我们可以测试我们的代码:


$ go test
go: finding rsc.io/quote/v3 v3.1.0
go: downloading rsc.io/quote/v3 v3.1.0
go: extracting rsc.io/quote/v3 v3.1.0
PASS
ok    example.com/hello  0.024s
$


请注意,我们的模块现在依赖于 rsc.io/quote 和 rsc.io/quote/v3:


$ go list -m rsc.io/q...
rsc.io/quote v1.5.2
rsc.io/quote/v3 v3.1.0
$


Go modules 的每个不同主要版本(v1、v2 等)使用不同的模块路径:从 v2 开始,路径必须以主要版本结束。


在示例中,

rsc.io/quote的 v3 版本不再 rsc.io/quote:而是由模块路径 rsc.io/quote/v3。


此约定称为语义导入版本控制,它为不兼容的包(具有不同主要版本的包)提供不同的名称。


相比之下,

rsc.io/quote 的 v1.6.0 应与 v1.5.2 向后兼容,因此它重用 rsc.io/quote。(在上一节中,rsc.io/sampler v1.99.99 应与 rsc.io/sampler v1.3.0 向后兼容,但模块行为的错误或不正确的客户端假设都可能发生。


go 命令允许生成最多包含任何特定模块路径的一个版本,这意味着最多包含每个主要版本的一个版本:一个 rsc.io/quote、一个 rsc.io/quote/v2、rsc.io/quote/v3,等等。这为模块作者提供了关于单个模块路径可能重复的清晰规则:程序不可能同时使用 rsc.io/quote v1.5.2 和 rsc.io/quote v1.6.0 构建。同时,允许模块的不同主要版本(因为它们具有不同的路径)使模块使用者能够逐步升级到新的主要版本。


在此示例中,我们希望使用 rsc/quote/v3 v3.1.0 的 quote.Concurrency,但尚未准备好迁移我们使用 rsc.io/quote v1.5.2。我们暂且通过起别名的方式使用。在大型程序或代码库中,增量迁移的能力尤为重要。


06

将依赖项升级到新的主版本


让我们完成从同时使用 rsc.io/quote 和 rsc.io/quote/v3

到仅使用 rsc.io/quote/v3 的依赖项升级。由于主要版本更改,我们预期某些 API 可能已被删除、重命名或以其他方式以不兼容的方式更改。


运行 go doc rsc.io/quote/v3 命令,

阅读文档, 我们可以看到, Hello() 已成为 Hellov3():


$ go doc rsc.io/quote/v3
package quote // import "rsc.io/quote"
Package quote collects pithy sayings.
func Concurrency() string
func GlassV3() string
func GoV3() string
func HelloV3() string
func OptV3() string
$


(输出中还有一个已知错误,显示的导入路径错误地丢弃了 /v3。)


我们可以更新在 hello.go 中使用的 quote.Hello(),改为使用 quoteV3.HelloV3():


package hello
import quoteV3 "rsc.io/quote/v3"
func Hello() string {
    return quoteV3.HelloV3()
}
func Proverb() string {
    return quoteV3.Concurrency()
}


现在仅使用依赖项 rsc.io/quote 的一个版本,不再需要给导入的依赖项定义别名 quoteV3,因此我们可以修改为:


package hello
import "rsc.io/quote/v3"
func Hello() string {
    return quote.HelloV3()
}
func Proverb() string {
    return quote.Concurrency()
}


让我们重新运行 go test,以确保一切正常运行:


$ go test
PASS
ok      example.com/hello       0.014s


07

删除未使用的依赖项


我们已经删除了我们使用的依赖项 rsc.io/quote, 但它仍然出现在 go list -m  all 和我们的 go.mod 文件中:


$ go list -m all
example.com/hello
golang.org/x/text v0.3.0
rsc.io/quote v1.5.2
rsc.io/quote/v3 v3.1.0
rsc.io/sampler v1.3.1
$ cat go.mod
module example.com/hello
go 1.12
require (
    golang.org/x/text v0.3.0 // indirect
    rsc.io/quote v1.5.2
    rsc.io/quote/v3 v3.0.0
    rsc.io/sampler v1.3.1 // indirect
)
$


为什么?因为构建单个包(如 go build 或 go test)可以轻松地判断何时缺少依赖项并需要添加,但何时可以安全地删除依赖项,只有在检查了模块中的所有包以及这些包的所有可能的生成标记组合后,才能删除依赖项。普通构建命令不加载此信息,因此无法安全地删除依赖项。


但是,我们可以使用 go mod tidy 命令清理这些未使用的依赖项:


$ go mod tidy
$ go list -m all
example.com/hello
golang.org/x/text v0.3.0
rsc.io/quote/v3 v3.1.0
rsc.io/sampler v1.3.1
$ cat go.mod
module example.com/hello
go 1.12
require (
    golang.org/x/text v0.3.0 // indirect
    rsc.io/quote/v3 v3.1.0
    rsc.io/sampler v1.3.1 // indirect
)
$ go test
PASS
ok    example.com/hello  0.020s
$


08

总结


Go modules 是 Go 未来版本中使用的依赖项管理系统。模块功能现在在所有支持的 Go 版本中可用(即从 Go 1.11 起的版本)。


本文介绍了 Go modules 这些基本操作:

  • go mod init 创建一个新模块, 并初始化描述它的 go.mod 文件。
  • go build、go test 和其他包构建命令根据 go.mod 文件需要添加新的依赖项。
  • go list -m all 打印当前模块的所有依赖项列表。
  • go get 更新依赖项所需的版本(或添加新的依赖项)。
  • go mod tidy 删除未使用的依赖项。


建议大家开始在本地开发中使用 module,并将 go.mod 和 go.sum 文件添加到项目中的版本控制。





目录
相关文章
|
存储 缓存 Linux
Go Modules 介绍与基本操作(上)
Go Modules 介绍与基本操作
97 0
|
6月前
|
NoSQL Go Redis
如何使用 Go 和 `go-redis/redis` 库连接到 Redis 并执行一些基本操作
如何使用 Go 和 `go-redis/redis` 库连接到 Redis 并执行一些基本操作
97 1
|
6月前
|
存储 缓存 JSON
Go Modules:Go语言依赖管理的新篇章
Go Modules是Go 1.11引入的依赖管理标准,解决`GOPATH`的依赖冲突问题。
111 1
|
7月前
|
存储 Go
Golang深入浅出之-Go语言依赖管理:GOPATH与Go Modules
【4月更文挑战第27天】Go语言依赖管理从`GOPATH`进化到Go Modules。`GOPATH`时代,项目结构混乱,可通过设置多个工作空间管理。Go Modules自Go 1.11起提供更现代的管理方式,通过`go.mod`文件控制依赖。常见问题包括忘记更新`go.mod`、处理本地依赖和模块私有化,可使用`go mod tidy`、`replace`语句和`go mod vendor`解决。理解并掌握Go Modules对现代Go开发至关重要。
140 2
|
存储 Go API
怎么发布 Go Modules v1 版本?
怎么发布 Go Modules v1 版本?
64 0
|
缓存 JSON Go
Go 语言各个版本支持 Go Modules 的演进史
Go 语言各个版本支持 Go Modules 的演进史
102 1
|
存储 Go API
Go Modules 如何创建和发布 v2 及更高版本?
Go Modules 如何创建和发布 v2 及更高版本?
149 0
|
Go
Go中对通道的基本操作
Go中Channel的基本操作
78 0
|
算法 Go
Go实现栈与队列基本操作
一 前言 二 实现栈与队列基本操作 2.1 栈基本操作 2.2 队列基本操作 三 用栈实现队列 3.1 理论 3.2 算法题 3.3 思路 3.4 代码部分 四 用队列实现栈 4.1 理论 4.2 算法题 4.3 思路 4.4 使用两个队列实现 4.5 优化 4.6 使用一个队列实现
105 0
|
存储 算法 Go
一文读懂Go Modules原理
一文读懂Go Modules原理