Go中的包管理

419 阅读4分钟

软件包管理是Go一直以来所遗漏的东西之一。之前(1.11之前)go get 的主要缺点之一是缺乏对管理依赖版本和实现可重复构建的支持。社区已经开发了包管理器和工具,如Glide、dep和其他许多工具,作为版本依赖的事实解决方案。

"我使用go get进行生产构建"。- 从来没有人这么说过。

Go的包管理的实现可以追溯到Google(它有一个巨大的单体仓库来存放他们所有的源代码)。让我们来分析一下 "pre - go模块 "的软件包管理工具有什么问题。

  1. 版本依赖性
  2. 销售依赖性
  3. 必要性GOPATH

版本依赖性

go get 在默认情况下,go的包管理不支持模块版本。Go的第一版软件包管理的理念是--不需要模块版本管理,不需要第三方模块库,你可以从你的当前分支构建一切。

在Go 1.11之前,添加一个依赖项意味着在你的GOPATH 中克隆该依赖项的源代码库。仅此而已。当时并没有版本的概念。相反,在克隆时它总是指向当前的主分支。当不同的项目需要不同版本的依赖关系时,另一个主要问题就出现了,这也是不可能的。

依赖关系的销售

包的销售通常指的是依赖包与你的项目存储在同一个地方。这通常意味着你的依赖关系被检查到你的源代码管理系统中,如Git。

考虑这种情况--A使用了依赖项B,而B使用了C的1.5版本中引入的依赖项C的一个特性,B必须能够确保A的构建使用C 1.5或更高版本。在Go 1.5之前,没有任何机制可以在不重写导入路径的情况下将依赖性代码与命令一起携带。

GOPATH的必要性

GOPATH 的存在主要有两个原因:

  1. 在Go中,import 声明通过其完全合格的导入路径引用一个包。GOPATH 的存在是为了让go工具从GOPATH/src 内的任何目录中计算出有关包的绝对导入路径。
  2. 用来存储由Go获取的依赖项的位置。go get.

这有什么问题?

  1. GOPATH 它不允许像他们在其他语言中所习惯的那样,在选择的目录中检查项目的源代码。
  2. 此外,GOPATH 不允许开发者同时签出一个项目(或其依赖关系)的多个副本。

引入Go模块

Go 1.11引入了对Go模块的初步支持。来自Go Wiki。

模块是相关Go包的集合,作为一个单一的单元而被版本化。模块记录了精确的依赖性要求,并创建了可重复的构建。

Go模块带来了三个重要的内置功能。

  1. go.mod 文件类似于 或 。package.json Pipfile

  2. 一个由机器生成的横向依赖性描述 -go.sum

  3. 不再有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  [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 " for more information about a command.

迁移到Go模块

要使用 Go 模块,请将 Go 更新到>= 1.11 版本。由于GOPATH 即将消失,人们可以通过以下两种方式之一激活模块支持:

  • GOPATH/src 树之外的目录下调用go 命令,在当前目录下有一个有效的go.mod 文件。
  • 如果源代码在GOPATH 下,Go 模块就不会工作。要覆盖这一行为,请在设置了GO111MODULE=on 环境变量的情况下调用go 命令。

让我们按照这些简单的步骤开始移植吧:

  • 由于GOPATH 不再是必要的,将模块移出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 buildgo test 命令会得到该模块的最新版本并将其添加到go.mod 文件中。如果你想明确地添加一个依赖关系,请运行go get github.com/username/repository

升级/降级一个依赖关系

go get github.com/username/[[email protected]](https://deepsource.io/cdn-cgi/l/email-protection)下载并设置依赖关系的特定版本,并更新go.mod 文件。

$ go get github.com/getsentry/[email protected]
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=

给依赖关系供货

当使用模块时,go命令完全忽略了供应商目录。为了向后兼容旧版本的 Go,或者确保所有用于构建的文件都存储在一个文件树中,请运行go mod vendor

这将在主模块的根目录下创建一个名为vendor 的目录,并将所有来自依赖模块的包存储在那里。

注意:要使用主模块的顶级供应商目录进行构建,运行'go build -mod=vendor'。

删除不使用的依赖项

go mod tidy 修剪不使用的依赖,并更新 文件。go.mod

常见问题

GOPATH 不再需要了吗?

是的,告别GOPATH

哪个版本是默认的?

go.mod文件和go命令一般使用语义版本作为描述模块版本的标准形式,这样就可以通过版本比较来确定哪个版本应该比另一个版本早或晚。像v1.2.3 这样的模块版本是通过在底层源码库中标记一个修订版而引入的。没有标记的修订版可以用 "伪版本 "来指代,如v0.0.0-yyyymmddhhmmss-abcdefabcdef ,其中时间是 UTC 的提交时间,最后的后缀是提交哈希的前缀。

go.sum 应该被检查到版本控制中吗?

是的。