Go mod 使用

go modules 是 golang 1.11 新加的特性。現在1.12 已經發布了,是時候用起來了。Modules官方定義爲:

模塊是相關Go包的集合。modules是源代碼交換和版本控制的單元。 go命令直接支持使用modules,包括記錄和解析對其他模塊的依賴性。modules替換舊的基於GOPATH的方法來指定在給定構建中使用哪些源文件。

如何使用 Modules ?

  1. 把 golang 升級到 1.11(現在1.12 已經發布了,建議使用1.12)
  2. 設置 GO111MODULE

GO111MODULE

GO111MODULE 有三個值:off, onauto(默認值)

  • GO111MODULE=off,go命令行將不會支持module功能,尋找依賴包的方式將會沿用舊版本那種通過vendor目錄或者GOPATH模式來查找。
  • GO111MODULE=on,go命令行會使用modules,而一點也不會去GOPATH目錄下查找。
  • GO111MODULE=auto,默認值,go命令行將會根據當前目錄來決定是否啓用module功能。這種情況下可以分爲兩種情形:

    • 當前目錄在GOPATH/src之外且該目錄包含go.mod文件
    • 當前文件在包含go.mod文件的目錄下面。
當modules 功能啓用時,依賴包的存放位置變更爲$GOPATH/pkg,允許同一個package多個版本並存,且多個項目可以共享緩存的 module。

go mod

golang 提供了 go mod命令來管理包。

go mod 有以下命令:

命令 說明
download download modules to local cache(下載依賴包)
edit edit go.mod from tools or scripts(編輯go.mod
graph print module requirement graph (打印模塊依賴圖)
init initialize new module in current directory(在當前目錄初始化mod)
tidy add missing and remove unused modules(拉取缺少的模塊,移除不用的模塊)
vendor make vendored copy of dependencies(將依賴複製到vendor下)
verify verify dependencies have expected content (驗證依賴是否正確)
why explain why packages or modules are needed(解釋爲什麼需要依賴)

如何在項目中使用

示例一:創建一個新項目

  1. GOPATH 目錄之外新建一個目錄,並使用go mod init 初始化生成go.mod 文件
➜  ~ mkdir hello
➜  ~ cd hello
➜  hello go mod init hello
go: creating new go.mod: module hello
➜  hello ls
go.mod
➜  hello cat go.mod
module hello

go 1.12
go.mod文件一旦創建後,它的內容將會被go toolchain全面掌控。go toolchain會在各類命令執行時,比如go get、go build、go mod等修改和維護go.mod文件。

go.mod 提供了module, requirereplaceexclude 四個命令

  • module 語句指定包的名字(路徑)
  • require 語句指定的依賴項模塊
  • replace 語句可以替換依賴項模塊
  • exclude 語句可以忽略依賴項模塊
  1. 添加依賴

新建一個 server.go 文件,寫入以下代碼:

package main

import (
    "net/http"
    
    "github.com/labstack/echo"
)

func main() {
    e := echo.New()
    e.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "Hello, World!")
    })
    e.Logger.Fatal(e.Start(":1323"))
}

執行 go run server.go 運行代碼會發現 go mod 會自動查找依賴自動下載:

$ go run server.go
go: finding github.com/labstack/echo v3.3.10+incompatible
go: downloading github.com/labstack/echo v3.3.10+incompatible
go: extracting github.com/labstack/echo v3.3.10+incompatible
go: finding github.com/labstack/gommon/color latest
go: finding github.com/labstack/gommon/log latest
go: finding github.com/labstack/gommon v0.2.8
# 此處省略很多行
...

   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v3.3.10-dev
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\
⇨ http server started on [::]:1323

現在查看go.mod 內容:

$ cat go.mod

module hello

go 1.12

require (
    github.com/labstack/echo v3.3.10+incompatible // indirect
    github.com/labstack/gommon v0.2.8 // indirect
    github.com/mattn/go-colorable v0.1.1 // indirect
    github.com/mattn/go-isatty v0.0.7 // indirect
    github.com/valyala/fasttemplate v1.0.0 // indirect
    golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect
)

go module 安裝 package 的原則是先拉最新的 release tag,若無tag則拉最新的commit,詳見 Modules官方介紹。 go 會自動生成一個 go.sum 文件來記錄 dependency tree:

$ cat go.sum
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
... 省略很多行
  1. 再次執行腳本 go run server.go 發現跳過了檢查並安裝依賴的步驟。
  2. 可以使用命令 go list -m -u all 來檢查可以升級的package,使用go get -u need-upgrade-package 升級後會將新的依賴版本更新到go.mod

    • 也可以使用 go get -u 升級所有依賴

go get 升級

  • 運行 go get -u 將會升級到最新的次要版本或者修訂版本(x.y.z, z是修訂版本號, y是次要版本號)
  • 運行 go get -u=patch 將會升級到最新的修訂版本
  • 運行 go get package@version 將會升級到指定的版本號version
  • 運行go get如果有版本的更改,那麼go.mod文件也會更改

示例二:改造現有項目(helloword)

項目目錄爲:

$ tree
.
├── api
│   └── apis.go
└── server.go

1 directory, 2 files

server.go 源碼爲:

package main

import (
    api "./api"  // 這裏使用的是相對路徑
    "github.com/labstack/echo"
)

func main() {
    e := echo.New()
    e.GET("/", api.HelloWorld)
    e.Logger.Fatal(e.Start(":1323"))
}

api/apis.go 源碼爲:

package api

import (
    "net/http"

    "github.com/labstack/echo"
)

func HelloWorld(c echo.Context) error {
    return c.JSON(http.StatusOK, "hello world")
}
  1. 使用 go mod init *** 初始化go.mod
$ go mod init helloworld
go: creating new go.mod: module helloworld
  1. 運行 go run server.go
go: finding github.com/labstack/gommon/color latest
go: finding github.com/labstack/gommon/log latest
go: finding golang.org/x/crypto/acme/autocert latest
go: finding golang.org/x/crypto/acme latest
go: finding golang.org/x/crypto latest
build command-line-arguments: cannot find module for path _/home/gs/helloworld/api

首先還是會查找並下載安裝依賴,然後運行腳本 server.go,這裏會拋出一個錯誤:

build command-line-arguments: cannot find module for path _/home/gs/helloworld/api

但是go.mod 已經更新:

$ cat go.mod
module helloworld

go 1.12

require (
        github.com/labstack/echo v3.3.10+incompatible // indirect
        github.com/labstack/gommon v0.2.8 // indirect
        github.com/mattn/go-colorable v0.1.1 // indirect
        github.com/mattn/go-isatty v0.0.7 // indirect
        github.com/valyala/fasttemplate v1.0.0 // indirect
        golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect
)
那爲什麼會拋出這個錯誤呢?

這是因爲 server.go 中使用 internal package 的方法跟以前已經不同了,由於 go.mod會掃描同工作目錄下所有 package 並且變更引入方法,必須將 helloworld當成路徑的前綴,也就是需要寫成 import helloworld/api,以往 GOPATH/dep 模式允許的 import ./api 已經失效,詳情可以查看這個 issue

  1. 更新舊的package import 方式

所以server.go 需要改寫成:

package main

import (
    api "helloworld/api"  // 這是更新後的引入方法
    "github.com/labstack/echo"
)

func main() {
    e := echo.New()
    e.GET("/", api.HelloWorld)
    e.Logger.Fatal(e.Start(":1323"))
}
一個小坑:開始在golang1.11 下使用go mod 遇到過 go build github.com/valyala/fasttemplate: module requires go 1.12 這種錯誤,遇到類似這種需要升級到1.12 的問題,直接升級golang1.12 就好了。幸虧是在1.12 發佈後才嘗試的go mod 🤷‍♂️
  1. 到這裏就和新創建一個項目沒什麼區別了

使用replace替換無法直接獲取的package

由於某些已知的原因,並不是所有的package都能成功下載,比如:golang.org下的包。

modules 可以通過在 go.mod 文件中使用 replace 指令替換成github上對應的庫,比如:

replace (
    golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a
)

或者

replace golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a

參考鏈接

References

[1] Modules官方介紹: https://github.com/golang/go/...
[2] issue: https://github.com/golang/go/...
[3] 這種錯誤: https://github.com/golang/go/...
[4] Modules官方介紹: https://github.com/golang/go/...
[5] Golang 1.11 新功能介紹 – Modules: https://www.lightblue.asia/go...
[6] What are Go modules and how do I use them?: https://talks.godoc.org/githu...
[7] go mod doesn't work for github.com/gomarkdown/markdown/html : https://github.com/golang/go/...
[8] 再探go modules:使用與細節: https://www.cnblogs.com/apoce...
[9] 初窺Go module: https://tonybai.com/2018/07/1...


最後,感謝女朋友支持和包容,比❤️

也可以在公號輸入以下關鍵字獲取歷史文章:公號&小程序 | 設計模式 | 併發&協程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章