Go语言版本控制及包依赖管理

传统Go构建以及包依赖管理

image.png
  • 我们知道go get获取的代码会放在GOROOT/src下面,而go build会在GOROOT/src和GOPATH/src下面按照import path去搜索package,由于go get 获取的都是各个package repo的trunk/mainline的代码,因此,Go 1.5之前的Go compiler都是基于目标Go程序依赖包的trunk/mainline代码去编译的。这样的机制带来的问题是显而易见的,至少包括:
  1. 因依赖包的trunk的变化,导致不同人获取和编译你的包/程序时得到的结果实质是不同的,即不能实现reproduceable build
  2. 因依赖包的trunk的变化,引入不兼容的实现,导致你的包/程序无法通过编译
  3. 因依赖包演进而无法通过编译,导致你的包/程序无法通过编译
  • 为了实现reporduceable build,Go 1.5引入了Vendor机制,Go编译器会优先在vendor下搜索依赖的第三方包,这样如果开发者将特定版本的依赖包存放在vendor下面并提交到code repo,那么所有人理论上都会得到同样的编译结果,从而实现reporduceable build

Go语言版本控制

  • 在没有版本控制时,go get 的一个重大缺陷是对于给定的更新无法知道是否是用户所期望的。

  • Go 将在下个版本(1.11)中看到官方的包版本控制,去除了 GOPATH 依赖,同时还引入了 module(模块) 的概念。

  • 版本控制可以让我们能够实现重编译。当我让你试用我程序最新版本时,我清楚的知道你不仅仅获取到的是我最新程序的代码,还包括我代码所依赖的相同版本的包,这样才能编译出完全一样的二进制包。

  • 版本控制还能让我们不同阶段保持同样的编译方式,即使我们的依赖包可能有新版本了,只要我们的配置未允许使用,go 命令也不会使用新版本的包。

  • 尽管添加版本控制是必须的功能,但同时也不能失去go 命令行现有的优秀特性:简单、高效、易懂,所以它应该足够透明不能破坏掉go get 本身功能。

  • Go新版本中保留了go get的精华部分,增加了重复构建,采用了语义化的版本控制,弃用了 vendor,废弃了基础工程创建时依赖GOPATH,并且提供了老项目平滑迁移的方式。

Go添加版本控制共分四个步骤

导入兼容规则

  • 包管理系统中最大的痛苦在于解决兼容性问题

    比如,大多数系统中包B 声明需要的包D 版本是6或者更高版本,然后包C声明所需的包D 版本是2,3和4,但不能高于版本5。如果你正在编写包A ,你想同时引入包B 和C ,那么你不走运了:没有一个独立的D 版本可以供B 和C 同时选择编译进A。B 和C 做的都是合理的,你也没办法改变它,所以你就被卡住了。

  • 为了避免主导者设计一个导致现有的大型程序无法编译的系统,提案要求包作者遵循以下导入兼容性原则:

    如果一个旧包和新包有相同的导入路径,新包必须向后兼容旧包 这条规则是对前面 Go FAQ 的重申,引用 FAQ 中最后讲的:“如果需要完全变更,那么就创建个新导入路径的包”。开发者希望能通过语义化的版本来表达这样一个变更,因此我们把语义化版本控制也加入到我们提案中。具体点说,主版本2 和更新的版本可以通过在路径中包含版本信息来区分,比如:


import "github.com/go-yaml/yaml/v2"

  • 包作者遵循导入兼容性原则可以让我们减少适配工作,让系统更简单的同时也让包生态减少碎片化
    当然,实际上尽管作者尽最大努力去做了,更新时也难免会出现破坏用户使用的情况。因此,使用一个不频繁升级的升级机制很重要,这也是接下来我们要讲的。

最小版本规则

  • 几乎现在所有的包管理包括dep和cargo都在构建时使用最新的包版本,基于两方面的重要因素,被认为这是个错误的约定:

    首先,“最新可用版本”有可能因为外部事件导致变更,像新版本发布。也许今晚你依赖的包中有人会发布个新版本,第二天早上你再编译有可能就产生不同的结果了;

    第二,为了覆盖这个默认约定,开发者花费大量的时间告诉包管理器不使用哪个版本的包。

  • 提案中我们使用了不同的方式,称之为最小版本选择。

    构建时每个包默认使用的是最老的可用版本,这个方式让昨天和今天的编译不会有变化,因为你总不会在今天发布一个更老版本吧。更好的是,开发者只需告诉包管理器最小可用的那个版本,包管理器就可以很快的决定哪个版本可用。我们称它为最小版本选择一方面是因为我们选择的是最小版本,另一方面是因为对整个系统来说是最小化的,避免了现有系统的复杂性。

    最小版本选择为模块指定了其依赖模块的最低版本需求,这为后续升级和降级操作提供了一个很好的选择。同时,它还可以通过排除指定版本的依赖或者指定特殊版本依赖完成编译。

    最小版本选择在不锁定文件情况下默认就完成了可重复构建。

    最小版本选择是导入兼容的关键。用户不会再说:“不,版本太新了”,更多情况是面临“不,版本太旧了”,这种情况下解决方案很明确:升级新版本就可以了。

Go Module

  • Go Module是共享一个导入路径前缀的包集合,也就是我们所说的模块路径

    Module是版本控制的单元,Module的版本通过语义化的版本字符串表示,当开发中使用Git 时,开发者通过给模块的Git 资源库添加一个新tag的方式来定义一个新的语义化版本。尽管强烈推荐使用语义化版本的方式,但也支持指向特定commit。

  • 模块定义在一个叫go.mod的新文件里,里面包含了模块所依赖包的最小版本

    下面就是个简单的go.mod文件:


module "rsc.io/hello"

require (

"golang.org/x/text" v0.0.0-20180208041248-4e4a3210bb54

"rsc.io/quote" v1.5.2

)

这个文件通过路径标识 rsc.io/hello 定义了一个模块,它本身还依赖于两个其他模块:golang.org/x/text 和 rsc.io/quote ,这个模块自身编译的时候使用的是 go.mod 文件中指定的依赖列表的版本。对于更上一层的编译,其他导入这个模块的地方将使用它较新的版本编译。

包发布者最好使用语义化的 tag 发布版本,vgo 也鼓励通过打tag的版本号方式,而不是任意的提交版本。

  • 除了指定必须的依赖版本,go.mod 文件还可以实现前面章节中提到的排除和替换的版本,但是这些只有当直接编译该模块的时候起作用,在模块作为整体工程一部分编译时就不行了

  • Goinstall 和旧的 go get 通过像git 和hg 这样的版本控制工具直接下载代码,这种方式存在很多问题,其中包括碎片化严重:用户如果没有bzr 就没法下载托管在Bazaar 资源库的代码。相比之下,Go Module则是通过HTTP 下载zip 包的方式。

  • Module统一通过zip包的形式提供可以让下载协议更简单,公司或者个人可以处于任何原因考虑(安全或者想要缓存副本防止源被删除)自己做下载代理,使用代理来确保可用性并且通过go.mod定义了哪些代码需要用到

Go 命令

  • go 命令必须更新才能使用模块功能。一个重要的变化就是常用的构建命令,像 gobuild, go install, go run, 和 go test 将需要按指定需求解析对应的依赖关系了

  • 最重要的变化还是终结了GOPATH作为Go 代码工作空间的设置,由于go.mod文件包含了完整的模块路径并且还定义了每个使用的依赖的版本,因此包含go.mod文件的目录就可以被认为是一个目录树的根目录了,该目录树作用于自身的工作空间,并且和其他类似的目录彼此隔离。现在你只需git clone然后cd就可以直接撸代码了,不再需要GOPATH了

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容