之前也写过 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. MySQL中concat()、concat_ws()、group_concat()函数的使用技巧与心得总结

    Author:极客小俊 一个专注于web技术的80后 我不用拼过聪明人,我只需要拼过那些懒人 我就一定会超越大部分人! CSDN@极客小俊,原创文章, B站技术分享 B站视频 : Bilibili.c ...

  2. spring:Beanfactory和ApplicationContext、BeanFactory 和 FactoryBean

    1.Beanfactory和ApplicationContext有什么区别 ApplicationContext (1)在配置文件加载后创建bean 利用debug方式,在Student类的无参构造方 ...

  3. Python-读写csv数据模块 csv

    案例: 通过股票网站,我们获取了中国股市数据集,它以csv数据格式存储 Data,Open,High,Low,Close,Volume,Adj Close 2016-06-28,8.63,8.47,8 ...

  4. Python练习题 049:Project Euler 022:姓名分值

    本题来自 Project Euler 第22题:https://projecteuler.net/problem=22 ''' Project Euler: Problem 22: Names sco ...

  5. 走进shiro,构建安全的应用程序---shiro修仙序章

    0. 写在前面 在最近的一个项目当中,我们基于Shiro实现我们系统的认证和授权.借此机会,写几篇博客,总结一下在本次实践当中遇到的问题,和较全面地学习一下Shiro的知识点, 1. 权限管理 权限管 ...

  6. 《VC++ 深入详解》 第3版 这是盗版书么~。。。

    <VC++ 深入详解> 第3版 www.broadview.com.cn 书读到一小半,发现书重复了一部分,缺失一部分.... 难受~ 比较难继续下去了 有一样的小伙伴么~ <VC+ ...

  7. P5091 【模板】扩展欧拉定理

    题目链接 昨天考试考到了欧拉公式,结果发现自己不会,就来恶补一下. 欧拉公式 \(a^b \bmod p = a^{b}\) \(b < \varphi(p)\) \(a^b \bmod p = ...

  8. AD15使用笔记

    AD15使用笔记 1.板内孔开洞 步骤:选中图形->Tools->Convert->Creat Borad Cutout From Selected Primitives;

  9. Go语言中的常见的几个坑

    目录 1.for range 2.defer与闭包 3.map内存溢出 4.协程泄漏 5.http手动关闭 记录一下日常中遇到的几个坑,加深一下印象. 1.for range 这个是比较常见的问题了, ...

  10. bash 在指定目录查找包含特定关键字的文件

    比如我们要在目录/usr/local/nginx/conf/vhost/下查找baidu.com这个关键字的文件 方法1: find /usr/local/nginx/conf/vhost/ -exe ...