包管理是 Go 一直被诟病做得不好的功能之一. 先前版本 (go 1.11 之前) 的主要缺点之一是 go get 是缺乏对依赖包版本的管理和对可复制构建 (reproducible build) 的支持. Go 社区已经开发了一些包管理器和工具作为版本化包依赖的事实标准解决方案, 如 https://github.com/Masterminds/glide ,dep 以及一些 辅助工具 等.
"我在生产构建中使用 go get." - 没有人这么说过.
Go 语言的包管理实现可追溯到 Google 公司内的代码依赖管理(Google 将内部所有源代码都存放在一个巨大的单体存储库中). 我们来分析一下在 "Go module" 之前 Go 语言的包管理工具都出了什么问题.
依赖包的版本化
依赖包的本地缓存(vendor)
GOPATH 的必要性
依赖包的版本化
go get 默认情况下不支持包版本控制. go 软件包管理的第一版实现背后的想法是 - 不需要包版本控制, 不需要第三方包存储库, 您可以从当前分支中构建所有内容.
在 Go 1.11 之前的版本中, 添加依赖项意味着将该依赖项的源代码仓库克隆到 $GOPATH 下面. 就是这样, 没有版本的概念. 版本始终指向克隆时刻的主分支. 出现了另一个主要问题是, 当不同的项目需要依赖包的不同版本时, Go 包管理工具无法实现.
依赖包的本地缓存(vendor)
依赖包本地缓存通常是指相关依赖包与项目存储在同一位置. 这通常意味着将您的依赖项源码也提交到源管理系统中, 例如 Git.
考虑这样一种情况 - A 使用依赖项 B, 而 B 使用了 C 版本在 1.5 版本中引入一个功能, 这时 B 必须确保 A 在构建时使用的也是 C 1.5 或更高版本. 在 Go 1.5 之前的版本中, 没有一种机制可以在不重写导入路径的情况下将依赖包代码与命令绑定在一起.
GOPATH 的必要性
GOPATH 存在的主要原因有两个:
在 Go 中, import 声明通过其完全限定的导入路径来引用包. GOPATH 存在可以方便 Go 工具计算 GOPATH/src 内的任何目录所涉及软件包的绝对导入路径.
它是 Go get 命令存储包依赖项的位置.
这有什么问题?
GOPATH 不允许开发人员像其他语言一样选择任意喜欢的目录签出项目的源代码.
此外, GOPATH 不允许开发人员同时检出某个项目 (或其依赖项) 的多个副本.
Go Module 介绍
Go 1.11 引入了对 Go 模块 (module) 的初步支持. 下面摘自 Go Wiki:
一个模块是一组相关的 Go 包的集合, 这个包集合被当做一个独立的单元进行统一版本管理. 模块精确记录了依赖要求并支持创建可复制的构建.
Go 模块带来了三个重要的内置功能:
go.mod 文件, 它与 package.JSON 或 Pipfile 文件的功能类似.
机器生成的传递依赖项描述文件 - go.sum.
不再有 GOPATH 限制. 模块可以位于任何路径中.
- $ go help mod
- Go mod provides access to operations on modules.
- Note that support for modules is built into all the go commands,
- not just 'go mod'. For example, day-to-day adding, removing, upgrading,
- and downgrading of dependencies should be done using 'go get'.
- See 'go help modules' for an overview of module functionality.
- Usage:
- go mod <command> [arguments]
- The commands are:
- download download modules to local cache
- edit edit go.mod from tools or scripts
- graph print module requirement graph
- init initialize new module in current directory
- tidy add missing and remove unused modules
- vendor make vendored copy of dependencies
- verify verify dependencies have expected content
- why explain why packages or modules are needed
- Use "go help mod <command>" for more information about a command.
更多相关讨论在 这里 .
迁移到 Go Module
要使用 Go 模块, 请更新 Go 到 1.11 及以上版本. 由于不再需要 GOPATH, 因此可以通过以下两种方式之一激活模块支持(译注: 下面的行为仅适用于 Go 1.11~Go 1.12, Go 1.13 版本 https://tip.golang.org/doc/go1.13 默认开启 Go module, 无论是否在 GOPATH 下, 除非 GO111MODULE=off):
在 GOPATH/src 之外的目录中调用 Go 命令, 并在当前目录中存在一个有效的 go.mod 文件.
如果源码在 GOPATH 之下, Go 模块将不起作用. 要改变此行为, 请设置环境变量 GO111MODULE=on 后再调用 Go 命令.
让我们通过以下简单的步骤开始迁移:
由于 GOPATH 不再必要的了, 将 module 移出 GOPATH.
在项目根目录中, 创建初始模块定义 - go mod init GitHub.com/username/repository.go mod 还会自动转换现有的包管理器 (如 dep 和 Gopkg,glide 以及 其他六种 ) 的依赖关系. 这将创建一个名为 go.mod 的文件, 该文件存储了模块名以及模块的依赖项及其版本.
- $ cat go.mod
- module GitHub.com/deepsourcelabs/cli
- go 1.12
- require (
- GitHub.com/certifi/gocertifi v0.0.0-20190410005359-59a85de7f35e
- GitHub.com/getsentry/raven-go v0.2.0
- GitHub.com/pkg/errors v0.0.0-20190227000051-27936f6d90f9
- )
运行 go build 会创建一个 go.sum 文件, 其中包含特定模块版本的内容的预期校验和. 这是为了确保这些模块将来的下载内容与第一次下载是相同的. 请注意, go.sum 不是锁文件.
- $ cat go.sum
- GitHub.com/certifi/gocertifi v0.0.0-20190410005359-59a85de7f35e h1:9574pc8MX6rF/QyO14SPHhM5KKIOo9fkb/1ifuYMTKU=
- GitHub.com/certifi/gocertifi v0.0.0-20190410005359-59a85de7f35e/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
- GitHub.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
- GitHub.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
- GitHub.com/pkg/errors v0.0.0-20190227000051-27936f6d90f9 h1:dIsTcVF0w9viTLHXUEkDI7cXITMe+M/MRRM2MwisVow=
- GitHub.com/pkg/errors v0.0.0-20190227000051-27936f6d90f9/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
关于版本控制的注意事项: 为了保持向后兼容性, 如果模块的版本为 v2 或更高版本, 则模板的主版本必须以 / vN 的形式被包含在 go.mod 文件中使用的模块路径的末尾. 比如: module GitHub.com/username/repository/v2
日常命令
列出依赖项
go list -m all 列出当前模块及其所有依赖项.
- $ go list -m all
- GitHub.com/deepsourcelabs/cli
- GitHub.com/certifi/gocertifi v0.0.0-20190410005359-59a85de7f35e
- GitHub.com/getsentry/raven-go v0.2.0
- GitHub.com/pkg/errors v0.0.0-20190227000051-27936f6d90f9
在 go list 输出中, 当前模块 (也称为主模块) 始终是第一行, 其后是路径排序所有依赖模块.
列出软件包的可用版本
go list -m -versions GitHub.com/username/repository 列出软件包的可用版本.
- $ go list -m -versions GitHub.com/getsentry/raven-go
- GitHub.com/getsentry/raven-go v0.1.0 v0.1.1 v0.1.2 v0.2.0
添加依赖
添加依赖项是隐式的. 在代码中导入依赖项后, 运行 go build 或 go test 命令将获取模块的最新版本并将其添加到 go.mod 文件中. 如果要显式添加依赖项, 请运行 go get GitHub.com/username/repository.
依赖项的升级 / 降级
go get GitHub.com/username/repository@vx.x.x 下载并设置依赖项和更新 go.mod 文件的特定版本.
- $ go get GitHub.com/getsentry/raven-go@v0.1.2
- go: finding GitHub.com/getsentry/raven-go v0.1.2
- go: downloading GitHub.com/getsentry/raven-go v0.1.2
- go: extracting GitHub.com/getsentry/raven-go v0.1.2
- $ cat go.mod
- module GitHub.com/deepsourcelabs/marvin-go
- go 1.12
- require (
- GitHub.com/certifi/gocertifi v0.0.0-20190410005359-59a85de7f35e
- GitHub.com/getsentry/raven-go v0.1.2
- GitHub.com/pkg/errors v0.0.0-20190227000051-27936f6d90f9
- )
- $ cat go.sum
- GitHub.com/certifi/gocertifi v0.0.0-20190410005359-59a85de7f35e h1:9574pc8MX6rF/QyO14SPHhM5KKIOo9fkb/1ifuYMTKU=
- GitHub.com/certifi/gocertifi v0.0.0-20190410005359-59a85de7f35e/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
- GitHub.com/getsentry/raven-go v0.1.2 h1:4V0z512S5mZXiBvmW2RbuZBSIY1sEdMNsPjpx2zwtSE=
- GitHub.com/getsentry/raven-go v0.1.2/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
- GitHub.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
- GitHub.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
- GitHub.com/pkg/errors v0.0.0-20190227000051-27936f6d90f9 h1:dIsTcVF0w9viTLHXUEkDI7cXITMe+M/MRRM2MwisVow=
- GitHub.com/pkg/errors v0.0.0-20190227000051-27936f6d90f9/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
vendor 依赖项
使用模块时, go 命令将完全忽略 vendor 目录. 为了向后兼容旧版 Go, 或确保将用于构建的所有文件一起存储在单个文件树中, 请运行 go mod vendor.
这将在主模块的根目录中创建一个 vendor 目录, 并将依赖模块中的所有软件包存储在该目录中.
注意: 要使用主模块的顶级 vendor 目录进行构建, 请运行'go build -mod=vendor'.
删除未使用的依赖项
go mod tidy 将删除未使用的依赖项并更新 go.mod 文件.
常见问题解答
GOPATH 不再需要了?
是, 永别了 GOPATH.
默认情况下拉取哪个版本?
go.mod 文件和 go 命令通常将语义版本用作描述模块版本的标准形式, 以便可以比较版本以确定哪个版本应早于或晚于其他版本. v1.2.3 通过在基础源存储库中标记 (tag) 修订来引入类似的模块版本. 未标记 (untag) 的修订版可以使用 "伪版本" 之类的来引用: v0.0.0-yyyymmddhhmmss-abcdefabcdef, 其中时间是 UTC 的提交时间, 最后的后缀是提交哈希的前缀.
go.sum 应该被检入到版本库中吗?
是.
鉴于本人近期较忙, 又不希望让博客长草, 近一段时间会挑选翻译一些笔者认为比较优秀的外文文章分享给大家.
本文翻译自 《Package management in Go - brief overview of package management in Go - pre and post Go modules》 https://deepsource.io/blog/go-modules/ .
我的网课 "Kubernetes 实战: 高可用集群搭建, 配置, 运维与应用 https://coding.imooc.com/class/284.html" 在慕课网上线了, 感谢小伙伴们学习支持!
我爱发短信 https://51smspush.com/ : 企业级短信平台定制开发专家 https://51smspush.com/
smspush : 可部署在企业内部的定制化短信平台, 三网覆盖, 不惧大并发接入, 可定制扩展; 短信内容你来定, 不再受约束, 接口丰富, 支持长短信, 签名可选.
著名云主机服务厂商 DigitalOcean 发布最新的主机计划, 入门级 Droplet 配置升级为: 1 core CPU,1G 内存, 25G 高速 SSD, 价格 5$/ 月. 有使用 DigitalOcean 需求的朋友, 可以打开这个 链接地址 https://m.do.co/c/bff6eed92687 :https://m.do.co/c/bff6eed92687 开启你的 DO 主机之路.
我的联系方式:
微博: https://weibo.com/bigwhite20xx
微信公众号: iamtonybai
博客: tonybai.com
GitHub: https://github.com/bigwhite
微信赞赏:
商务合作方式: 撰稿, 出书, 培训, 在线课程, 合伙创业, 咨询, 广告合作.
© 2019,bigwhite. 版权所有.
来源: http://www.tuicool.com/articles/memQrqq