go包管理发展历史

下面是官方的包管理工具的发展历史:

  • 在 1.5 版本之前,所有的依赖包都是存放在 GOPATH 下,没有版本控制。这个类似 Google 使用单一仓库来管理代码的方式。这种方式的最大的弊端就是无法实现包的多版本控制,比如项目 A 和项目 B 依赖于不同版本的 package,如果 package 没有做到完全的向前兼容,往往会导致一些问题。
  • 1.5 版本推出了 vendor 机制。所谓 vendor 机制,就是每个项目的根目录下可以有一个 vendor 目录,里面存放了该项目的依赖的 package。go build 的时候会先去 vendor 目录查找依赖,如果没有找到会再去 GOPATH 目录下查找。
  • 1.11 版本推出 modules 机制,简称 mod。go module 是go包的集合,是源代码交换和版本化控制的基本单元

GOPATH和GOROOT

GOROOT的目的就是告知go当前的安装位置,默认go会安装在/usr/local/go下,但也允许自定义安装位置(通过 export GOROOT=$HOME/go1.9.3指定)。编译的时候从GOROOT去找SDK的system libariry

GOPATH必须要设置,GOPATH告知go,需要代码(包括本项目即内部依赖和引用外部项目的代码即外部依赖)的时候去哪里找。GOPATH随着项目的不同重新设置
GOPAT有三个目录:sec bin pkg

  • src目录:go编译时查找代码的地方;按照golang默认约定,go run , go install 等命令的当前工作路径(即在此路径下执行上述命令)
  • bin目录:go get 这种bin工具的时候,二进制文件下载的目的地;golang 编译可执行文件存放路径
  • pkg目录:golang编译包时,生成的.a文件存放路径。.a文件是编译过程中生成的,每个package都会生成对应的.a文件,Go在编译的时候先判断package的源码是否有改动,如果没有的话,就不再重新编译.a文件。
内部依赖管理

编译时会去$GOPATH/src/目录去查找需要的代码

GOPATH来管理外部依赖

go允许import不同代码库的代码,例如github.com, k8s.io, golang.org等等;对于需要import的代码,可以使用 go get 命令取下来放到GOPATH对应的目录中去。例如go get github.com/globalsign/mgo(下载和创建项目要保持目录一致,这样才能保证能够正确的引用外部导入和内部导入),会下载到$GOPATH/src/github.com/globalsign/mgo中去,当其他项目在import github.com/globalsign/mgo的时候也就能找到对应的代码了。

对于go来说,其实并不在意你的代码是内部还是外部的,总之都在GOPATH里,任何import包的路径都是从GOPATH开始的。Go 语言原生包管理的缺陷:

依赖 列表/关系 无法持久化到本地,需要找出所有依赖包然后一个个 go get
只能依赖本地全局仓库(GOPATH/GOROOT),无法将库放置于局部仓库($PROJECT_HOME/vendor)

vendor

vendor就是让go编译时,优先从项目源码树根目录下的vendor目录查找代码,如果vendor中有,则不再去GOPATH中去查找。

以kube-keepalived-vip为例。该项目会调用k8s.io/kubernetes的库(Client),但如果你用1.5版本的kubernetes代码来编译keepalived,会编译不过。1.5版本中代码有变化,已经没有这个Client了。这就是前面说的依赖GOPATH来解决go import所带来的问题,代码不对上了。

使用vendor目录可以把所有依赖的包都拷贝到了vendor目录下,对于需要编译该项目的人来说,只要把代码从github上clone$GOPATH/src以后,就可以进去go build了(注意,必须将kube-keepalived-vip项目拷贝到$GOPATH/src目录中,否则go会无视vendor目录,仍然去$GOPATH/src中去找依赖包)。

无法精确的引用外部包进行版本控制,不能指定引用某个特定版本的外部包;一旦外部包升级,vendor下的代码不会跟着升级

#安装
go get -u github.com/kardianos/govendor

# 进入项目的根目录
# 创建 vendor 文件夹和 vendor.json 文件,此时文件中只有本项目的信息
govendor init

# 拷贝GOPATH下的代码到vendor目录中,更新vendor.json
govendor add +包名

# 列出已经存在的依赖包
govendor list

# 找出使用的对应包
govendor list -v fmt

# 拉取指定版本的包
govendor fetch golang.org/x/net/context@a4bbce9fcae005b22ae5443f6af064d80a6f5a55
govendor fetch golang.org/x/net/context@v1   # Get latest v1.*.* tag or branch.
govendor fetch golang.org/x/net/context@=v1  # Get the tag or branch named "v1".

在这里插入图片描述

Modules

go module 是go包的集合,是源代码交换和版本化控制的基本单元

「模块根目录」 ( Module root ) : 包含了名为 go.mod 文件的目录,可以存放于文件系统上的任何位置,而不用管
GOPATH 路径到底是什么 「模块路径」 ( Module path ) : 与模块根目录对应的导入路径的前缀 「主模块」( Main
module ) : 包行了运行 go 命令的所在目录的模块

GO111MODULE

要使用go module,首先要设置GO111MODULE=on

如果设置为on ,那么无论模块在于何种路径,都会启用模块支持,始终使用module-aware mode 。首先会在$GOPATH/pkg/mod中查找有没有所需要的的依赖,没有直接会下载,完全忽略GOPATH以及vendor

目录设置为off,禁用 go module功能。go compier(编译器)会始终使用GOPATH mode,即无论要构建的原码是否在GOPATH 路径下,go complier 都会在传统的GOPATHvendor目录下收搜目标程序依赖的go package ;如果一个包在vendor$GOPATH下都在,查找依赖的顺序:
1.优先使用vendor目录下面的包
2.如果vendor下面没有搜索到,再搜索$GOPATH/src
3.如果$GOPATH下面没有搜索到,那么搜索$GOROOT/src下面的包

如果没有设置,或设置为 auto,满足以下任一条件时才使用 module-aware mode:
当前目录位于 GOPATH/src 之外并且包含 go.mod 文件
当前目录位于包含 go.mod 文件的目录下

既有项目

假设你已经有了一个go 项目, 比如在$GOPATH/github.com/smallnest/rpcx下, 你可以使用go mod
init github.com/smallnest/rpcx在这个文件夹下(即模块根目录)创建一个空的go.mod (只有第一行
module github.com/smallnest/rpcx即模块导入名称)。

下面是一个简化的go.mod的内容

module my/thing //模块名称

require (//要求的依赖项列表以及版本
        one/thing v1.3.2
        other/thing v2.5.0 // indirect
        ...
)

exclude (//排除的依赖项,仅在当前模块为主模块时生效
        bad/thing v0.7.3
)

replace (//替换的依赖项,仅在当前模块为主模块时生效
        src/thing 1.0.2 => dst/thing v1.1.0
)

go get ./..查找依赖,并记录在go.mod 文件中(你可以指定-tags,这样可以把tags的依赖都查找到。执行上面的命令会把go.modlatest版本换成实际的最新版本,并且会生成一个go.sum记录每个依赖库的版本和哈希值,用于验证缓存的依赖项是否满足模块要求

新的项目

你可以在GOPATH之外创建新的项目。
go mod init packagename可以创建一个空的go.mod,然后你可以在其中增加require github.com/smallnest/rpcx latest依赖,或者像上面一样让go自动发现和维护。
go mod download可以下载所需要的依赖,但是依赖并不是下载到$GOPATH中,而是$GOPATH/pkg/mod中,用来当做缓存,多个项目可以共享缓存的module。

go mod命令

//download    下载依赖的module到本地cache
//edit        edit go.mod from tools or scripts (编辑go.mod文件)
例如 go mod edit -require="github.com/chromedp/chromedp@v0.1.0",修改依赖关系使用chromedp 的v0.1.0版本
//tidy 增加丢失的module,去掉未用的module,如在项目的开发过程中, 依赖有变更, 可使用 go mod tidy 来应用这些变更到 go.mod 文件.
//graph       print module requirement graph (打印模块依赖图))
//init        在当前文件夹下初始化一个新的module, 创建go.mod文件
//vendor  在项目发布时会要将依赖复制到项目中,会复制modules下载到vendor中, 貌似只会下载你代码中引用的库,而不是go.mod中定义全部的module。
//verify      verify dependencies have expected content (校验依赖)
//why         explain why packages or modules are needed (解释为什么需要依赖)
//go list -m -json all //依赖详情

集成

go module 功能被集成到 go 命令行工具中,例如,在调用诸如 go build,go install,go run,go test 之类的命令时,将启动相应的操作,如缓存,创建或更新 go.mod 和 go.sum 等

go get 升级

运行 go get -u 将会升级到最新的次要版本或者修订版本
运行 go get -u=patch 将会升级到最新的修订版本
运行 go get package@version 将会升级到指定的版本号version

语义化版本

golang官方推荐的最佳实践叫做semver,写全了就是Semantic Versioning(语义化版本)。形如vX.Y.Z的形式显然比一串hash更直观,x.y.z, z是修订版本号, y是次要版本号,如此一来包的导入路径发生了变化,不同的导入路径意味着不同的包,更具体的规范在这里

标记版本号的软件发行后,禁止(MUST NOT)改变该版本软件的内容。任何修改都必须(MUST)以新版本发行。

标准的版本号必须(MUST)采用 X.Y.Z 的格式,其中 X、Y 和 Z 为非负的整数,且禁止(MUST NOT)在数字前方补零。X 是主版本号、Y 是次版本号、而 Z 为修订号。每个元素必须(MUST)以数值来递增。例如:1.9.1 -> 1.10.0 -> 1.11.0。

版本格式:主版本号.次版本号.修订号,版本号递增规则如下:
主版本号:当你做了不兼容的 API 修改。每当主版本号递增时,次版本号和修订号必须(MUST)归零。
次版本号:当你做了向下兼容的功能性新增,每当次版本号递增时,修订号必须(MUST)归零。
如果同时依赖是 v1.5.0 和 v1.10.0,最小的兼容版本是 v1.10.0。
修订号:当你做了向下兼容的问题修正。这里的修正指的是针对不正确结果而进行的内部修改
如果依赖写成 v1.0.5 和 v1.0.10,这两版本是兼容的,则选择最小版本依赖 v1.0.5

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值