摘自:

http://blueskykong.com/2019/02/18/go-dep-1/

https://www.cnblogs.com/apocelipes/p/10295096.html#vcs-semver

GOPATH和GOROOT

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

GOPATH必须要设置,GOPATH告知go,需要代码(包括本项目即内部依赖和引用外部项目的代码即外部依赖)的时候去哪里查找。GOPATH可以随着项目的不同而重新设置。

GOPATH下会有3个目录:src、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下的代码不会跟着升级

govendor

  • 该工具将项目依赖的外部包拷贝到项目下的 vendor 目录下,并通过 vendor.json 文件来记录依赖包的版本,方便用户使用相对稳定的依赖。

  • 使用步骤

    #安装
    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".
  • govendor还可以直接指定依赖包版本来获取包

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 compiler 会始终使用 GOPATH mode,即无论要构建的源码目录是否在 GOPATH 路径下,go compiler 都会在传统的 GOPATH 和 vendor 目录 (仅支持在 GOPATH 目录下的 package) 下搜索目标程序依赖的 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 buildgo installgo rungo test 之类的命令时,将启动相应的操作,如缓存,创建或更新 go.modgo.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。

  • 版本格式:主版本号.次版本号.修订号,版本号递增规则如下:

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

go包管理的更多相关文章

  1. ASP.NET Core 静态文件及JS包管理器(npm, Bower)的使用

    在 ASP.NET Core 中添加静态文件 虽然ASP.NET主要大都做着后端的事情,但前端的一些静态文件也是很重要的.在ASP.NET Core中要启用静态文件,需要Microsoft.AspNe ...

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

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

  3. Node.js包管理器Yarn的入门介绍与安装

    FAST, RELIABLE, AND SECURE DEPENDENCY MANAGEMENT. 就在昨天, Facebook 发布了新的 node.js 包管理器 Yarn 用以替代 npm .咱 ...

  4. Python黑帽编程1.3 Python运行时与包管理工具

    Python黑帽编程1.3  Python运行时与包管理工具 0.1  本系列教程说明 本系列教程,采用的大纲母本为<Understanding Network Hacks Attack and ...

  5. 你需要知道的包管理器(Package Manager)

    最近我花了一点时间关注了在不同系统之中所用到的包管理器(Package Manager) .最开始的时候,我是在使用Linux操作系统时,对这种工具以及它背后的想法深深迷恋住了:这真是自由的软件世界. ...

  6. 包管理工具Carthage使用

    iOS项目中第三方开源库的工具有Cocoapods和Carthage,swift官方出了一个包管理工具SPM(Swift Package Manager). 首先,大体讲一下Cocoapods和Car ...

  7. RPM包管理命令rpm

    RPM包管理器是Linux系统中最早的软件包管理软件之一,也是目前最流行的软件包管理器,许多发行版都使用RPM作为默认的软件包管理软件. RPM软件包名称格式: samba-client-3.0.33 ...

  8. Node包管理工具

    Node包管理工具 只是简单的介绍一些工具的使用,有利于开发过程.除了介绍Node包管理工具,还介绍了前端打包工具,前端模块管理工具 Node包管理工具:    --npm    --cnpm    ...

  9. godep 包管理工具

    godep是解决包依赖的管理工具 安装 go get github.com/tools/godep 成功安装后,在GOPATH的bin目录下会有一个godep可执行的二进制文件,后面执行的命令都是用这 ...

  10. pycharm快捷键、常用设置、包管理

    pycharm快捷键.常用设置.包管理 在PyCharm安装目录 /opt/pycharm-3.4.1/help目录下可以找到ReferenceCard.pdf快捷键英文版说明 or 打开pychar ...

随机推荐

  1. 小程序组件--> 组件传参

    小程序组件,在components文件夹右击-->创建文件夹-->右击-->新建component即可 创建一个组件 如果多个地方需要使用到,可以在app.json中加入一下代码,相 ...

  2. docker安装 与 基本配置

    1.安装docker #yum remove docker \ docker-common \ container-selinux \ docker-selinux \ docker-engine \ ...

  3. ASP.NET Core 的 Dependency Injection

    ASP.NET Core使用了大量的DI(Dependency Injection)设计,有用过Autofac或类似的DI Framework对此应该不陌生.本篇将介绍ASP.NET Core的依赖注 ...

  4. C++内联函数(C++ inline)详解

    使用函数能够避免将相同代码重写多次的麻烦,还能减少可执行程序的体积,但也会带来程序运行时间上的开销. 函数调用在执行时,首先要在栈中为形参和局部变量分配存储空间,然后还要将实参的值复制给形参,接下来还 ...

  5. mysql 导出查询结果

    show variables like '%secure%'; 看看导出位置 SELECT * FROM tb WHERE sn = '1' LIMIT 1,10into outfile '/var/ ...

  6. 强烈建议为你的Android项目加上 largeHeap 属性

    小内存机器使用“微信”时,看视频经常崩溃(out of memory) ,小内存机器有时候明明内存还很多,却还是抛出“内存不够”,应该就是每个APP能用“堆大小”的限制. 如上图,Android项目的 ...

  7. linux下使用clamav排查病毒

    clamav wget http://www.clamav.net/downloads/production/clamav-0.102.0.tar.gz ### Installyum -y insta ...

  8. 安全漏洞XSS、CSRF、SQL注入以及DDOS攻击

    随着互联网的普及,网络安全变得越来越重要,程序员需要掌握最基本的web安全防范,下面列举一些常见的安全漏洞和对应的防御措施. 0x01: XSS漏洞 1.XSS简介 跨站脚本(cross site s ...

  9. linux删除文件的前n行

    需求描述: 今天看了一个系统的临时文件,有5.6G的大小,这个文件也没有用了,想要将大部分的文件都删除掉. 在此记录下删除的过程.删除前n行的记录. 操作过程: 对于数据量比较大的情况(本例5800万 ...

  10. kotlin基础 密封类

    密封类 密封类用来表示受限的类继承结构:当一个值为有限几种的类型, 而不能有任何其他类型时.在某种意义上,他们是枚举类的扩展:枚举类型的值集合 也是受限的,但每个枚举常量只存在一个实例,而密封类 的一 ...