使用 dep 管理 Go 套件


更新 @ 2018-11-26: 科技真的一天都嫌長,一年之內,這篇文章已經過時了!

而根據 dep 項目的 GitHub 頁面:

dep was the “official experiment.” The Go toolchain, as of 1.11, has (experimentally) adopted an approach that sharply diverges from dep. As a result, we are continuing development of dep, but gearing work primarily towards the development of an alternative prototype for versioning behavior in the toolchain.

有關新的 Go 內置套件管理,請參閱官方 GitHub Wiki - Go 1.11 Modules.

感謝 John Arundel @bitfieldErhan Yakut @yakuter 指出了這篇文章的問題。 🙇


更新 @ 2018-02-03: 來自 godep 團隊的 Sam Boyer 澄清了本文中的一些不正確的信息。 在此給 Sam Boyer 和讀者造成的不便表示歉意。 😢


之前發布了一篇關於使用 Glide 來管理 Go 套件的文章,有讀者留言說 Glide 已經過時,而且 Glide 的團隊也建議用戶轉移到另一個由 golang 團隊編寫,叫 dep 的套件管理工具。

The Go community now has the dep project to manage dependencies. Please consider trying to migrate from Glide to dep… Glide will continue to be supported for some time but is considered to be in a state of support rather than active feature development.

之前有計劃指 dep 將會被放到 Go 1.10 的核心,但似乎還需要一些時間

更新 @ 2018-02-03:

  • dep 已經正式發布。
  • dep 沒有移動到 Go 1.10 的工具鏈中。請參閱此路線圖以獲取最新信息。
其實只是我不夠快。🐌

在 $GOPATH 中創建項目

跟之前一樣,項目文件夾必須放在 $GOPATH 下,新項目的路徑在 $GOPATH/src/gitlab.com/ykyuen/dep-example 。首先建立主程序。

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package main

import humanize "github.com/dustin/go-humanize"
import "fmt"

func main() {
  fmt.Println("hello world")
  fmt.Printf("That file is %s.\n", humanize.Bytes(82854982)) // That file is 83 MB.
  fmt.Printf("You're my %s best friend.\n", humanize.Ordinal(193)) // You are my 193rd best friend.
  fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491.
}

使用 dep

Gopkg.toml 與 Gopkg.lock

Dep 需要讀取兩個文件,分別是 Gopkg.tomlGopkg.lock 。我們用來 dep init 指令初始化來這兩個文件。

1
2
3
[[email protected] dep-example]$ dep init
  Using master as constraint for direct dep github.com/dustin/go-humanize
  Locking in master (bb3d318) for direct dep github.com/dustin/go-humanize

如您所見,dep init 這個指令會掃描源代碼並將項目所需的所有套件下載到 vendor 文件夾中。

Gopkg.lock 的功能與 glide.lock 的功能完全相同,用作鎖定套件的版本,而套件的版本則需定義在 Gopkg.toml 內。 簡而言之,Gopkg.lock 文件是自動生成的,它取決於源代碼中的 import 語句 和 Gopkg.toml

更改套件版本

讓我們編輯 Gopkg.toml ,並使用稍舊版本的 go-humanize 而不是最新的 master 分支。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
#   name = "github.com/user/project"
#   version = "1.0.0"
#
# [[constraint]]
#   name = "github.com/user/project2"
#   branch = "dev"
#   source = "github.com/myfork/project2"
#
# [[override]]
#  name = "github.com/x/y"
#  version = "2.4.0"


[[constraint]]
  #branch = "master"
  name = "github.com/dustin/go-humanize"
  revision = "0b19b17f90333e44518aa31bbf8126017960aee3"

然後執行 dep ensure 指令來更新套件。以下是更新後 Gopkg.lock 的 diff。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
--- /home/ykyuen/go/src/gitlab.com/ykyuen/dep-example/Gopkg.lock
+++ # This file is autogenerated, do not edit; changes
@@ -2,14 +2,13 @@
 
 
 [[projects]]
-  branch = "master"
   name = "github.com/dustin/go-humanize"
   packages = ["."]
-  revision = "bb3d318650d48840a39aa21a027c6630e198e626"
+  revision = "0b19b17f90333e44518aa31bbf8126017960aee3"
 
 [solve-meta]
   analyzer-name = "dep"
   analyzer-version = 1
-  inputs-digest = "a5d65a2bdf47e41b99ad71813142ae93970f5806c77630b2aea665fe631dde23"
+  inputs-digest = "0023bfe634a061b89ae0fbd71e3236f3f75f0843a0c974eeb9822040c0ea2dc4"
   solver-name = "gps-cdcl"
   solver-version = 1
 

新增一個套件

我們可以使用 dep ensure -add 指令來新增套件。

1
2
3
4
5
[[email protected] dep-example]$ dep ensure -add github.com/leekchan/accounting
Fetching sources...

"github.com/leekchan/accounting" is not imported by your project, and has been temporarily added to Gopkg.lock and vendor/.
If you run "dep ensure" again before actually importing it, it will disappear from Gopkg.lock and vendor/.

現在我們的 vendor 文件夾中已下載了 accounting 套件,版本條件也寫進了 Gopkg.toml 並鎖定在 Gopkg.lock 中。 讓我們更新 main.go ,如下所示。

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package main

import humanize "github.com/dustin/go-humanize"
import accounting "github.com/leekchan/accounting"
import "math/big"
import "fmt"

func main() {
  fmt.Println("hello world")
  fmt.Printf("That file is %s.\n", humanize.Bytes(82854982)) // That file is 83 MB.
  fmt.Printf("You're my %s best friend.\n", humanize.Ordinal(193)) // You are my 193rd best friend.
  fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491.

  ac := accounting.Accounting{Symbol: "$", Precision: 2}
  fmt.Println(ac.FormatMoney(123456789.213123))                       // "$123,456,789.21"
  fmt.Println(ac.FormatMoney(12345678))                               // "$12,345,678.00"
  fmt.Println(ac.FormatMoney(big.NewRat(77777777, 3)))                // "$25,925,925.67"
  fmt.Println(ac.FormatMoney(big.NewRat(-77777777, 3)))               // "-$25,925,925.67"
  fmt.Println(ac.FormatMoneyBigFloat(big.NewFloat(123456789.213123))) // "$123,456,789.21"
}

並執行它。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[[email protected] dep-example]$ go run main.go
hello world
That file is 83 MB.
You're my 193rd best friend.
You owe $6,582,491.
$123,456,789.21
$12,345,678.00
$25,925,925.67
-$25,925,925.67
$123,456,789.21

與 git 子模組的問題

Glide 相比,dep 的一個主要分別是套件的子模組會被忽略。例如,當通過 dep 添加 go-goracle/goracle 套件後,裡面 odpi 子模組的資料夾會是空的,導致錯誤。下面的連結可找到有關忽略子模組的原因。

更新 @ 2018-02-03:

以上有關 git 子模塊的段落並不正確。

Sam Boyer 寫道:

dep should be perfectly fine at pulling in git submodules in the case you describe. I just replicated what you describe here locally, and the problem isn’t submodules — it’s that there’s no Go code in github.com/go-goracle/goracle/odpi, so it can’t be imported directly.

You likely need to turn off unused-packages pruning in Gopkg.toml for that project specifically, as otherwise dep ensure will automatically remove what appears to be an unused directly (but it seems it’s actually used by cgo).

更新 @ 2018-03-04:

暫時 go-goracle/goracle 這個套件 dep 與不兼容. 您可以到以下帖子查看 dep 團隊的最新更新。

總結

  • dep 很有可能成為 Golang 的官方套件管理工具。
  • 如果您正在開始一個新的 Golang 項目,建議使用 dep
  • 如果您在現有項目中使用 Glide,可以考慮遷移到 dep,但是我認為繼續使用 glide 一段時間直到 dep 正式發布也不壞。
  • 另外,缺少套件的子模組可能會導致程序錯誤。
  • dep 已經正式發布。
  • dep 兼容有 git 子模塊的套件。
  • 盡可能使用 Golang 內建的套件。
  • 您可以在 gitlab.com 上找到這編文章的源代碼。