1. 程式人生 > >Go 語言包管理機制深入分析

Go 語言包管理機制深入分析

隨著 Go 語言的深入使用,其依賴管理機制也一直是各位 Gopher 熱衷於探討的話題。Go 語言的原始碼依賴可通過 go get 命令來獲取,但自動化程度不高,於是官方提供了 Dep 這樣的自動化批量管理依賴的工具。雖然 Go 語言的依賴管理在很多方面還是不如人意,但整個體系正在日趨完善,本篇就將從最基本的依賴管理場景出發,一同探討 Go 語言依賴管理的一些最佳實踐。

Go 依賴管理的基本思路

在 Go 語言中,我們通過 go get 命令將 GitHub 或者 Google Code 上的程式碼下載到本地指定目錄,然後在開發程式碼中通過 import 的形式引用本地的程式碼。

import "github.com/spf13/cobra"

Go 語言可以通過直接分析程式碼中的 import 語句來查詢依賴關係。go get 命令在執行時,就會自動解析 import 來安裝所有的依賴。那麼下載的依賴在本地是如何儲存的呢?

這裡就涉及到 Go 語言的 WORKSPACE 概念,簡單來說就是通過 GOPATH 環境變數來設定 Go 程式碼的位置。一般來說,GOPATH 目錄下會包含 pkg、src 和 bin 三個子目錄,這三個目錄各有用處。

  • bin 目錄用來放置編譯好的可執行檔案,為了使得這裡的可執行檔案可以方便的執行,在 shell 中設定PATH變數。

  • src 目錄用來放置程式碼原始檔,在進行 import 時,是使用這個位置作為根目錄的。自己編寫的程式碼也應該放在這下面,不同的專案放在不同的目錄下進行管理。

  • pkg 用來放置安裝的包的連結物件(Object)的。這個概念有點類似於連結庫,Go 會將編譯出的可連線庫放在這裡,方便編譯時連結。不同的系統和處理器架構的物件會在 pkg 存放在不同的資料夾中。

當專案在 src 目錄下管理時,多個專案可能都會使用相同的依賴,如果每個專案都存一份依賴顯然會帶來大量的冗餘,這裡我們推薦一個設定 GOPATH 環境變數時的小技巧。

export GOPATH="/usr/local/share/go:$HOME/codes/go"

這樣第三方包就會預設放置在第一個路徑中,而你可以在第二個路徑下編寫自己的程式碼,多個專案共享一份依賴。

dep - 官方 Go 依賴管理工具

dep 是 Go 語言官方提供的依賴管理工具,跟其他依賴管理工具類似,都是通過一個檔案描述依賴的座標資訊,然後批量管理(下載、升級等)依賴包(原始碼)。dep 是一個開源專案, 大家可以在 https://github.com/golang/dep 瞭解詳細資訊,其安裝方式大家可以參考官方說明,這裡我們主要介紹其使用。

基本操作

通過 dep init 命令來初始化,會建立Gopkg.lock,Gopkg.toml檔案和一個空的vendor目錄。

我們在程式碼中通過 import 命令新增依賴後,通過 dep ensure 就可以下載依賴到本地 $GOPATH/src 目錄下。

main.go

...
import (
	"github.com/golang/glog"
)
...

Gopkg.lock

[[projects]]
  branch = "master"
  digest = "1:1ba1d79f2810270045c328ae5d674321db34e3aae468eb4233883b473c5c0467"
  name = "github.com/golang/glog"
  packages = ["."]
  pruneopts = "UT"
  revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"

[solve-meta]
  analyzer-name = "dep"
  analyzer-version = 1
  input-imports = [
    "github.com/golang/glog"
  ]
  solver-name = "gps-cdcl"
  solver-version = 1

通過 dep status 我們可以檢視當前依賴引用的情況

$ dep status
PROJECT                               CONSTRAINT     VERSION        REVISION  LATEST   PKGS USED
github.com/golang/glog                branch master  branch master  23def4e   23def4e  1

另外有一個 dep check 命令來檢查是否存在依賴被引用,但是程式碼中並沒有使用的情況,Go 語言對於依賴的引用比較嚴格,不允許引用了但是沒使用的情況。從軟體安全的角度考慮,這是一個很好的實踐,避免引入一些安全風險。

$ dep check
# Gopkg.lock is out of sync:
github.com/golang/glog: in Gopkg.lock's input-imports, but neither imported nor required

當然,這種時候我們就需要移除本地依賴,最好不要手動刪除vendor中的內容,而是通過 dep ensure -update 命令來移除。

從 dep 的目錄結構,我們可以分析出 dep 的基本工作思路:

在這裡插入圖片描述

這裡面有兩個關鍵的步驟:

  • 解析依賴
    從當前專案的 import 檔案中解析出整個工程的依賴情況,並結合 Gopkg.toml 定義的規則,然後將依賴關係輸出給 Gopkg.lock,注意這個 lock 檔案最好不要手動修改。

  • 獲取依賴
    通過 Gopkg.lock 瞭解整個依賴關係之後,將依賴的具體內容拉取下來放到 vendor 目錄中,然後執行 Go build 時從本地的 vendor 讀取依賴並完成構建。

這一些都是在 dep ensure 時完成的,其實在執行這個命令時還可以傳引數,最主要的是 -no-vendor-vendor-only 這兩個引數。

-no-vendor 引數只會導致執行 resolve 函式,結果是建立一個新的Gopkg.lock 檔案,不會更新 vendor;而 -vendor-only 引數將跳過 resolve 並僅執行 vendoring 函式,導致 vendor/ 從已存在的Gopkg.lock 重新更新。

總結

Go dep 目前是一款比較好用的依賴管理工具,很多比較大型的專案都在使用,從中可以學習到依賴管理的一些基本思路,對於理解其他語言,比如NPM的依賴管理模型也是比較有好處的。