【Go】dep使用介绍

时间:2024-03-24 11:06:19

一. 简介

依赖管理一直以来都是大型项目开发所面临的一个问题,成熟的编程语言都会有对应的一个甚至多个依赖管理工具。
例如**C++**项目通常会使用Make、Scons等来管理依赖的so,Java项目通常会使用Maven来管理依赖包。Golang项目同样也需要有类似工具来管理对应的依赖包。

dep是Golang官方依赖管理工具,目前只支持Golang 1.9以上的版本。

① go get

Golang最原始的依赖管理是go get ,执行命令后会拉取代码放入src下面,使用go get依赖管理的项目结构如下。假设项目目录 $GOPATH/src/demo

└── bin
└── pkg
└── src
    ├── demo
    │   └── main.go
    ├── github.com
    ├── golang.org
    └── gopkg.in

go get存在以下几个问题

  1. go get的代码是作为GOPATH下全局依赖,并且没有版本控制。
  2. go get管理依赖必须要设置GOPATH=/xx/xx/src,这样才能保证代码可以编译通过。

② dep

dep 依赖管理引入了 vendor 目录作为依赖管理目录,目前 IDEGolang 编辑插件都能很好的支持。
例如Goland依赖包时会优先查找项目根目录下的vendor目录,dep依赖管理的项目结构如下。假设项目目录 $GOPATH/src/projectname

. 
├── Gopkg.lock
├── Gopkg.toml
├── main.go
└── vendor
    ├── github.com
    │   ├── gin-contrib
    │   ├── gin-gonic
    │   ├── golang
    │   ├── mattn
    │   └── ugorji
    ├── golang.org
    │   └── x
    └── gopkg.in
        ├── go-playground
        └── yaml.v2

二. 安装

  1. 设置环境变量,使用vendor目录
    GO15VENDOREXPERIMENT=1
  2. 安装dep
    curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
    其它安装方式请参考官网 https://golang.github.io/dep/docs/installation.html
  3. 验证安装,安装成功后输入dep命令会有以下输出
$ dep
Dep is a tool for managing dependencies for Go projects

Usage: "dep [command]"

Commands:

  init     Set up a new Go project, or migrate an existing one
  status   Report the status of the project's dependencies
  ensure   Ensure a dependency is safely vendored in the project
  version  Show the dep version information
  check    Check if imports, Gopkg.toml, and Gopkg.lock are in sync

Examples:
  dep init                               set up a new project
  dep ensure                             install the project's dependencies
  dep ensure -update                     update the locked versions of all dependencies
  dep ensure -add github.com/pkg/errors  add a dependency to the project

Use "dep help [command]" for more information about a command.

三. 使用

dep依赖管理非常简单,只有几个子命令,下面我们重点介绍几个常用的命令。

① dep init

一个项目要使用dep管理依赖首先必须使用dep init来初始化,进入项目主目录初始化。

$ dep init -v
Getting direct dependencies...
Checked 1 directories for packages.
Found 0 direct dependencies.
Root project is "sample/demo"
1 transitively valid internal packages
0 external packages imported from 0 projects
   (0)   ✓ select (root)
✓ found solution with 0 packages from 0 projects

Solver wall times by segment:
select-root: 72.32µs
other: 8.248µs

TOTAL: 80.568µs

dep init之后项目主目录下会多两个文件Gopkg.lockGopkg.toml和一个目录vendor

  1. Gokpg.lock 自动生成不可自行修改,Gopkg.lock定义了所有依赖项目的详细信息。
  2. Gopkg.toml 依赖管理的核心文件,可以生成也可以手动修改。Gopkg.toml里面只定义项目直接依赖项,而Gopkg.lock里面除了包含Gopkg.toml中的所有项之外,还包含间接依赖项。例如我们的项目依赖项目A, 而项目A又依赖B、C,那么只有A会包含在Gopkg.toml中,而A、B、C都会定义在Gopkg.lock中。
  3. vendor 是依赖管理目录,会优先从vendor目录找依赖代码。

它们三者之间的关系如下
【Go】dep使用介绍

② dep status

dep status用来查看项目依赖的状态

$ dep status
PROJECT                     CONSTRAINT  VERSION  REVISION  LATEST  PKGS USED
github.com/gorilla/context  v1.1.1      v1.1.1   08b5f42   v1.1.1  1  
github.com/gorilla/mux      v1.6.2      v1.6.2   e3702be   v1.6.2  1
--------------------- 

③ dep ensure

dep ensure用来安装、更新依赖包代码,-v 参数可以输出详细过程。

  1. dep ensure: 安装项目依赖的代码
  2. dep ensure -update: 更新项目依赖
  3. dep ensure -add: 添加项目依赖

注意: 在国内使用dep ensure有个很重要的问题是,当我们使用了第三方开源代码例如Github,由于GFW原因会导致无法下载源码到本地,因此需要保证本地机器是能够*

四. Gopkg.toml

一个完整的Gopkg.toml文件如下所示

required = ["github.com/user/thing/cmd/thing"]

ignored = [
  "github.com/user/project/pkgX",
  "bitbucket.org/user/project/pkgA/pkgY"
]

noverify = ["github.com/something/odd"]

[metadata]
codename = "foo"

[prune]
  non-go = true

  [[prune.project]]
    name = "github.com/project/name"
    go-tests = true
    non-go = false

[[constraint]]
  name = "github.com/user/project"
  version = "1.0.0"

  [metadata]
  property1 = "value1"
  property2 = 10

[[constraint]]
  name = "github.com/user/project2"
  branch = "dev"
  source = "github.com/myfork/project2"

[[override]]
  name = "github.com/x/y"
  version = "2.4.0"

  [metadata]
  propertyX = "valueX"

Gopkg.toml文件是由dep init命令生成的, 支持自定义编辑文件内容,主要用来定义dep行为的几种规则。

  1. 依赖规则: constraintoverride定义依赖的包的版本以及搜索来源
  2. 包引用规则: requiredignored定义哪些包是必须包含或者哪些包必须排除
  3. prune 定义哪些包是不需要的会自动从vendor中删除

由于toml文件非树形结构,Gopgk.toml文件要求required和ignored必须在constraint和override之前定义

① constraint

constraint定义了项目直接依赖的包相关的信息

[[constraint]]
  # Required: the root import path of the project being constrained.
  name = "github.com/user/project"
  # Recommended: the version constraint to enforce for the project.
  # Note that only one of "branch", "version" or "revision" can be specified.
  version = "1.0.0"
  branch = "master"
  revision = "abc123"

  # Optional: an alternate location (URL or import path) for the project's source.
  source = "https://github.com/myfork/package.git"

  # Optional: metadata about the constraint or override that could be used by other independent systems
  [metadata]
  key1 = "value that convey data to other systems"
  system1-data = "value that is used by a system"
  system2-data = "value that is used by another system"
  1. name: 必须字段,定义依赖包的名称
  2. version: 推荐字段,定义依赖包的特定版本
  3. branch: 推荐字段,定义依赖包分支名称
  4. revision: 可选字段,定义修订标识
  5. source: 可选字段,定义依赖项目代码来源
  6. metadata: 可选字段,定义项目元信息

正常情况下constraint只需要定义name、version和branch这几个字段即可。

② override

override定义了项目直接依赖的包相关的信息,但它是用于解决依赖冲突。
实际项目开发中我们经常会遇到A依赖B、C,B依赖C这种情况。

假设A项目的Gopkg.toml如下

[[constraint]]
  name = "B"
  branch = "master"
  
[[constraint]]
  name = "C"
  branch = "master"

假设B项目的Gopkg.toml如下

[[constraint]]
  name = "C"
  branch = "master"

接下来如果A项目依赖了C的某个分支,那此时A项目的Gopkg.toml如下。

[[constraint]]
  name = "B"
  branch = "master"
  
[[constraint]]
  name = "C"
  branch = "branch"

这个时候A项目中执行dep ensure是不会成功的,会提示A和B依赖的C冲突了。
为了解决这个问题只需要A项目Gopkg.toml把constraint改成override,强制让B项目在依赖C项目的时候使用branch而不是使用master。

[[constraint]]
  name = "B"
  branch = "master"
  
[[override]]
  name = "C"
  branch = "branch"

③ prune

prune定义了全局和每个项目的清理机制,这个配置决定了哪些文件会被写入vendor目录,prune配置支持以下3个配置选项。没有设置默认prune选项为禁用

  1. unused-packages 表示是否需要删除未出现在引用视图里的包
  2. non-go 表示是否需要删除未被Go代码使用的文件
  3. go-tests 表示是否需要删除单元测试文件

一个完整的prune配置如下所示

[prune]
  # 全局配置
  go-tests = true
  unused-packages = true
  
  # 子依赖配置
  [[prune.project]]
    name = "github.com/ethereum/xxxx"
    # xxxx只删除go-tests
    unused-packages = false

五. Gopkg.lock

Gopkg.lock文件由dep ensuredep init命令自动生成用户不能做任何编辑修改。文件内容为整个项目的完整依赖,由一序列的**[[project]]**组成。

# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.

[[projects]]
  digest = "1:51046bf99bcee01c3ff29ed1866c8e43a2191220406781a26b208f19e2b40c1d"
  name = "cloud.google.com/go"
  packages = [
    "compute/metadata",
    "internal/version",
    "translate",
    "translate/internal/translate/v2",
  ]
  pruneopts = "UT"
  revision = "6cb29e61d96723a38dcac44d4c15c36744d96d07"
  version = "v0.29.0"

[[projects]]
  digest = "1:9f3b30d9f8e0d7040f729b82dcbc8f0dead820a133b3147ce355fc451f32d761"
  name = "github.com/BurntSushi/toml"
  packages = ["."]
  pruneopts = "UT"
  revision = "3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005"
  version = "v0.3.1"
  1. digest: vendor目录下当前项目内容的哈希签名,使用标准的crypto/sha256算法
  2. name: 依赖的项目名
  3. packages: 项目依赖的完整的包列表
  4. pruneopts: prune选项, N表示non-go,U表示unused-packages,T表示go-tests
  5. revision: 修订版本唯一标识
  6. version: 项目版本