Golang 包管理机制

1. 历史

在go1.11之前, 并没有官方的包管理机制(Godep算个半官方), 主流的包管理机制有:

  • GoVendor
  • Glide
  • Godep

在go1.11之后, 官方推出了GoModule作为正统的包管理机制, 但在1.13版本之前默认没有开启, 需要使用GO111MODULE=on参数进行开启.

2. 基础概念

在 Go 语言中,我们通过 go get 命令将 GitHub 或者 Google Code 上的代码下载到本地指定目录,然后在开发代码中通过 import 的形式引用本地的代码。例如:

import "github.com/json-iterator/go"

Go 语言可以通过直接分析代码中的 import 语句来查询依赖关系。go get 命令在执行时,就会自动解析 import 来安装所有的依赖。

下载的包依赖在本地存储涉及到了Go语言的workplace的概念:

通过 GOPATH 环境变量来设置 Go 代码的位置, 包含三个文件夹: src, pkg, bin:

  • src: 包含工程所有的源码
  • pkg: 包含编译生成的package目标文件
  • bin: 包含最后生成的可执行文件

这种原始的方式存在一些不足之处:

  1. 不能很方便地隔离不同项目的环境
  2. 由于墙的存在, 很多依赖很难下载
  3. 不能很方便地控制某个依赖包的版本
  4. 不能管理 Go 本身的版本

这些问题常常导致一些很致命的问题, 所以为了解决这个问题, 出现了包管理机制( go1.5版本推出了vendor, go1.9版本推出了dep, go1.11版本推出了module, 实在是令人头秃 ). 随着Go版本的迭代, 包管理机制也越来越多, 这也导致了一个问题: 不同的项目用了不通的包管理机制, 非常麻烦.

3. 使用

这里主要介绍govendor, godep以及gomodule三种方式. 大多数的包管理工具都是通过一个文件描述依赖的坐标信息, 然后批量管理(下载, 升级等)依赖.

3.1. govendor

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

在Golang1.5之后, Go提供了 GO15VENDOREXPERIMENT 环境变量(Go 1.6版本默认开启该环境变量), 用于将go build时的应用路径搜索调整成为 当前项目目录/vendor 目录方式.

  • govendor的几种状态

    状态 缩写 含义
    +local l 本地包,即项目自身的包组织
    +external e 外部包, 被GOPATH管理
    +vendor v 被govendor 管理
    +std s 标准库
    +unused u 未使用的包
    +missing m 代码引用了依赖包,但是该包找到
    +program p 主程序包,意味着可以编译为执行文件
    +outside 外部包和缺失的包
    +all 所有包

3.1.1. 安装

go get -u -v github.com/kardianos/govendor

完成后可使用govendor查看govendor的相关信息

3.1.2. 说明

govendor只是用来管理项目的依赖包, 如果GOPATH中本身没有项目的依赖包, 则需要通过go get先下载到GOPATH中, 再通过govendor add +external拷贝到vendor目录中. Go 1.6以上版本默认开启GO15VENDOREXPERIMENT环境变量.

3.1.3. 相关命令

init     创建 vendor 文件夹和 vendor.json 文件
list 列出已经存在的依赖包
add 从 $GOPATH 中添加依赖包,会加到 vendor.json
update 从 $GOPATH 升级依赖包
remove 从 vendor 文件夹删除依赖
status 列出本地丢失的、过期的和修改的package
fetch 从远端库增加新的,或者更新 vendor 文件中的依赖包
sync Pull packages into vendor folder from remote repository with revisions
migrate Move packages from a legacy tool to the vendor folder with metadata.
get 类似 go get,但是会把依赖包拷贝到 vendor 目录
license List discovered licenses for the given status or import paths.
shell Run a "shell" to make multiple sub-commands more efficient for large projects. go tool commands that are wrapped:
`+<status>` package selection may be used with them
fmt, build, install, clean, test, vet, generate, to

e.g.

# Setup your project.
cd "my project in GOPATH"
# 初始化 vendor 目录, project 下出现 vendor 目录
govendor init
# 从$GOPATH中添加依赖到vendor下
govendor add +external
# 列出已经存在的依赖包
govendor list
# Look at what is using a package
govendor list -v fmt
# 从远程添加, 更新依赖
govendor fetch golang.org/x/net/context@a4bbce9fcae005b22ae5443f6af064d80a6f5a55
# 格式化本地依赖文件
govendor fmt +local
# Build everything in your repository only
govendor install +local
# Test your repository only
govendor test +local

3.2. godep

Dep其实还有两个版本, 他们的作者是同一个人, 但dep是官方版本, godep是第三方工具.

github地址:

godep: https://github.com/tools/godep

dep: https://github.com/golang/dep

相比非官方的godep, 官方的dep的兼容性更好一些, 是官方出品, 可以有更长久的更新维护.

3.2.1. 安装

curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh

完成后使用dep version可以查看DEP版本即安装成功

3.2.2. 说明

当你使用dep管理依赖时, 会在目录下产生以下几个文件:

├── Gopkg.lock
├── Gopkg.toml
└── vendor
  • Gopkg.lock 是生成的文件,不要手工修改 .

  • Gopkg.toml 是依赖管理的核心文件,可以生成也可以手动修改.

    一般情况下Gopkg.toml里面只定义直接依赖项, 而Gopkg.lock里面除了包含Gopkg.toml中的所有项之外, 还包含传递依赖项. 比如我们的项目依赖项目A, 而项目A又依赖B、C, 那么只有A会包含在Gopkg.toml中, 而A、B、C都会定义在Gopkg.lock中. 所以Gopkg.lock定义了所有依赖的项目的详细信息(commit ID和packages), 使得每次build我们自己的项目时, 始终基于确定不变的依赖项.

  • vendor目录是 go1.5 以后依赖管理目录, 这个目录的依赖代码是优先加载的, 类似 node 的 node_module 目录.

3.2.3. 相关命令

# 初始化
dep init
# 依赖管理帮助
dep help ensure
# 添加一条依赖
dep ensure -add github.com/bitly/go-simplejson
# 这里 @= 参数指定的是 某个 tag
dep ensure -add github.com/bitly/go-simplejson@=0.4.3
# 添加后一定记住执行 确保 同步
dep ensure
# 建议使用
dep ensure -v
# 删除没有用到的 package
dep prune -v
# 依赖更新
dep ensure -update -v

3.3. gomodule

GoModule是官方提供的包管理解决方案. 通过GoModule. 开发者可以把工程放在GOPATH之外的位置. 相比于之前的包管理方案: dep, vendor, GoModule的管理方案更加灵活.

Go1.11版本初步引入的GoModule模块, 1.12版本正式开始支持.

3.3.1. 安装

编辑环境变量, 添加:

export GO111MODULE=on
# 加速下载, 使用代理
export GOPROXY=https://goproxy.io,direct

由于gomodules是官方自带的, 所以只要你的go版本是1.11以上, 就可以直接使用

3.3.2. 相关命令

# 初始化
go mod init
# 下载依赖
go mod download
# 移除未用模块, 添加缺失模块
go mod tidy
# 验证模块正确性
go mod verify
# 将依赖复制到项目的vendor目录下
go mod vendor

4. GoModule的优点

4.1. 使用replace进行本地包替换

使用场景: 有时候一些依赖在墙外, 或者并没有进入go的依赖仓库

解决方法:

例如有cypto, sys这两个依赖被墙了, 在go.mod文件中添加:

replace (
golang.org/x/crypto => github.com/golang/crypto latest
golang.org/x/sys => github.com/golang/sys latest
)

例如log, logfmt这两个依赖在网上无法找到, 在go.mod文件中添加:

replace (
github.com/qiniu/log => /Users/user/go/src/github.com/yis/test/vendor/github.com/qiniu/log
github.com/go-logfmt/logfmt => /Users/user/go/src/github.com/yis/test/vendor/github.com/go-logfmt/logfmt
)

注意点: 顶层依赖可替换但间接依赖不可替换

4.2. 语义化版本 semver—Semantic Versioning

semver是官方为了类库升级引入的新规范,即:

“If an old package and a new package have the same import path, the new package must be backwards compatible with the old package.” - go modules wiki "

如果旧软件包和新软件包具有相同的导入路径,则新软件包必须向后兼容旧软件包。"

main.go

package main
import (
"fmt"
v1 "github.com/e421083458/gomodtest_base"
v2 "github.com/e421083458/gomodtest_base/v2"
)
func main(){
v2.NewIntCollection("hello","sex")
v1.NewIntCollection("hello")
fmt.Println("hello");
}

go.mod

module new_module_test

require (
github.com/e421083458/gomodtest_base v1.0.1
github.com/e421083458/gomodtest_base/v2 v2.0.0
)

4.3. 依赖包冲突问题

这边存在两种情况:

  1. 直接引用的包和间接引用的包是同一个包,但版本不同

    依赖关系:
    gomodtest_test|--> gomodtest_dep |--> gomodtest_base@v1.0.0
    |--> gomodtest_base@v1.0.1

    go mod tidy时, 项目会自动更新与依赖包关联的第三方包相同版本号, 并写入go.mod, 解决版本冲突问题.

  2. 间接引用的两个包是同一个包,但版本不同

    比如以下场景:
    gomodtest_test|--> gomodtest_dep |--> gomodtest_base@v1.0.0
    |--> gomodtest_dep2 |--> gomodtest_base@v1.0.1

    go mod tidy时, 默认使用第一个包引用版本号 -> gomodtest_base@v1.0.0, 并写入到go.mod.

4.4. 自动查找包依赖

go mod遵循了之前go get自动下载依赖特性, 所有的依赖包会自动全部下载.

未启用go mod功能的包会自动下载最高 tag 版本或最高 master commit版本, 而不是只会查询使用了go mod功能的包.

4.5. 总结

  1. 大部分场景下go mod initgo mod tidy两个命令就够用了
  2. mod做了一件类似maven的事: 把所有包都打上了版本号

    从此我们不再为多版本使用的困扰, 也不再为IDE打开一个新项目后等待n分钟的生成缓存犯愁. 推荐大家丢掉vendor使用统一管理.
  3. semver 将版本信息绑定进包名对于习惯了传统包管理器方案的用户来说显得有些怪异, 可能需要花上一些额外时间适应.

其他

1. 参考文章

Golang包管理--GoVendor

Go 包管理机制深入分析

go之官方依赖管理工具dep安装和使用

10分钟学会go module

Golang 包管理机制的更多相关文章

  1. Golang包管理工具glide简介

    Golang包管理工具glide简介 前言 Golang是一个十分有趣,简洁而有力的开发语言,用来开发并发/并行程序是一件很愉快的事情.在这里我感受到了其中一些好处: 没有少了许多代码格式风格的争论, ...

  2. 层次分明井然有条,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang包管理机制(package)EP10

    Go lang使用包(package)这种概念元素来统筹代码,所有代码功能上的可调用性都定义在包这个级别,如果我们需要调用依赖,那就"导包"就行了,无论是内部的还是外部的,使用im ...

  3. Golang 包管理简介

    Golang 包管理 在一个项目里,如果想引用本地包,经常会把新手搞的莫名其妙.这里通俗记录一下. 首先先要知道几个默认的规则 必须定义环境变量GOPATH,GOPATH可以定义多个目录 所有项目代码 ...

  4. Android包管理机制(二)PackageInstaller安装APK

    前言 在本系列上一篇文章Android包管理机制(一)PackageInstaller的初始化中我们学习了PackageInstaller是如何初始化的,这一篇文章我们接着学习PackageInsta ...

  5. Android包管理机制(一) PackageInstaller的初始化

    前言 包管理机制是Android中的重要机制,是应用开发和系统开发需要掌握的知识点之一. 包指的是Apk.jar和so文件等等,它们被加载到Android内存中,由一个包转变成可执行的代码,这就需要一 ...

  6. ubuntu包管理机制

    1 ubuntu包管理机制 跟大家分享一下ubuntu的软件管理机制.如果你们有过: apt-get install 或者 apt-get update 失败的经历. 在众多的apt命令中迷失. 疑惑 ...

  7. 前端工程化 - 剖析npm的包管理机制

    转自https://juejin.im/post/5df789066fb9a0161f30580c 现如今,前端开发的同学已经离不开 npm 这个包管理工具,其优秀的包版本管理机制承载了整个繁荣发展的 ...

  8. Node: 包管理机制

    Node.js 的模块机制可以很好地解决业务代码混乱的难题,但对于第三方模块包,就有些力不从心了,因为第三方模块包分散存放在各地,无法集中式管理.这就需要一个包管理机制,在 Node.js 中,Isa ...

  9. golang包管理工具

    软件开发中,不可避免的会使用到第三方库,因此包管理工具可以极大的方便开发者管理第三方依赖,避免掉入"依赖地狱". 作为google强大背书的golang语言,golang官方包管理 ...

随机推荐

  1. Floodlight+Mininet的SDN实验平台搭建初探

    平台环境说明: Cpu:Intel Core 2 Duo T6570 Mem:4.00GB Os :Ubuntu 14.04 1.Floodlight Floodlight是一个比较成熟的sdn控制器 ...

  2. 1.IO流

    1.组成部分 File.FileInputStream.FileOutStream.FileWriter.FileReader 2.File pathSeparatorChar ; separator ...

  3. gin框架中的渲染

    各种数据格式的响应 json.结构体.XML.YAML类似于java的properties.ProtoBuf 点击查看代码 // json响应 func someJson(context *gin.C ...

  4. 微信小程序入门教程之三:脚本编程

    这个系列教程的前两篇,介绍了小程序的项目结构和页面样式. 今天,接着往下讲,教大家为小程序加入 JavaScript 脚本,做出动态效果,以及如何跟用户互动.学会了脚本,就能做出复杂的页面了. 本篇的 ...

  5. 前端HTML基础之form表单

    目录 一:form表单 1.form表单功能 2.表单元素 二:form表单搭建(注册页面) 1.编写input会出现黄色阴影问题 三:完整版,前端代码(注册页面) 四:type属性介绍 1.inpu ...

  6. docker和K8s对应参数

    创建 Pod 时设置命令及参数 创建 Pod 时,可以为其下的容器设置启动时要执行的命令及其参数.如果要设置命令,就填写在配置文件的 command 字段下,如果要设置命令的参数,就填写在配置文件的  ...

  7. 如何在Visual Studio中添加opencvsharp的可视化工具

    这个文件放到这个目录下,就可以看mat对象查看了

  8. 出现 error: RPC failed; curl 18 transfer closed with outstanding read data remaining 的原因

    最近在做全栈项目,前台后台,服务器端,三端在一个文件夹,当git clone 项目的时候就会出现:error: RPC failed; curl 18 transfer closed with out ...

  9. 「ZJOI2017」树状数组

    「ZJOI2017」树状数组 以下均基于模2意义下,默认\(n,m\)同阶. 熟悉树状数组的应该可以发现,这题其实是求\(l-1\)和\(r\)位置值相同的概率. 显然\(l=1\)的情况需要特盘. ...

  10. Tomcat临时目录及java.io.tmpdir对应的目录

    最近客户现场的技术支持接连反馈了一个问题:导入数据的时候,上传的excel会在服务器上生成一个临时文件,而这个临时文件都在  tomcat 的安装目录下,如果上传次数比较多的话,就会导致tomcat安 ...