NotePublic/Software/Development/Language/Go/Basic/Go_mod_入门.md

6.8 KiB
Raw Blame History

Go mod 入门

1.什么是 go.mod

Go.mod 是 Golang1.11 版本新引入的官方包管理工具用于解决之前没有地方记录依赖包具体版本的问题,方便依赖包的管理。

Go.mod 其实就是一个 Modules关于 Modules 的官方定义为:

Modules 是相关 Go 包的集合是源代码交换和版本控制的单元。go 命令直接支持使用 Modules包括记录和解析对其他模块的依赖性。Modules 替换旧的基于 GOPATH 的方法,来指定使用哪些源文件。

Modules 和传统的 GOPATH 不同,不需要包含例如 srcbin 这样的子目录,一个源代码目录甚至是空目录都可以作为 Modules只要其中包含有 go.mod 文件。

2.如何使用 go.mod

  1. 首先将 go 的版本升级为 1.11 以上
  2. 设置 GO111MODULE

GO111MODULE 有三个值off, on 和 auto默认值

  • GO111MODULE=offgo 命令行将不会支持 module 功能,寻找依赖包的方式将会沿用旧版本那种通过 vendor 目录或者 GOPATH 模式来查找。
  • GO111MODULE=ongo 命令行会使用 modules而一点也不会去 GOPATH 目录下查找。
  • GO111MODULE=auto默认值go 命令行将会根据当前目录来决定是否启用 module 功能。这种情况下可以分为两种情形:
    • 当前目录在 GOPATH/src 之外且该目录包含 go.mod 文件
    • 当前文件在包含 go.mod 文件的目录下面。

3.go mod 命令

golang 提供了 go mod 命令来管理包。go mod 有以下命令:

![go mod cmd](./img/Go.mod_入门 /001.png)

4.go.mod 如何在项目中使用

首先我们要在 GOPATH/src 目录之外新建工程,或将老工程 copy 到 GOPATH/src 目录之外。

PSgo.mod 文件一旦创建后,它的内容将会被 go toolchain 全面掌控。go toolchain 会在各类命令执行时,比如 go get、go build、go mod 等修改和维护 go.mod 文件。

go.mod 文件内提供了 module, require、replace 和 exclude 四个关键字,这里注意区分与上表中命令的区别,一个是管理 go mod 命令,一个是 go mod 文件内的关键字。

  • module 语句指定包的名字(路径)
  • require 语句指定的依赖项模块
  • replace 语句可以替换依赖项模块
  • exclude 语句可以忽略依赖项模块

下面是我们建立了一个 hello.go 的文件

package main
 
import (
    "fmt"
)
 
func main() {
    fmt.Println("Hello, world!")
}

然后,在当前目录下,命令行运行 go mod init + 模块名称 初始化模块,即:

go mod init hello

运行完之后,会在当前目录下生成一个 go.mod 文件,这是一个关键文件,之后的包的管理都是通过这个文件管理。

官方说明:除了 go.mod 之外go 命令还维护一个名为 go.sum 的文件,其中包含特定模块版本内容的预期加密哈希。

go 命令使用 go.sum 文件确保这些模块的未来下载检索与第一次下载相同的位,以确保项目所依赖的模块不会出现意外更改,无论是出于恶意、意外还是其他原因。 go.mod 和 go.sum 都应检入版本控制。

go.sum 不需要手工维护,所以可以不用太关注。

注意:子目录里是不需要 init 的,所有的子目录里的依赖都会组织在根目录的 go.mod 文件里

接下来,让我们的项目依稀一下第三方包

如修改 hello.go 文件如下,按照过去的做法,要运行 hello.go 需要执行 go get 命令 下载 gorose 包到 $GOPATH/src

package main
 
import (
    "fmt"
    "github.com/gohouse/gorose"
)
 
func main() {
    fmt.Println("Hello, world!")
}

但是,使用了新的包管理就不在需要这样做了,直接:

go run hello.go

稍等片刻… go 会自动查找代码中的包,下载依赖包,并且把具体的依赖关系和版本写入到 go.mod 和 go.sum 文件中。 查看 go.mod它会变成这样

module test
 
require (
    github.com/gohouse/gorose v1.0.5
)

require 关键字是引用,后面是包,最后 v1.11.1 是引用的版本号

这样,一个使用 Go 包管理方式创建项目的小例子就完成了。

5.依赖的包下载到哪里了

使用 Go 的包管理方式,依赖的第三方包被下载到了$GOPATH/pkg/mod 路径下。

6.依赖包的版本是怎么控制的

在上一个问题里,可以看到最终下载在 $GOPATH/pkg/mod 下的包中最后会有一个版本号 v1.0.5,也就是说,$GOPATH/pkg/mod 里可以保存相同包的不同版本。

版本是在 go.mod 中指定的。如果,在 go.mod 中没有指定go 命令会自动下载代码中的依赖的最新版本,本例就是自动下载最新的版本。如果,在 go.mod 用 require 语句指定包和版本 go 命令会根据指定的路径和版本下载包,指定版本时可以用 latest这样它会自动下载指定包的最新版本。

7.可以把项目放在 $GOPATH/src 下吗

可以,但是 go 会根据 GO111MODULE 的值而采取不同的处理方式默认情况下GO111MODULE=auto 自动模式。

  1. auto 自动模式下,项目在$GOPATH/src 里会使用$GOPATH/src 的依赖包,在$GOPATH/src 外,就使用 go.mod 里 require 的包;
  2. on 开启模式1.12 后,无论在$GOPATH/src 里还是在外面,都会使用 go.mod 里 require 的包;
  3. off 关闭模式,就是老规矩。

8.依赖包中的地址失效了怎么办

在 go 快速发展的过程中,有一些依赖包地址变更了。以前的做法:

  1. 修改源码,用新路径替换 import 的地址
  2. git clone 或 go get 新包后copy 到$GOPATH/src 里旧的路径下

无论什么方法,都不便于维护,特别是多人协同开发时。使用 go.mod 就简单了,在 go.mod 文件里用 replace 替换包,例如:

replace golang.org/x/text => github.com/golang/text latest

这样go 会用 github.com/golang/text 替代 golang.org/x/text原理就是下载 github.com/golang/text 的最新版本到 $GOPATH/pkg/mod/golang.org/x/text 下。

9.init 生成的 go.mod 的模块名称有什么用

本例里,用 go mod init hello 生成的 go.mod 文件里的第一行会申明 module hello

因为我们的项目已经不在$GOPATH/src 里了,那么引用自己怎么办?就用模块名+路径。

例如,在项目下新建目录 utils创建一个 tools.go 文件:

package utils
 
import fmt
 
func PrintText(text string) {
    fmt.Println(text)
}

在根目录下的 hello.go 文件就可以 import “hello/utils” 引用 utils

package main
 
import (
    "hello/utils"
    "github.com/astaxie/beego"
)
 
func main() {
    utils.PrintText("Hi")
    beego.Run()
}

10.以前老项目如何用新的包管理

  1. 如果用 auto 模式,把项目移动到$GOPATH/src 外
  2. 进入目录,运行 go mod init + 模块名称
  3. go build 或者 go run 一次