Go module

Go 在 包依赖管理缺少泛型 两个方面饱受诟病,它们也是 Go 粉们最希望 Go 核心 Team 重点完善的两个方面。

Go modules 是 go team 在解决包依赖管理方面的一次勇敢尝试,庆幸自己不用去学习 dep,vgo 之类的了。

2021.07.24 更新关于 Go 1.16 的内容

可以直接看 Release Note 或者这篇 Go 1.16 中关于 go get 和 go install 你需要注意的地方

  • go buildgo test 默认情况下不再修改 go.modgo.sum。可通过 go mod tidygo get 或者手动完成;
  • go install 命令可以接受一个版本后缀了,例如 go install sigs.k8s.io/kind@v0.9.0,并且它是在模块感知的模式下运行,可忽略当前目录或上层目录的 go.mod 文件。这对于在不影响主模块依赖的情况下,安装二进制很方便;
  • 在将来,go install 被设计为用于构建和安装二进制文件,go get 则被设计为用于编辑 go.mod 变更依赖,并且使用时应该与 -d 参数共用,在将来版本中 -d 可能会默认启用;
  • 基本上 go install <package>@<version> 是用于命令的全局安装:例如:go install sigs.k8s.io/kind@v0.9.0
  • go get 安装二进制的功能,后续版本将会删除;
  • go get 主要被设计为修改 go.mod 追加依赖之类的,但还存在类似 go mod tidy 之类的命令,所以使用频率可能不会很高;

GOPATH

先来厘清一下思路。$GOPATH 是所谓的工作目录,它下面有 3 个文件夹 bin, pkg, src

  • bin: 如果你安装的包有 main 入口的话,go install 之后放这里,是 2 进制文件。
  • pkg: 如果你安装的包没有 main 入口的话,go install 之后放这里。
  • src: 基本上你的开发工作都在这里进行,用来放置源码。

所以,当安装完 Go 之后,所有教材都会推荐你先设置一下 $GOPATH

# A typical Go workspace
bin/
    myapp							# Executable binary
    hello						    # Executable binary
pkg/
   github.com/callicoder/example/
       numbers.a                    # Package archive
       strings.a                    # Package archive
   github.com/gorilla/
       mux.a                        # Package archive
   go.uber.org/
       zap.a                        # Package archive
src/
    github.com/callicoder/example/  # Project repository
       .git/
       myapp/
          app.go                    # Executable program containing main package and function
       numbers/                     # Go Package (contains utility functions for working with numbers)
          prime.go
          prime_test.go
       strings/                     # Go Package (contains utility functions for working with strings)
          reverse.go
          trim.go
    github.com/gorilla/mux/			# 3rd Party package
       #... package contents
    go.uber.org/zap/				# 3rd Party package
       #... package contents
    hello/     						# Local package (not published anywhere)
       hello.go

	# ... (more repositories and packages omitted) ...

Building / Installing

For commands which have package main

  • go build builds the command and leaves the result in the current working directory.
  • go install builds the command in a temporary directory then moves it to $GOPATH/bin.

For packages

  • go build builds your package then discards the results.
  • go install builds then installs the package in your $GOPATH/pkg directory.

go get

这个命令在内部实际上分成了两步操作:第一步是下载源码包,第二步是执行 go install。

当你执行 go get github.com/boyter/scc/ 后,会从 Github 克隆代码到你本地的 $GOPATH/src/github/boyter/scc/ 目录。

参数介绍:

  • -d 只下载不安装
  • -u 强制使用网络去更新包和它的依赖包
  • -v 显示操作流程的日志及信息,方便检查错误
  • -f 只有在你包含了 -u 参数的时候才有效,不让 -u 去验证 import 中的每一个都已经获取了,这对于本地 fork 的包特别有用
  • -fix 在获取源码之后先运行 fix,然后再去做其他的事情
  • -t 同时也下载需要为运行测试所需要的包

go get 和 go install 的区别

  • go get 执行:download、compile、install
  • go install 执行:compile、install

推荐使用 go get -dgo install

为什么要存在 go install 呢,使用 go get 不是够用了么?

因为 go get 第一步就是 download 远程的库,如果就就想要使用本地的版本,go get 是办不到的,所以如果你不需要 download 你需要就可以使用 go install

更多相关可以阅读 https://stackoverflow.com/questions/24878737/what-is-the-difference-between-go-get-and-go-install

VSCode 和 GOPATH

  1. 首次安装 vscode-go 插件后,会自动使用 go env 环境变量 GOPATH 设置的路径。
  2. 在 VSCode 的 User settings 中,设置 go.gopath 会覆盖步骤 1 的设置。
  3. 在 VSCode 的 Workspace settings 中,设置 go.gopath 会覆盖步骤 2 的设置。
  4. go.inferGopath 如何设为 true,会覆盖 go.gopath 设置,VSCode 会根据当前打开的目录作为工作区去推断 GOPATH
    • 其方式为:向上搜索打开的文件夹,一旦发现 src 就将其上层目录设置为 GOPATH
    • 例如:你打开了目录 /aaa/bbb/ccc/src,那么 GOPATH 会被自动设置为 GOPATH=/aaa/bbb/ccc
  5. 通常 go get 会把代码安装在 GOPATH 对应的目录中,也可以通过修改插件的 go.toolsGopath 设置一个专门的目录,不过设置完之后别忘了执行 Go: Install Tools
    • go.toolsGopath 如果没设置的话,默认还会放根据 1 - 4 步骤中设置的路径来存放文件

最后 debug 工具无法使用上面步骤的设置项,它只认全局 GOPATH,如果你没有设置 GOPATH(不可能),它会按照步骤4的方式去推测。

结论

建议 VSCode 只设置自动回溯,找到合适的路径。

{
    "go.inferGopath": true
}

使用 go modules 后,GOPATH 是废弃的。这里这么定义,是为了 VSCode F12 跳转指定代码。VSCode 的一些插件还是需要 GOPATH 的。

Go Modules

好处

  • 不用再定义 GOPATH,这里指的是 go build 、 go install 等等 go 命令。IDE 插件目前还是需要 GOPATH。
  • 工程目录不再需要放置 src 目录下
  • 不再需要 vendor 机制以及其他第 3 方 dep 工具
  • 工程内不再有依赖库代码

简单总结:代码可以随意放置,执行 Go 命令,不再需要指定 GOPATH。

GO111MODULE

Go Modules 是否启用目前由环境变量 GO111MODULE 控制:

  • off - 则 go 命令从不使用新模块支持。它查找vendor 目录和 GOPATH 以查找依赖关系,也就是继续使用 GOPATH模式
  • on - 则 go 命令需要使用模块,go 会忽略 GOPATH 和 vendor 文件夹,只根据 go.mod 下载依赖。
  • auto 或未设置 - 则 go 命令根据当前目录启用或禁用模块支持。仅当当前目录位于 GOPATH/src 之外并且其本身包含 go.mod 文件或位于包含 go.mod 文件的目录下时,才启用模块支持。

基础用法

myproj
├── main.go
└── pkg
    └── myapi
        └── data
            └── api.go
  1. Creating a new module - 进入项目目录 myproj,执行 go mod init myproj 会在当前目录下创建一个 go.mod 文件。
  2. Adding a dependency
    • 方法1: 有了模块定义,然后执行 go mod tidy 会自动生成依赖,填充 go.mod, go.sum 文件。
    • 方法2: 修改源码用 import 引入模块 然后执行 go build, go test 等命令,会更新 go.mod, go.sum 文件。
  3. Upgrading dependencies - go list -m all 查看当前项目正在使用的 package 版本,然后执行 go get rsc.io/sampler 来更新指定的 package 再执行 go test 会自动更新 go.mod 文件。

跟 Gemfile 一样,你可以在 go.mod 指定依赖库的版本。如果没有指定,go 命令会自动下载代码中的依赖的最新版本。

如果依赖包地址更换了咋办

在 go.mod 文件里用 replace 替换包

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

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

主要是方便其它根目录的包通过 模块名+路径 的方式来引用。

例如,在项目下新建目录 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()
}

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

升级依赖项

>$ go list -m -versions github.com/gin-gonic/gin
// 将会列出 Gin 版本历史
github.com/gin-gonic/gin v1.1.1 v1.1.2 v1.1.3 v1.1.4 v1.3.0

将版本更新到上个版本,这里只是个演示。

>$ go get github.com/gin-gonic/gin@v1.1.4 // 只需要在依赖后面加上 @version 就可以了
>$ go list -m all
// 看到了版本变化
github.com/gin-gonic/gin v1.1.4

或者可以使用 go mod 来进行版本的切换,这样就需要两个步骤了:

>$ go mod edit -require="github.com/gin-gonic/gin@v1.1.4" // 修改 go.mod 文件
>$ go mod tidy //下载更新依赖

常用命令列表

  • go mod init 初始化模块,例如 go mod init github.com/wjp2013/hello
  • go build, go test 和其它构建命令会自动为 go.mod 添加新的依赖
  • go get 改依赖关系的所需版本(或添加新的依赖关系)
  • go list -m all 列出当前模块及其所有依赖项
  • go mod tidy 拉取缺少的模块,移除不用的模块

不常用命令

  • go mod download 下载依赖包到本地 cache
  • go mod edit 编辑 go.mod 文件,选项有 -json、-require 和 -exclude,可以使用帮助 go help mod edit
  • go mod graph 打印模块依赖图
  • go mod vendor 将依赖复制到 vendor 目录
  • go mod verify 验证依赖是否正确
  • go mod why 解释为什么需要依赖

相关文章

GOPATH

module

如果觉得我的文章对您有用,请在支付宝公益平台找个项目捐点钱。 @Victor Mar 20, 2019

奉献爱心