欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

golang包管理的古往今来

程序员文章站 2022-05-31 19:54:28
https://golang.org/ before GO1.5-GOPATH 在GO1.5之前用GOPATH以及GOROOT这两个环境变量来决定包的位置。 GOROOT就是告知当前go的安装位置,编译的时候从GOROOT去找sdk的system library。 GOPATH的目的是为了告知go, ......

golang包管理的古往今来

before go1.5-gopath

在go1.5之前用gopath以及goroot这两个环境变量来决定包的位置。

goroot就是告知当前go的安装位置,编译的时候从goroot去找sdk的system library。

gopath的目的是为了告知go,需要代码的时候,去哪里查找。注意这里的代码,包括本项目和引用外部项目的代码。gopath可以随着项目的不同而重新设置。 从go 1.8开始,如果gopath没有被设置,会有一个默认值,在unix上为$home/go,在windows上为%userprofile%/go

gopath下会有3个目录:src, bin, pkg。

  • src目录:go编译时查找代码的地方

  • bin目录:go get、godep这种bin工具的时候,二进制文件下载的目的地

  • pkg目录:编译生成的lib文件存储的地方。

go允许import不同代码库的代码,例如github.com, k8s.io, golang.org等等;对于需要import的代码,可以使用 go get 命令取下来放到gopath对应的目录中去。例如go get github.com/silenceshell/hcache,会下载到$gopath/src/github.com/silenceshell/hcache中去,当其他项目在import github.com/silenceshell/hcache的时候也就能找到对应的代码了。

看到这里也就明白了,对于go来说,其实并不care你的代码是内部还是外部的,总之都在gopath里,任何import包的路径都是从gopath开始的;唯一的区别,就是内部依赖的包是开发者自己写的,外部依赖的包是go get下来的。

存在问题:

  • 不能区分版本,以至于令开发者以最后一项包名作为版本划分

  • 依赖 列表/关系 无法持久化到本地,需要找出所有依赖包然后一个个 go get

  • 只能依赖本地全局仓库($gopath/goroot),无法将库放置于局部仓库($project_home/vendor)

go1.5-vendor

go1.5使用vendor需要手动设置环境变量 go15vendorexperiment=1 ,go1.6该环境变量默认设为1,在1.7版本时,已去掉该环境变量,默认开启vendor特性。

项目必须在$gopath/src目录下

在项目的目录下增加一个 vendor 目录来存放外部的包 , 在这种模式下,会将第三方依赖的源码下载到本地,不同项目下可以有自己不同的vendor。但是这样做又引入了新的问题,因为随着项目的依赖增多,代码库会越来越大,尤其很多带前端资源的项目,几十m到几百m都是很常见的,你的项目可以就依赖它某一点内容,但是却集成了全部资源。

go vendor的管理工具

  • godep

  • govendor

  • glide

go官方wiki给了一个比较全面的对比

godep更直观,使用者也多一些,一些个人的小项目可以用;glide功能更丰富,更接近maven(例如glide.lock跟maven的pom.xml比较类似,可以指定获取某一个版本),新的项目可以考虑使用glide。

go1.11-go modules

基于vgo演变而来,是golang官方的包管理工具。

configure

目前modules并不是默认启用的,可以通过环境变量go111module开启或者关闭,它有三个可选值:off、on、auto,默认值是 auto。

  1. off。关闭支持,go 会从gopath和vendor文件夹寻找包。

  2. on。开启支持,go 会忽略 gopath 和 vendor 文件夹,只根据go.mod下载依赖。

  3. auto。当项目在$gopath/src,且项目根目录有go.mod文件时,开启模块支持,否则依旧使用gopath以及vendor。

go modules使用:

add dependencies

go mod init example.com/hello

go mod命令生成一个go.mod文件,go.mod文件出现在模块的根目录中。 子目录中的程序包具有由模块路径以及子目录路径组成的导入路径。 例如,如果我们创建了一个子目录world,则无需(也不希望)在那里运行go mod init。 该软件包将自动被识别为example.com/hello模块的一部分,导入路径为example.com/hello/world

go命令使用go.mod中列出的特定依赖项模块版本来解析导入。 当遇到未由go.mod中的任何模块提供的包的导入时,go命令将自动使用最新版本查找包含该软件包的模块,并将其添加到go.mod中。 (“最新”定义为最新的标记稳定(非预发行)版本,或者最新的标记预发行版本,或者最新的未标记版本。)在我们的示例中,go test将新的导入rsc.io/quote解析为 rsc.io/quote v1.5.2模块 它还下载了rsc.io/quote使用的两个依赖项,即rsc.io/samplergolang.org/x/text仅直接依赖项记录在go.mod文件中。

go modules 下载的包在 gopath/pkg/mod,这就是前面所说的 ‘maven way’;安装的命令仍在 $gopath/bin

除了go.mod之外,go命令还会维护一个名为go.sum的文件,其中包含特定模块版本内容的预期 cryptographic hashes

go命令使用go.sum文件来确保这些模块的将来下载与第一次下载时检索的位相同,以确保您的项目所依赖的模块不会由于恶意,意外或其他原因而意外更改。 应该将go.mod和go.sum都检查到版本控制中。

go list -m all  # 列出当前的所有模块以及依赖

update dependencies

版本的表示使用语义版本标签,包括三部分:major;minor;patch。例如v0.2.1,major就是0,minor就是2,patch就是1.

  对于minor级别上的升级: 

go get golang.org/x/text  # 自动下载最新版本

  可能会通过,可能出现版本不兼容,

go list -m -versions rsc.io/sampler # 列出此模块可获得的标签版本
go get rsc.io/sampler@v1.3.1 # 下载相应的版本,注意用@再加版本信息,不加版本信息默认@latest

  对于major级别上的升级:

  go模块的每个不同的主要版本(v1,v2等)都使用不同的模块路径:从v2开始,该路径必须以主要版本结尾。在示例中,rsc.io/quote的v3不再是rsc.io/quote:而是由模块路径rsc.io/quote/v3标识。此约定称为语义导入版本控制,它为不兼容的程序包(具有不同主要版本的程序包)提供了不同的名称。相反,rsc.io/quote的v1.6.0应该与v1.5.2向后兼容,因此它重用了名称rsc.io/quote。 (在上一节中,rsc.io/sampler v1.99.99应该已经与rsc.io/sampler v1.3.0向后兼容,但是关于模块行为的错误或错误的客户端假设都可能发生。)

  go命令允许构建最多包含任何特定模块路径的一个版本,即每个major版本至多:一个rsc.io/quote,一个rsc.io/quote/v2,一个rsc.io/quote/ v3,依此类推。这为模块作者提供了关于可能重复单个模块路径的明确规则:程序无法同时使用rsc.io/quote v1.5.2和rsc.io/quote v1.6.0来构建。同时,允许模块的不同major版本(因为它们具有不同的路径)使模块使用者可以逐步升级到新的主要版本。在此示例中,我们想使用rsc / quote / v3 v3.1.0中的quote.concurrency,但尚未准备好迁移rsc.io/quote v1.5.2的使用。在大型程序或代码库中,增量迁移的能力尤其重要。

remove unused dependencies

尽管有的模块我们可能不使用了,但是go list -m all还是会显示,以及在go.mod中也会有,不会自动删除。仅在检查模块中的所有软件包以及这些包的所有可能的构建标记组合之后,才能删除依赖项。 普通的build命令不会加载此信息,因此它不能安全地删除依赖项。

go mod tidy # 清除未使用的模块

 

参考内容: