Go mod 使用

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

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

如何使用 Modules ?

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

GO111MODULEgit

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

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

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

go mod

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

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 文件,寫入如下代碼:api

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 內容:bash

$ 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...


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

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

相關文章
相關標籤/搜索