文章目录

  • 简介
  • 将项目迁移至 Go modules
  • 使用依赖管理器
  • 没有依赖管理器
  • 模块模式下的测试
  • 发布发行版
  • 导入和规范模块路径
  • 结论
  • 相关文章
  • 参考文献

简介

翻译自 Go 官方博文 Migrating To Go Modules。

Jean de Klerk
21 August 2019

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

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

Go 项目使用多种依赖管理策略,其中对 vendor 包的管理有两个比较流行的工具 dep 和 glide,但他们在行为上有很大的差异,而且并不是总能很好地同时使用。一些项目将其整个 GOPATH 目录存储在一个 Git 仓库中。其他人则只依赖于 go get 并期望在GOPATH中安装较新版本的依赖项。

Go 的模块系统在 Go 1.11 中引入,它提供了一个内置在 Go 命令中的官方依赖管理解决方案。本文描述了将项目转换为模块的相关工具和技术。

请注意:如果您的项目已经标记为 v2.0.0 或更高版本,则在添加 go.mod 文件时需要更新模块路径。我们将在以后的一篇文章中解释如何做到这一点,而不会破坏您的用户。

将项目迁移至 Go modules

在开始过渡到 Go 模块时,项目可能处于下面三种状态之一:

  • 全新的 Go 项目
  • 没用采用模块来管理依赖的已建立的 Go 项目
  • 不采用任何依赖管理器的已建立的 Go 项目

第一种情况是在使用 Go Modules中介绍的;我们将在本文中讨论后两种情况。

使用依赖管理器

若要转换已使用依赖管理工具的项目,请运行以下命令:

$ git clone https://github.com/my/project
[...]
$ cd project
$ cat Godeps/Godeps.json
{"ImportPath": "github.com/my/project","GoVersion": "go1.12","GodepVersion": "v80","Deps": [{"ImportPath": "rsc.io/binaryregexp","Comment": "v0.2.0-1-g545cabd","Rev": "545cabda89ca36b48b8e681a30d9d769a30b3074"},{"ImportPath": "rsc.io/binaryregexp/syntax","Comment": "v0.2.0-1-g545cabd","Rev": "545cabda89ca36b48b8e681a30d9d769a30b3074"}]
}
$ go mod init github.com/my/project
go: creating new go.mod: module github.com/my/project
go: copying requirements from Godeps/Godeps.json
$ cat go.mod
module github.com/my/projectgo 1.12require rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca
$

go mod init 创建一个新的 go.mod 文件,并自动从 Godeps.json、Gopkg.lock 或许多其他受支持的格式导入依赖项。go mod init 参数是模块路径,表示模块的位置。

现在是暂停并运行 go buildgo test 的好时机。后面的步骤可能会修改你的 go.mod 文件,所以如果您喜欢采用迭代的方法,当前的 go.mod 文件最接近于之前的依赖说明。

$ go mod tidy
go: downloading rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca
go: extracting rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca
$ cat go.sum
rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca h1:FKXXXJ6G2bFoVe7hX3kEX6Izxw5ZKRH57DFBJmHCbkU=
rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
$

go mod tidy 找到所有依赖的包及间接依赖的包,添加到 go.mod 文件中,并删除无用的依赖包。如果直接依赖包未启用 Go Module,那么间接依赖包的尾部将被注释 // indirect 标记。在将 go.mod 文件提交到版本库之前,最好先运行 go.mod tidy

让我们确保代码可以成功构建和测试:

$ go build ./...
$ go test ./...
[...]
$

请注意,其他依赖管理器可能在单个包或整个代码仓库(而不是模块)级别指定依赖项,并且通常不识别依赖项的 go.mod 文件中指定的依赖项。因此,您可能不会得到与以前完全相同的每个包的版本,并且有升级变更带来的风险。因此,遵循上述命令并对结果的依赖项进行审计是很重要的。要做到这一点,请运行:

$ go list -m all
go: finding rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca
github.com/my/project
rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca
$

并将结果版本与旧的依赖管理文件进行比较,以确保所选版本是适当的。如果您发现的版本不是您想要的,您可以使用 go mod why -m 和/或 go mod graph 找出原因,然后使用 go get 并升级或降级到正确的版本。例如:

$ go mod why -m rsc.io/binaryregexp
[...]
$ go mod graph | grep rsc.io/binaryregexp
[...]
$ go get rsc.io/binaryregexp@v0.2.0
$

没有依赖管理器

对于没有依赖关系管理系统的 Go 项目,首先创建一个 go.mod 文件:

$ git clone https://go.googlesource.com/blog
[...]
$ cd blog
$ go mod init golang.org/x/blog
go: creating new go.mod: module golang.org/x/blog
$ cat go.mod
module golang.org/x/bloggo 1.12
$

如果没有来自以前的依赖项管理器的配置文件,go mod init 将创建一个只包含 module 和 go 指令的 go.mod 文件。在本例中,我们将模块路径设置为 golang.org/x/blog,因为这是它的自定义导入路径。用户可以使用此路径导入包,我们必须小心不要随意更改它。

module 指令声明模块路径,go 指令声明用于编译模块内代码 Go 语言的预期版本。

接下来,运行 go mod tidy 以添加模块的依赖项:

$ go mod tidy
go: finding golang.org/x/website latest
go: finding gopkg.in/tomb.v2 latest
go: finding golang.org/x/net latest
go: finding golang.org/x/tools latest
go: downloading github.com/gorilla/context v1.1.1
go: downloading golang.org/x/tools v0.0.0-20190813214729-9dba7caff850
go: downloading golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
go: extracting github.com/gorilla/context v1.1.1
go: extracting golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
go: downloading gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
go: extracting gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
go: extracting golang.org/x/tools v0.0.0-20190813214729-9dba7caff850
go: downloading golang.org/x/website v0.0.0-20190809153340-86a7442ada7c
go: extracting golang.org/x/website v0.0.0-20190809153340-86a7442ada7c
$ cat go.mod
module golang.org/x/bloggo 1.12require (github.com/gorilla/context v1.1.1golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7golang.org/x/text v0.3.2golang.org/x/tools v0.0.0-20190813214729-9dba7caff850golang.org/x/website v0.0.0-20190809153340-86a7442ada7cgopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
)
$ cat go.sum
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
git.apache.org/thrift.git v0.0.0-20181218151757-9b75e4fe745a/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
[...]
$

go mod tidy 添加了模块依赖的所有包和间接依赖包,并生成一个 go.sum 文件,其中包含每一个依赖包特定版本的校验和。让我们在结束时确保代码仍能生成并通过测试:

$ go build ./...
$ go test ./...
ok      golang.org/x/blog   0.335s
?       golang.org/x/blog/content/appengine [no test files]
ok      golang.org/x/blog/content/cover 0.040s
?       golang.org/x/blog/content/h2push/server [no test files]
?       golang.org/x/blog/content/survey2016    [no test files]
?       golang.org/x/blog/content/survey2017    [no test files]
?       golang.org/x/blog/support/racy  [no test files]
$

请注意,当 go mod tidy 添加一个依赖时,它会添加该模块的最新版本。如果您的 GOPATH 包含了一个较旧版本的依赖项,随后发布了一个突破性的更改,您可能会看到 go mod tidygo buildgo test 发生错误。如果发生这种情况,请尝试使用 go get(例如,go get github.com/broken/module@v1.1.0) 降级到旧版本,或者花点时间使您的模块与每个依赖项的最新版本兼容。

模块模式下的测试

有些测试在迁移到 Go 模块后可能需要调整。

如果测试需要在包目录中写入文件,则当包目录位于模块缓存中时,它可能会失败,因为模块缓存是只读的。特别是,这可能导致 go test all 失败。测试时应该将需要写入的文件复制到临时目录中。

如果测试依赖于相对路径(…/package-in-another-module)来定位和读取另一个包中的文件,且依赖的包位于另一个模块中,测试将失败。该模块将位于模块缓存的版本子目录或 replace 指令中指定的路径。如果是这样的话,您可能需要将测试输入拷贝到您的模块中,或者将测试输入从原始文件转换为嵌入在 .go 源文件中的数据。

如果期望测试中的 Go 命令以 GOPATH 模式运行,则可能会失败。如果是这样的话,您可能需要向被测试的源码文件目录中添加一个 go.mod 文件,或者显式地设置 GO111MODULE=OFF。

go env -w GO111MODULE=OFF

发布发行版

最后,您应该标记并发布模块的新版本。如果还没有发布任何版本,这是可选的。但是如果没有正式版本,下游用户将依赖于使用伪版本的特定提交,这可能更难支持。

$ git tag v1.2.0
$ git push origin v1.2.0

新的 go.mod 文件为您的模块定义了一个规范的导入路径,并添加了 Go 最低版本要求。如果您的用户已经使用了正确的导入路径,并且您的模块还没有进行中断性的更改,那么添加 go.mod 文件是向后兼容的,但这是一个重大的更改,可能会暴露出现有已知的问题。如果有现有的版本标记,则应该增加 minor version。请参阅 Publishing Go Modules,以了解如何增加和发布版本。

导入和规范模块路径

每个模块在 go.mod 文件中声明其模块路径。每个引用模块中的包的导入语句都必须将模块路径作为包路径的前缀。但是,有时可能会遇到通过不同的导入路径提供包的代码仓库,比如 golang.org/x/lint 和 github.com/golang/lint 都指向模块 go.googlesource.com/lint。代码库中包含的 go.mod 文件声明其路径为 golang.org/x/lint,因此只有该路径对应于一个有效模块。

Go 1.4 提供了一种使用 // import comments 规范导入路径的机制,但是包作者并不一定会提供它们。因此,在模块之前编写的代码可能对模块使用了非规范的导入路径,但并不会出现不匹配的错误。在使用模块时,导入路径必须与模块规范路径匹配,因此可能需要更新 import 语句:例如,您可能需要将import “github.com/golang/lint” 更改为 import “golang.org/x/lint”

对于主要版本 2 或更高版本的 Go 模块,会出现另一种情况,即模块的规范路径可能与其仓库路径不同。一个主要版本高于 1 的 Go 模块必须在其模块路径中包含一个主版本后缀:例如,版本 v2.0.0 必须具有后缀 /v2。但是,import 语句可能引用了模块中没有该后缀的包。例如,非模块用户使用 github.com/russrose/blackfriday/v2 包对应于版本 v2.0.1 可能已经将其导入为github.com/russrose/blackfriday,那么需要更新导入路径以包含 /v2 后缀。

结论

对于大多数用户来说,转换到 Go 模块应该是一个简单的过程。由于非规范的导入路径或依赖项中的破坏更改,可能偶尔会出现的问题。以后的文章将探讨发布新版本、v2 及更高版本以及调试异常情况的方法。

如果想提供反馈并帮助塑造 Go 依赖管理的未来,请发送错误报告或经验报告。

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

相关文章

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

参考文献

[1] 【Go 专家编程】go.mod 文件中的indirect准确含义

Part 2 —— 迁移到 Go Modules相关推荐

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

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

  2. Go语言的依赖解决方案Go modules(go.mod、go mod)、Goland使用Go mod模式创建工程、GO111MODULE

    文章目录 一.关于Go modules 1. GOPATH 2. 为什么弃用 GOPATH 模式 3. Go Modules基本使用(Go mod 使用记录) 4. Go modules 中常用环境变 ...

  3. #61 Go Modules、Go Module Proxy 和 goproxy.cn

    #61 Go Modules.Go Module Proxy 和 goproxy.cn Go 1.11 推出的模块(Modules)为 Go 语言开发者打开了一扇新的大门.随着模块一起推出的还有模块代 ...

  4. Go Modules详解

    Go modules 是 Go 语言中正式官宣的项目依赖解决方案,Go modules(前身为vgo)于 Go1.11 正式发布,在 Go1.14 已经准备好,并且可以用在生产上(ready for ...

  5. css模块化及CSS Modules使用详解

    什么是css模块化? 为了理解css模块化思想,我们首先了解下,什么是模块化,在百度百科上的解释是,在系统的结构中,模块是可组合.分解和更换的单元.模块化是一种处理复杂系统分解成为更好的可管理模块的方 ...

  6. 干货满满的 Go Modules 和 goproxy.cn

    大家好,我是一只普通的煎鱼,周四晚上很有幸邀请到 goproxy.cn 的作者 @盛傲飞(@aofei) 到 Go 夜读给我们进行第 61 期 <Go Modules.Go Module Pro ...

  7. dep指定版本 go_Go 1.12 版本的新特性

    Go 1.12 昨天,Go 官方发布 1.12 版本.本文介绍下 Go 1.12 版本变更的内容. Go 1.12 正式版发布了,距离上个正式发布版 Go 1.11 已经过去半年.跟往常一样,Go 1 ...

  8. 项目改用GoModules管理依赖的方法和经验总结

    Go语言官方提供的依赖包管理工具已经发布很久了,有很多大佬的文章对Go Modules做了非常详尽的介绍,比如煎鱼大佬的: Go Modules终极入门(文章链接:https://juejin.cn/ ...

  9. Flutter2 的 Sound null safety ?!以及发布pub上面的null safety标签实现

    Flutter2 来了 2021年3月最大的猿圈事件莫过于Flutter Engage上,Flutter2的官宣了. 我从燃爆的Flutter2登场视频上感受到了一个由Flutter引领的大前端新纪元 ...

最新文章

  1. 【Java】面试高频考题---topK问题详解(堆heap求解)
  2. python拷贝linux文件到windows_windows python文件拷贝到linux上执行问题
  3. 如何解决ALV的负数符号前显的问题
  4. 【Step1】【SPFA】poj2457-Part Acquisition
  5. 腾达fh307没有显示服务器名,腾达(Tenda)FH307路由器上网设置 | 192路由网
  6. 扔盘子(51Nod-1279)
  7. Eclipse导入的项目中的中文都是乱码,如何解决?
  8. 流程控制库async
  9. 可靠性测试设备技术含量_电气自动化控制设备的可靠性测试(1)
  10. 语音合成论文和英伟达撞车,韩国小哥紧急放出全部草稿代码和样本 | 资源帖...
  11. python程序封装成exe_如何将python脚本封装成exe程序?
  12. 机器学习与深度学习基础概念介绍
  13. 大数据分析,利用向外扩展技术深入挖掘商业价值
  14. Struts2校验器(二)之注解
  15. vue-cli工具搭建vue-webpack项目
  16. vue 下载文件跨域
  17. linux测坏道脚本,linux测试硬盘坏道
  18. java中美元符号的作用_MyBatis中#号与美元符号的区别
  19. 将maximo工具栏图标放置在正上方
  20. android热更新机制

热门文章

  1. Palo Alto 再次修复一个严重的 PAN-OS 漏洞
  2. Traefik-kubernetes 初试
  3. spring boot框架学习2-spring boot核心(1)
  4. 阿里云前端周刊 - 第 15 期
  5. Zabbix 3.0 安装部署
  6. 如何手动删除并重新安装 .NET Framework 2.0
  7. sqlite 交叉编译-转
  8. 从头构建自己的Linux系统 -转
  9. 文档转换html6,html学习文档-6、HTML 文本格式化(示例代码)
  10. 【note】Java程序设计基础第五版(下)