[Go] Go Modules

程式語言:Go
工具:
go mod
官方文件
官方 wiki 文件

功能:套件管理,V1.11 之後才支援,建議搭配 VCS (例:github)

環境變數設定

使用 modules,build|test|install 需在 go.mod 的目錄下,不然會出錯
go.mod & go.sum 必須要加入版控,以確保在不同版本都能成功的 build
  • GO111MODULE
    • unset or auto
      • Inside GOPATH
        • 預設跟 1.10 行為一樣 (忽略 modules) 
      • Outside GOPATH
        • 當存在 go.mod,則為 modules 行為
    • on
      • 強制為 modules 行為,即使 inside GOPATH
    • off
      • 強制為 1.10 的行為,即使擁有 go.mod

簡易步驟

  1. 移至 Project 目錄
    $ cd <project path outside $GOPATH/src>         # e.g., cd ~/projects/hello
    若 Project 位於 GOPATH 中,需設定以下環境變數
    $ export GO111MODULE=on                         # manually active module mode
  2. 建立 go.mod
    $ go mod init github.com/my/repo
    module 名字可任意,通常與 VCS 相關
  3. build|test|install 後,go.mod 將自動加入依賴,且 pkg 下載到 GOPATH/pkg/mod 內
    $ go build|test|install ./...
  4. 測試確認
    $ go test ./...
  5. 測試 all direct and indirect dependencies,防止不相容
    $ go test ./...

簡單範例

  1. 測試 code,hello.go
    package main
    
    import (
     "fmt"
     "rsc.io/quote"
    )
    
    func main() {
     fmt.Println("Hello World")
     quote.Hello()
    }
    
  2. 初始化 mod
    $ go mod init hello
    得到 go.mod
    module hello
    
    go 1.12
    
  3. build
    $ go build
    go.mod 多出一行 require
    module hello
    
    go 1.12
    
    require rsc.io/quote v1.5.2
    
    多出 go.sum,內為依賴版本的 Hash
    golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
    golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
    rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3Y=
    rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=
    rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
    rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
    
  4. 列出目前的依賴
    $ go list -m all
    hello
    golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
    rsc.io/quote v1.5.2
    rsc.io/sampler v1.3.0
  5. 更新 text 為 tag 版本
    $ go get golang.org/x/text
    go.mod 多出一行 require,並標注為 indirect
    module hello
    
    go 1.12
    
    require (
     golang.org/x/text v0.3.2 // indirect
     rsc.io/quote v1.5.2
    )
    

升级更新

  • 升級到最新的版本
    $ go get
  • 升級到最新的次要版本或者修訂版本 (x.y.z,z 是修訂版本號, y 是次要版本號)
    $ go get -u
  • 升級到最新的修訂版本
    $ go get -u=patch
  • 升級到指定的版本號 version
    $ go get package@version

版本號命名規則 semver

以下命名皆合法,詳細說明可參考連結
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7
gopkg.in/vmihailenco/msgpack.v2 v2.9.1
gopkg.in/yaml.v2 <=v2.2.1
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e
latest

指定不同版本的 module 範例

hello.go
package hello

import (
    "rsc.io/quote"
    quoteV3 "rsc.io/quote/v3"
)

func Hello() string {
    return quote.Hello()
}

func Proverb() string {
    return quoteV3.Concurrency()
}
hello_test.go
package hello

import "testing"

func TestProverb(t *testing.T) {
 want := "Concurrency is not parallelism."
 if got := Proverb(); got != want {
  t.Errorf("Proverb() = %q, want %q", got, want)
 }
}
執行測試
$ go test
go: finding rsc.io/quote/v3 v3.1.0
go: downloading rsc.io/quote/v3 v3.1.0
go: extracting rsc.io/quote/v3 v3.1.0
PASS
ok      hello   1.391s
可發現 rsc.io/quote 有兩個版本
$ go list -m rsc.io/q...
rsc.io/quote v1.5.2
rsc.io/quote/v3 v3.1.0
go.mod
module hello

go 1.12

require (
 rsc.io/quote v1.5.2
 rsc.io/quote/v3 v3.1.0
)
go.sum
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3Y=
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=
rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
順帶一提,如下,加了版本,仍是使用原來的 module 名,除非有重新指定
package hello

import (
    "rsc.io/quote/v3"
)

func Proverb() string {
    return quote.Concurrency()
}

本地依賴範例

Folder
└── test
    └── hello.go
    └── go.mod
└── test2
    └── hello.go
    └── go.mod
test/hello.go
package hello

func Hello() string {
 println("hello")
 return "Hello, world."
}
test/go.mod
module ttt //不影響運行

go 1.12
test2/hello.go
package main

import "test/hello" //可以是錯誤的路徑,只要 replace 是對的

func main() {
 hello.Hello()
}
test2/go.mod
module main

go 1.12

require test/hello v0.0.0 // 虛擬版號

// 取代為真正的路徑,可為絕對或相對路徑,但注意為 go.mod 所在的資料夾
replace test/hello => ../test
執行測試,需在 go.mod 的資料夾中
$ cd test2
$ go run hello.go
hello

命令

  • go mod download
    • 下載依賴的 module 到本地 cache,go get|build|test|install 有同樣效果
  • go mod edit
    • 編輯 go.mod 文件,需搭配 flag,可查看說明
      go help mod edit
  • go mod graph
    • 印出 module 依賴圖
  • go mod init
    • 在當前文件夾下初始化一個新的 module, 建立 go.mod 文件
  • go mod tidy
    • 增加丟失的 module,去掉未用的 module 
  • go mod vendor
    • 將依賴複製到 vendor
  • go mod verify
    • 校驗依賴
  • go mod why
    • 解釋為什麼需要依賴
  • go list -m
    • 列出所有的依賴

參考

浅析 golang module
gvm + go mod
关于Go Module的争吵
Go 語言 1.11 版本推出 go module
go modules详解
跳出Go module的泥潭
語意化版本 2.0.0

留言