Golang 包管理机制

1. 历史

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

  • GoVendor
  • Glide
  • Godep

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

2. 基础概念

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

  1. 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. 安装

  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. 相关命令

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

e.g.

  1. # Setup your project.
  2. cd "my project in GOPATH"
  3. # 初始化 vendor 目录, project 下出现 vendor 目录
  4. govendor init
  5. # 从$GOPATH中添加依赖到vendor下
  6. govendor add +external
  7. # 列出已经存在的依赖包
  8. govendor list
  9. # Look at what is using a package
  10. govendor list -v fmt
  11. # 从远程添加, 更新依赖
  12. govendor fetch golang.org/x/net/context@a4bbce9fcae005b22ae5443f6af064d80a6f5a55
  13. # 格式化本地依赖文件
  14. govendor fmt +local
  15. # Build everything in your repository only
  16. govendor install +local
  17. # Test your repository only
  18. 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. 安装

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

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

3.2.2. 说明

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

  1. ├── Gopkg.lock
  2. ├── Gopkg.toml
  3. └── 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. 相关命令

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

3.3. gomodule

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

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

3.3.1. 安装

编辑环境变量, 添加:

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

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

3.3.2. 相关命令

  1. # 初始化
  2. go mod init
  3. # 下载依赖
  4. go mod download
  5. # 移除未用模块, 添加缺失模块
  6. go mod tidy
  7. # 验证模块正确性
  8. go mod verify
  9. # 将依赖复制到项目的vendor目录下
  10. go mod vendor

4. GoModule的优点

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

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

解决方法:

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

  1. replace (
  2. golang.org/x/crypto => github.com/golang/crypto latest
  3. golang.org/x/sys => github.com/golang/sys latest
  4. )

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

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

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

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

  1. package main
  2. import (
  3. "fmt"
  4. v1 "github.com/e421083458/gomodtest_base"
  5. v2 "github.com/e421083458/gomodtest_base/v2"
  6. )
  7. func main(){
  8. v2.NewIntCollection("hello","sex")
  9. v1.NewIntCollection("hello")
  10. fmt.Println("hello");
  11. }

go.mod

  1. module new_module_test
  2. require (
  3. github.com/e421083458/gomodtest_base v1.0.1
  4. github.com/e421083458/gomodtest_base/v2 v2.0.0
  5. )

4.3. 依赖包冲突问题

这边存在两种情况:

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

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

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

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

    1. 比如以下场景:
    2. gomodtest_test|--> gomodtest_dep |--> gomodtest_base@v1.0.0
    3. |--> 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. Xbatis:SpringBoot 数据管理框架

    目录 安装 下载源码 编译源码 添加依赖 数据表 数据源 Xbatis XbatisManager Database/Table/Column Column Table Database Create ...

  2. 记录ABAP开发的日常——SAP_PO开发同步接口案例

    前言:在项目中遇到任务PO接口,需求是SRM发送采购订单信息给SAP,SAP根据信息调用BAPI同步数据,在此作为案例记录. 本次接口采用的协议是SOAP,当然也有其他的协议比如REST等等,在此不做 ...

  3. 树形dp空间优化(dfn)

    树形dp空间优化 介绍 有时题目会告诉我们n叉树的最大层数,或者给出一个完全n叉树树,直接做树形dp会爆空间时,就可以用这个优化方法. 多数树形dp都是先dfs到子树,再合并到根上,显然当合并到根上时 ...

  4. StringBuilder类介绍

    1 package cn.itcast.p2.stringbuffer.demo; 2 3 public class StringBuilderDemo { 4 public static void ...

  5. GitHub镜像

    GitHub 官网镜像(可以用来clone push等,但是不能登录) https://github.com.cnpmjs.org https://git.sdut.me https://hub.fa ...

  6. ansible roles实践——安装java

    [root@master] /etc/ansible$ cat roles/java/tasks/main.yml ---- name: unzip jdk unarchive: src=jdk-8u ...

  7. MySQL表空间结构

    在Innodb中,我们可以指定一张表的数据是保存在独立表空间还是系统表空间,这个参数是:innodb_file_per_table 如果我们设置这个参数的值为0,那么一个表将使用系统表空间来保存表的数 ...

  8. java代码实现调用短信接口,发送短信验证。

    一.代码示例 package com.aaa.zxf.login; import org.apache.commons.httpclient.HttpClient; import org.apache ...

  9. FreeSWITCH 1.10.7 编译(debian 11)

    1.安装预备库 apt install -y build-essential gdb gnupg2 wget autoconf lsb-release libtool libtool-bin libt ...

  10. JDK安装步骤

    安装过程: 新建文件夹 新建文件夹 首先新建两个路径:D:\java\jdk和D:\java\jre,代表我把Java安装到D盘下的java路径下,在该路径下要新建两个路径,一会儿放jdk和jre. ...