文章目录

  • 简介
  • 创建新模块
  • 添加依赖项
  • 升级依赖项
  • 使用新主版本的依赖项
  • 将依赖项升级到新的主要版本
  • 删除未使用的依赖项
  • 结论
  • 相关文章

翻译自 Go 官方博文 Using Go Modules。

Tyler Bui-Palsulich and Eno Compton
19 March 2019

简介

这篇文章是系列文章的第一部分。

  • Part 1 — Using Go Modules (this post)
  • Part 2 — Migrating To Go Modules
  • Part 3 — Publishing Go Modules
  • Part 4 — Go Modules: v2 and Beyond

Go 1.11 和 1.12 包括了初步的 support for modules,这是 Go 的新的依赖管理系统,它使依赖版本信息更加明确和易于管理。这篇博客文章介绍了开始使用 modules 所需的基本操作。

一个 module 是 Go packages 的集合,存储在项目根目录下的 go.mod 文件中。go.mod 文件定义了 module 的路径,这也是项目中使用时导入的路径。go.mod 文件还定义了 module 的依赖项,这些是项目成功构建所需的其他模块。每个依赖项都被编写为模块路径和特定的语义版本。

从 Go 1.11 开始,go 命令允许在当前目录或任何父目录有 go.mod 文件时使用 module,条件是目录位于 GOPATH/src 之外。在 GOPATH/src 中,即使找到了 go.mod,为了兼容性起见,go 命令仍然在旧的 GOPATH 模式下运行。有关详细信息,请参阅 Go 命令文档。从 Go 1.13 开始,module 模式将是所有开发的默认模式。

这篇文章介绍了在开发带有模块的 Go 代码时出现的一系列常见操作:

  • 创建新模块
  • 添加依赖项
  • 升级依赖项
  • 在新的主版本上添加一个依赖项
  • 将依赖项升级到新的主版本
  • 删除未使用的依赖项

创建新模块

让我们创建一个新模块。

在 GOPATH/src 之外,创建一个新的空目录,cd 到新创建的目录后,创建一个新的源文件 hello.go:

package hellofunc Hello() string {return "Hello, world."
}

同时写一个测试,hello_test.go:

package helloimport "testing"func TestHello(t *testing.T) {want := "Hello, world."if got := Hello(); got != want {t.Errorf("Hello() = %q, want %q", got, want)}
}

此时,目录包含一个包,但不包含一个模块,因为没有 go.mod 文件。如果我们现在在 /home/gopher/hello 工作,然后运行 go test,我们会看到:

go test
PASS
ok      _/home/gopher/hello 0.020s

最后一行总结了整个包测试。因为我们在 GOPATH 之外以及在任何模块之外工作,所以 go 命令不知道当前目录的导入路径,并根据目录名:_/home/gopher/hello组成一个伪路径。

让我们使用 go mod init 使当前目录成为模块的根目录,然后再次尝试 go test:

go mod init example.com/hello
go: creating new go.mod: module example.com/hello
go test
PASS
ok      example.com/hello   0.020s

祝贺,您已经编写并测试了您的第一个模块。

go mod init 命令生成了 go.mod 文件:

cat go.mod
module example.com/hellogo 1.12

go.mod 文件只出现在模块的根目录中。子目录中的包具有导入路径,由模块路径加上子目录的路径构成。例如,如果我们创建了一个子目录 world,我们就不需要(也不想)在那里运行 go mod init。包将自动识别为 example.com/hello 模块的一部分,并具有导入路 example.com/hello/world。

添加依赖项

Go 模块的主要目的是改进使用其他开发人员编写的代码的体验。

让我们更新 hello.go 导入 rsc.io/quote,并使用它实现 Hello:

package helloimport "rsc.io/quote"func Hello() string {return quote.Hello()
}

现在让我们再做一次测试:

go test
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: finding rsc.io/sampler v1.3.0
go: finding golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
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
PASS
ok      example.com/hello   0.023s

go 命令通过使用 go.mod 中列出的特定依赖模块版本来解析导入,当导入的包不在 go.mod 中时,go 命令自动查找包含该包的最新版本的模块,并将其添加到 go.mod 中。在我们的示例中,go test 将新的导入 rsc.io/quote 解析为模块 rsc.io/quote v1.5.2。它还下载了 rsc.io/quote 使用的两个依赖项,即 rsc.io/sampller 和 golang.org/x/text。仅在 go.mod 文件中记录直接依赖项。

cat go.mod
module example.com/hellogo 1.12require rsc.io/quote v1.5.2

第二个 go test 测试命令不会重复这项工作,因为 go.mod 现在是最新的,下载的模块缓存在本地 GOPATH/pkg/mod 中。

go test
PASS
ok      example.com/hello   0.020s

请注意,虽然 go 命令使添加新的依赖项变得快速和容易,但这并不是没有代价的。您的模块现在实际上依赖于关键领域中的新依赖项,比如正确性、安全性和适当的许可,这里仅举几个例子。有关更多的注意事项,请参阅 Russ Cox 的博文 Our Software Dependency Problem。

正如我们在上面看到的,添加一个直接依赖通常也会带来其他间接依赖。使用命令 go list -m 列出了当前模块及其所有依赖项:

example.com/hello
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0

在 go list 输出中,当前模块(也称为主模块)始终是第一行,然后是按模块路径排序的依赖项。

goang.org/x/text 版本 v0.0.0-20170915032832-14c0d48ead0c 是伪版本的一个示例,它是 go 命令针对某个具体的无标记提交的版本语法。

了 go.mod 之外,go 命令还维护一个名为 go.sum 的文件,该文件包含特定模块版本内容的散列哈希值:

cat go.sum
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZO...
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:Nq...
rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3...
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPX...
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/Q...
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9...

go 命令使用 go.sum 文件确保这些模块的未来下载与第一次下载具有相同的散列值,以确保您的项目所依赖的模块不会因恶意、意外或其他原因而意外更改。go.mod 和 go.sum 都应该提交到版本仓库进行版本控制。

升级依赖项

对于 Go 模块,使用语义版本标记引用版本。语义版本有三个部分:主要部分、次要部分和补丁部分。例如,对于 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.0go 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.0cat go.mod
module example.com/hellogo 1.12require (golang.org/x/text v0.3.0 // indirectrsc.io/quote v1.5.2
)

golang.org/x/text 包已升级到最新的标记版本(v0.3.0)。go.mod 文件也被更新为指定了 v0.3.0。注释 // indirect 表示依赖项不是由该模块直接使用的,而是由其他模块依赖项间接使用的。有关详细信息,请参阅 go help modules 输出的帮助信息。

现在,让我们尝试升级 rsc.io/sampler 次要版本。通过运行 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.99go 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

呃,哦!测试失败表明,最新版本的 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

我们一直在使用 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.1go test
PASS
ok      example.com/hello   0.022s

注意 go get 参数中的显式@v1.3.1。通常,传递给 get 的每个参数都可以采用显式版本;缺省值是@latest,它将解析为前面定义的最新版本。

使用新主版本的依赖项

让我们向包中添加一个新的函数:func Proverb 通过调用 quote.Concurrency 返回一个 Go 并发的说明,这个函数由 rsc.io/print/v3 模块提供。首先更新 hello.go 添加新函数:

package helloimport ("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 模块的每个不同的主版本(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/samplerv1.99.99 应该是向后兼容的,与rsc.io/samplerv1.3.0 兼容,但是客户端对模块行为的假设可能会出现错误。

go 命令要求每个主要版本最多包含一个版本:一个 rsc.io/quote、一个rsc.io/quote/v2、一个rsc.io/quote/v3 等等。这为模块作者提供了一个关于单个模块路径可能重复的明确规则:一个程序不可能同时使用 rsc.io/v1.5.2 和 rsc.io/v1.6.0 构建。同时,允许模块的不同主要版本(因为它们有不同的路径)使模块使用者能够逐步升级到新的主要版本。在本例中,我们希望使用 rsc/quote/v3 v3.1.0 中的 quote.Concurrency,但还没有准备好移除对 rsc.io/quote v1.5.2 的使用。增量迁移的能力在大型程序或代码库中尤为重要。

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

让我们完成从使用 rsc.io/quote 到只使用 rsc.io/print/v3 的转换。由于主要版本的更改,我们应该预期某些 API 可能已被删除、重命名或不兼容。通过阅读文档,我们可以看到 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。我们可以更新 quote.Hello() 的使用转为 quoteV3.HelloV3():

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

现在,不再需要重命名的导入了,所以我们可以撤销:

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

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

go test
PASS
ok      example.com/hello       0.014s

删除未使用的依赖项

我们已经删除了 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.1cat go.mod
module example.com/hellogo 1.12require (golang.org/x/text v0.3.0 // indirectrsc.io/quote v1.5.2rsc.io/quote/v3 v3.0.0rsc.io/sampler v1.3.1 // indirect
)

为什么?因为构建单个包,比如 go build 或 go test,可以很容易地判断出什么时候缺少并且需要添加,而不是什么时候可以安全地删除一些东西。只有在检查模块中的所有包以及这些包的所有可能的构建标记组合之后,才能删除依赖项。普通的 build 命令不加载此信息,因此无法安全地删除依赖项。

可以使用 go mod tidy 命令清理这些未使用的依赖项:

go mod tidygo 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.1cat go.mod
module example.com/hellogo 1.12require (golang.org/x/text v0.3.0 // indirectrsc.io/quote/v3 v3.1.0rsc.io/sampler v1.3.1 // indirect
)go test
PASS
ok      example.com/hello   0.020s

结论

Go mod 是 Go 管理依赖包的方法,从 Go 1.11 起支持该功能。

这篇文章介绍了使用 Go 模块的一些步骤:

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

我们鼓励您在本地开发中开始使用模块,并在项目中添加 go.mod 和 go.sum 文件。为了帮助改进 Go 的依赖管理,请提供反馈和帮助,如发送错误报告或经验报告。

感谢您的反馈和帮助来改进 Go 模块。

相关文章

  • Go Modules: v2 and Beyond
  • Publishing Go Modules
  • Module Mirror and Checksum Database Launched
  • Migrating to Go Modules
  • Go Modules in 2019
  • A Proposal for Package Versioning in Go
  • The cover story
  • The App Engine SDK and workspaces (GOPATH)
  • Organizing Go code

Part 1——使用 Go Modules相关推荐

  1. CSS Modules

    css-loader 提供了一种叫做 CSS Modules 的方案,可以帮我们自动生成唯一的类名,不会和其他模块的命名出现冲突 要使用 CSS Modules 有几个步骤,首先需要在 webpack ...

  2. modules黑名单

    http://www.linuxsir.org/bbs/thread321140.html 今天刚看了udev的资料,说可以用blacklist禁用掉, 写在/etc/modules.d/blackl ...

  3. Go modules基础精进,六大核心概念全解析(下)

    Go 语言做开发时,路径是如何定义的?Go Mudules又为此带来了哪些改变?本文将会全面介绍Go Modules六大核心概念,包括了设计理念与兼容性原则等,掌握这些技术点对于管理和维护Go 模块有 ...

  4. Go modules基础精进,六大核心概念全解析(上)

    Go 语言做开发时,路径是如何定义的?Go Mudules又为此带来了哪些改变?本文将会全面介绍Go Modules六大核心概念,包括了设计理念与兼容性原则等,掌握这些技术点对于管理和维护Go 模块有 ...

  5. SAP PM 入门系列7 - 常用Function Modules

    SAP PM 入门系列7 - 常用Function Modules Function Module Description Object BADI_EQMT_MODIFY You can update ...

  6. SAP MM 预制发票相关的Function Modules

    SAPMM 预制发票相关的Function Modules 创建预制发票: 1)BAPI_INCOMINGINVOICE_PARK 2)BAPI_INCOMINGINVOICE_CREATE1- In ...

  7. Modules in Node

    1.CommonJS规范 1.1 模块引用 const math = require('math') 复制代码 require方法接受模块标识,以此引入一个模块的API到当前上下文中. 1.2 模块定 ...

  8. modules not found(模块未找到)的解决方案

    起因:公司一部门经理换新电脑,然后excel 2007无法打开带有很多宏的excel,提示modules not found. 由于这个问题一直碰到很多次,都是没有真正的解决方案.最后发现公司装的系统 ...

  9. Multiple Spring Data modules found, entering strict repository configuration mode!

    2019独角兽企业重金招聘Python工程师标准>>> 背景 Springboot项目在启动时,控制台输出日志: 2019-05-28 15:28:32.439 INFO main ...

  10. laravel5.2基础多模块开发(pingpong/modules)

    2019独角兽企业重金招聘Python工程师标准>>> 1.下载laravel5.2 http://laravelacademy.org/resources-download 2.解 ...

最新文章

  1. linux 程序包 permission denied,Linux 执行程序 报错误:Permission denied.
  2. Scrum敏捷开发工具Leangoo-卡片多选
  3. 也谈TDD,以及三层架构、设计模式、ORM……没有免费的午餐,选择了,必付出代价...
  4. 河北省电子工程高级职称公示_2019年河北省电子工程职称评审,中级职称已经出结果了!...
  5. 系统服务有多个mysql_windows系统中安装多个Mysql服务
  6. 微机计算机原理姚向华课后答案,微型计算机操作系统
  7. CentOS7:JDK1.7.0_80安装
  8. django:访问本地静态文件的配置
  9. python怎么读发音百度翻译-python 百度翻译破解版,亲证可行
  10. Python基于共现提取《釜山行》人物关系
  11. 最近写mapreduce程序从hbase中抽取程序遇到的一些问题
  12. 飘刃 v0.0.10 首次发布,超快执行速度的 Vue 项目构建工具
  13. ThreadLocal
  14. Matlab-有限单元法-2D梁单元的刚度矩阵组装(曾攀)
  15. 2.12 Excel软件各工作区的显示和隐藏 [原创Excel教程]
  16. VMware Workstation 14中文破解版下载(附密钥)(笔记)
  17. 2020智能营销领域最具商业合作价值企业盘点
  18. [渝粤教育] 西南科技大学 信息组织与检索 在线考试复习资料2021版
  19. C#,JAVA人民币小写转大写
  20. matlab读取RGB888或RGB565像素文件并绘图

热门文章

  1. Checkpoint 发布恶意软件规避分析的技术百科
  2. 如何解决Greenplum中无法通过标准命令修复的元数据错误
  3. Html5 Egret游戏开发 成语大挑战(五)界面切换和数据处理
  4. SDNLAB技术分享(二):从Toaster示例初探ODL MD-SAL架构
  5. 解决python ConfigParser文件编码问题(按指定格式存储文件(txt))
  6. L1-061 新胖子公式 (10 分)-PAT 团体程序设计天梯赛 GPLT
  7. L1-057 PTA使我精神焕发 (5 分)-PAT 团体程序设计天梯赛 GPLT
  8. 【iOS】Touch Drag Inside 和 Touch Drag Outside、Touch Drag Enter、Touch Drag Exit的区别
  9. L2-026 小字辈-PAT团体程序设计天梯赛GPLT
  10. JAVA加勒比_【JAVA】synchronized关键字