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

简介: Go Modules 介绍与基本操作

01

介绍


Go 自 1.11 以来,包含对 Module 版本的支持。初始原型 vgo 于 2018 年 2 月宣布。2018 年 7 月,Module 版本进入 Go 代码仓库主分支。


Go 1.11 和 1.12 包含对 Go Modules 的初步支持,Go 的新依赖项管理系统使依赖关系版本信息更加明确且更易于管理。


640.jpg

图片来自https://www.callicoder.com/golang-packages/


Module 是存储在文件树中的 Go 包的集合,其根目录有 go.mod 文件。go.mod 文件定义了 Module 的模块路径,该路径也是用于根目录的导入路径,以及其依赖项要求,这些依赖项要求是成功构建所需的其他模块。每个依赖项要求都编写为模块路径和特定的语义版本。


自 Go 1.11 起,可以显式启用模块模式(通过设置 GO111MODULE=on),go 命令允许在当前目录或任何父目录具有 go.mod 文件时使用模块模式,前提是该目录位于 $GOPATH/src 之外。($GOPATH/src 内部,为了兼容性,go 命令仍以旧的 GOPATH 模式运行,即使找到 go.mod 文件。


在 Go 1.13,无需显式设置启用模块模式,只需设置 GO111MODULE=auto,如果发现任何 go.mod,即使在 GOPATH 内部,也表示启用模块模式。

(在 Go 1.13 之前,GO111MODULE=auto 永远不会在 GOPATH 内启用模块模式)。


自 Go 1.14 以来,模块支持被视为可供生产环境使用,鼓励所有用户从其他依赖管理系统迁移到 Module。并且改回需显式设置启用模块模式

(通过设置 GO111MODULE=on),如果不存在 go.mod 文件,大多数模块命令的功能更有限。


在 Go 1.15,可以通过 GOMODCACHE 环境变量设置模块缓存的位置。GOMODCACHE 的默认值是 GOPATH[0]/pkg/mod,可以在此更改模块缓存的位置。


本文将学习使用模块开发 Go 代码时出现的一系列基本操作,示例假定在 Linux 系统中 gopher 用户的家目录。


02

创建一个新 Module


在 $GOPATH/src 外的任何位置,创建一个新的空目录,进入新创建的目录,创建一个新的源码文件:hello.go。


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


再创建一个测试源码文件:hello_test.go。


package hello
import "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 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/hello
go 1.12
$


go.mod 文件仅出现在模块的根目录中。子目录中的包具有导入路径,包括模块路径和子目录路径。例如,如果我们创建了一个子目录 world,

我们不需要(也不想)运行 go mod init。包将自动识别为该模块 example.com/hello 的一部分,导入路径 example.com/hello/world。


03

添加依赖项


Go modules 的主要目的是改进使用其他开发人员编写的代码(即添加依赖项)的体验。


让我们更新我们的 hello.go 导入 rsc.io/quote 并用它来实现函数 Hello:


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


现在让我们再次运行 go test:


$ 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 test 命令使用 go.mod 文件中列出的特定依赖项模块版本解析导入。当它遇到 go.mod 文件中任何模块未提供的包的导入时,go 命令会自动通过「最新版本」来备份包含该包的模块并将其添加到 go.mod。「最新」定义为最新的标记稳定(非预发行)版本,或者最新的标记预发行版本,或者最新的未标记版本。


在我们的示例中,go test 导入 rsc.io/quote 模块的最新版本 rsc.io/quote v1.5.2。


它还下载了两个间接依赖项,

rsc.io/sampler 和 golang.org/x/text。

仅将直接依赖项记录在 go.mod 文件中:


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


再次运行 go test 命令,不会重复下载检索工作,因为 go.mod 现在是最新的,下载的模块在本地缓存目录中($GOPATH[0]/pkg/mod):


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


请注意,虽然 go 命令使添加新的依赖项变得快速而简单,但它并非没有成本。您的模块现在实际上依赖于关键领域(如正确性、安全性和正确许可等)中的新依赖关系。


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


$ go list -m all
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 列表输出中,当前模块(也称为主模块)始终是第一行,后跟按模块路径排序的依赖项。


golang.org/x/text 版本 v0.0.0-20170915032832-14c0d48ead0c 是伪版本的示例,这是特定未标记提交的命令版本语法。


除了 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 都应签入版本控制。


目录
相关文章
|
11天前
|
NoSQL Go Redis
如何使用 Go 和 `go-redis/redis` 库连接到 Redis 并执行一些基本操作
如何使用 Go 和 `go-redis/redis` 库连接到 Redis 并执行一些基本操作
11 1
|
25天前
|
存储 缓存 JSON
Go Modules:Go语言依赖管理的新篇章
Go Modules是Go 1.11引入的依赖管理标准,解决`GOPATH`的依赖冲突问题。
26 1
|
2月前
|
存储 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开发至关重要。
40 2
|
9月前
|
存储 Go API
怎么发布 Go Modules v1 版本?
怎么发布 Go Modules v1 版本?
41 0
|
9月前
|
缓存 JSON Go
Go 语言各个版本支持 Go Modules 的演进史
Go 语言各个版本支持 Go Modules 的演进史
62 1
|
9月前
|
存储 Go API
Go Modules 如何创建和发布 v2 及更高版本?
Go Modules 如何创建和发布 v2 及更高版本?
88 0
|
9月前
|
Go API
Go Modules 介绍与基本操作(下)
Go Modules 介绍与基本操作(下)
45 0
|
11月前
|
Go
Go中对通道的基本操作
Go中Channel的基本操作
58 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 使用一个队列实现
90 0
|
存储 算法 Go
一文读懂Go Modules原理
一文读懂Go Modules原理