之前也写过 Go 管理依赖工具 godep 的使用,当时看 godep 使用起来还是挺方便,其原因主要在于有总比没有强。关于依赖管理工具其实还是想从头聊聊这个需求以及大家做这个功能的各种出发点。

GOPATH 和 GOROOT

GOROOT 这个变量的作用就是为了告诉当前运行的 Go 进程当前 Go 安装在哪里,当你想要运行的时候去哪里找 Go SDK相关的类。

GOPATH 这个设定其实从语言层面上来说就有点反设计模式。主要原因在于 Go 刚出生的时候没有自带包管理功能,默认所有的项目和引用的第三方包都下载到 src 目录下,第三方包对于 Go 而言也是一个可用的Go项目,这一点跟 Java 区别还是挺大。

那 GOPATH 是否可以设定多个呢?当让可以,并且人家就是这么让你用的。

比如你有项目A,设定的 GOPATH 是:/src/projA/,那么A项目所有的依赖都会下载在这个目录。

比如你有项目B,设定的 GOPATH 是:/src/projB/,那么B项目所有的依赖都会下载在这个目录。

而如果你使用同一个 GOPATH,那么你所有的项目 down 下来的依赖都在 src 目录下,这时候就有版本的问题,如果 A 项目想用 依赖 C 的 1.0.0 版本,B 项目想用依赖 C 的 1.0.1 版本,这时候该怎么指定呢?

正确的 GOPATH 就是一个项目一个。

包管理历史上的“恩怨情仇”

所以问题来了,一个项目一个 GOPATH,如果我有三个项目是个超级无敌大系统,可能会 down 下来整个 github 上的开源库,那这存储都是问题啊。

针对 Go 官方对包管理的乱象无动于衷,社区的同学们实在是忍无可忍,相继开源了各种包管理工具。

13 年的时候 Godep 诞生,原理很简单,跟 maven 一样,指定一个统一的包管理目录,将这个目录作为唯一的 GOPATH。也是在这一年 Docker 开始火起来了, Go 作为微服务开发“非官方指定语言”被广泛使用,同时矮子里面拔高子, dep 也绽放异彩。

15年 Go 官方被社区倒逼,1.5 版本同步带上了一个实验性质的功能 vendor 机制。由于默认情况下是关闭态,所以 normal 用户无感知,super 用户才会去体验。1.6 版本的时候该功能才打开为正常使用状态。

那么什么是 vendor 机制呢?简单说就是在你的项目中包含一个 vendor 的文件夹,它代替了通用的 GOPATH 目录,你项目中所有的依赖都会在这里存在。有了 vendor 好处显而易见,你再也不用手工修改 GOPATH 了,每个人运行环境的包也都统一了起来。

但是问题也随之而来,如果你依赖的一个项目也使用 vendor 管理依赖,那不就形成了嵌套依赖了吗?这种情况怎么处理。

当然还有别的各种问题,总之这也不是一个能拿得出手的成熟方案。

后面又出了 glide 等等社区的方案,但是到这里为止官方还是没有出一个能一统天下的方案。

当时 Google 官方的 Go 负责人 Russ 其实是有和 dep 的开发者 Sam 沟通让他修改一些设计以便将 dep 继承到 go 命令中去,但是由于一些观念上的原因 Sam 并没有接受,所以也因此错过一段良缘。

后面官方由于社区的压力在Go 1.11 版本的时候集成了新特性 Go modules。这是首次以官方名义开发的包管理工具。Go 命令直接支持 modules 相关用法。

Go modules

Go modules 是官方推出的依赖管理工具,Go modules 提供了3个重要的功能:

  1. go.mod 文件,它与 package.jsonPipfile 文件的功能类似。
  2. 机器生成的传递依赖项描述文件 : go.sum
  3. 不再有 GOPATH 限制。模块可以位于任何路径中。

要想使用 Go modules 请升级到 1.11 及其以上版本,1.13 版本已经默认开启 Go modules,如果想体验这个功能建议将 Go 版本升级到 1.13。

另外,这个功能默认并不是开启的,需要手动设置环境变量开开启:

go env -w GO111MODULE=on

go env -w 是Go 1.13 新增的命令,用于写入环境变量。写入的地方是os.UserCOnfigDir所在的目录。

GO111MUDULE 变量是 Go modules 的开关。 有以下几个参数:

  • auto:如果项目包含了 go.mod 文件,则启用 Go modules 功能。在Go 1.13 中是默认值。
  • on:始终开启 Go modules。
  • off:禁用 Go modules。
使用

首先我们在 GOPATH外新建一个目录,比如 usr/local/mod-demo,进入目录下,生成 go.mod文件:

Go mod init mod-demo

打开我们刚生成的 go.mod 文件可以看到:

module mod-demo
go 1.13

go.mod 文件是开启 modules 的必备配置文件。它记录了当前项目引用的包数据信息。go.mod 文件中定义了以下关键词:

  • module:用于定义当前项目的模块路径
  • go:用于设置Go 版本信息
  • require:用于设置一个特定的模块版本
  • exclude:用于从使用中排除一个特定的模块版本
  • replace:用于将一个模块版本替换为另一个模块版本

接下来添加项目外的依赖:

package main

import (
"github.com/gin-gonic/gin" ) func main() {
d := gin.Default()
d.GET("/index", func(c *gin.Context) {
c.JSON(200, gin.H{"message":"hello world","data":""})
})
d.Run("127.0.0.1:8080")
}

上面我们添加了 gin 的依赖包,然后看 go.mod 的内容:

module mod-demo

go 1.15

require github.com/gin-gonic/gin v1.6.3

可以看到已经添加了依赖。

以下版本格式都是合法的:

gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7
gopkg.in/vmihailenco/msgpack.v2 v2.9.1
gopkg.in/yaml.v2 <=v2.2.1
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e
latest

Go module 安装 package 的原則是先拉最新的 release tag,若无tag则拉最新的commit,详见 Modules官方介绍。 Go 会自动生成一个 go.sum 文件来记录 dependency tree:

github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
......
......
......

这里会记录当前以来的依赖包所依赖的依赖。

另外,之前我们在 golang.org 上下载不下来的包,直接使用 github 上的镜像,使用 module 之后可以使用 replace 关键字来个替换:

比如你之前引用是这样的:

require (
golang.org/x/text v0.3.2
)

那么使用 replace 之后就是这样的:

require (
golang.org/x/text v0.3.2
)
replace golang.org/x/text v0.3.2 => github.com/golang.org/text v0.3.2

执行命令go list -m all 也可以查看当前所有依赖项。

Go modules 有如下常用命令:

download    download modules to local cache (下载依赖的module到本地cache))
edit edit go.mod from tools or scripts (编辑go.mod文件)
graph print module requirement graph (打印模块依赖图))
init initialize new module in current directory (再当前文件夹下初始化一个新的module, 创建go.mod文件))
tidy add missing and remove unused modules (增加丢失的module,去掉未用的module)
vendor make vendored copy of dependencies (将依赖复制到vendor下)
verify verify dependencies have expected content (校验依赖)
why explain why packages or modules are needed (解释为什么需要依赖)

GOPROXY加速

Go 很多包都会被 GFW 墙掉,现在有了Go modules 就可以解决了。

Go modules 支持配置镜像源,从而更快更稳定的完成 go get 操作,大家只需要 export GOPROXY 配置一下即可,推荐的源地址:https://goproxy.io/。

Go 包管理历史以及 Go mod 使用的更多相关文章

  1. python包管理历史

    1.标准库工具distutils,2000年发布,是包安装和发布工具 setup.python 程序,利用distutils 开发 示例: python setup.py install 安装一个包 ...

  2. 包管理神器-pipenv

    一:前言 介绍一个包管理神器-pipenv,这个工具可以让我们在写代码.创建Python运行环境.package依赖关系以及项目合作的时候更有效率. 在pycon2018上,Kenneth Reitz ...

  3. Go包管理go mod使用

    Go Modules介绍 为了解决Go包管理的问题,Go从1.11开始加入了Go Modules这一新特性.让包的依赖和版本管理更加容易. 一个module可以理解为一个单独的包或者模块,module ...

  4. 拜拜了,GOPATH君!新版本Golang的包管理入门教程

    Go 1.11和1.12实现了对包管理的初步支持,Go的新依赖管理系统使依赖版本信息明确且易于管理.Using Go Modules - The Go Blog 新的包管理模式有什么不同? 作为Go语 ...

  5. Golang 包管理机制

    Golang 包管理机制 1. 历史 在go1.11之前, 并没有官方的包管理机制(Godep算个半官方), 主流的包管理机制有: GoVendor Glide Godep 在go1.11之后, 官方 ...

  6. 层次分明井然有条,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang包管理机制(package)EP10

    Go lang使用包(package)这种概念元素来统筹代码,所有代码功能上的可调用性都定义在包这个级别,如果我们需要调用依赖,那就"导包"就行了,无论是内部的还是外部的,使用im ...

  7. Helm包管理

    Helm Kubernetes 包管理工具 Helm 可以帮助我们管理 Kubernetes 应用程序 - Helm Charts 可以定义.安装和升级复杂的 Kubernetes 应用程序,Char ...

  8. Linux程序包管理之rpm

    rpm简介 rpm( Red Hat Package Manager )是一个开放的软件包管理系统.它工作于Red Hat Linux及其他Linux系统,成为Linux中公认的软件包管理标准. rp ...

  9. Linux程序包管理之yum及源代码安装

    第十六章.Linux程序包管理之yum及源代码安装 目录 yum介绍 yum配置文件 yum的repo配置文件中可用的变量 yum命令的使用 使用光盘作为本地yum仓库 如何创建yum仓库 编译安装的 ...

随机推荐

  1. swagger2注解详细说明

    @Api:用在请求的类上,表示对类的说明 tags="说明该类的作用,可以在UI界面上看到的注解" value="该参数没什么意义,在UI界面上也看到,所以不需要配置&q ...

  2. Linux系统编程—进程间同步

    我们知道,线程间同步有多种方式,比如:信号量.互斥量.读写锁,等等.那进程间如何实现同步呢?本文介绍两种方式:互斥量和文件锁. 互斥量mutex 我们已经知道了互斥量可以用于在线程间同步,但实际上,互 ...

  3. Qt 展示pdf内容(新窗口或嵌入,pdfjs,linux)

    前言:初学Qt,在网上查找了诸多资料,有什么poppler.mupdf啊巴拉巴拉的,结果一个比一个费劲,最后还是采用pdfjs较为方便高效,为方便相关问题搜索,写了一下内容. 需求描述:Qt应用中不支 ...

  4. 利用python简单实现unittest

    python3的eval方法 eval() 函数用来执行一个字符串表达式,并返回表达式的值 # 例如 a = [1,2,3,4] b = "a" print(eval(b)) # ...

  5. 每天一个dos命令-net.

    Rem:关于net命令相关的常用实例(如果cmd中执行net相关命令,报错:Access is denied. 可以右键cmd,以管理员身份运行即可!) 1.创建一个新账号:net user ifsf ...

  6. Java11-ZGC

    Java 11包含一个全新的垃圾收集器--ZGC,它由Oracle开发,承诺在数TB的堆上具有非常低的暂停时间. 在本文中,我们将介绍开发新GC的动机,技术概述以及由ZGC开启的一些可能性. 那么为什 ...

  7. Urule开源版系列5——RuleSetParser解析过程

    接上期Urule开源版系列4--Core包核心接口之规则解析过程 之前源码到了Parser,这期详细解析下RuleSetParser的解析过程 1.主流程 特殊处理一个属性 循环处理元素 当元素名称是 ...

  8. 转载:python的format格式化输出

    https://www.cnblogs.com/chunlaipiupiupiu/p/7978669.html python中format函数   ---恢复内容开始--- python中format ...

  9. 进程管理、PS命令、nohup命令

    1. Windows 下,扩展名为exe的文件,鼠标双击,运行,把这个程序正在运行的实例,称之为进程 Windows进程的信息可以通过 任务管理器看到 查看到:正在运行的计算器程序 Calculato ...

  10. java内存屏障

    为什么会有内存屏障 每个CPU都会有自己的缓存(有的甚至L1,L2,L3),缓存的目的就是为了提高性能,避免每次都要向内存取.但是这样的弊端也很明显:不能实时的和内存发生信息交换,分在不同CPU执行的 ...