go module

前言

go 1.5 引進了vendor管理工程依賴包,可是vendor的存放路徑是在GOPATH底下,另外每一個依賴還能夠有本身的vendor,一般會弄得很亂,儘管dep管理工具能夠將vendor平級化管理,可是相對GOPATH的路徑是逃不掉的。另外,各個包的版本管理也顯得原始,甚至有的開發將依賴包從github直接download下來本身放到GOPATH底下的vendor。go的依賴包管理一致是開發者詬病的一個痛點。因此在千呼萬喚中,go 1.11 終於引進了go module管理工程的包依賴,去除了項目包管理對GOPATH的依賴,明確了依賴包的版本管理。git

 

定義

一個module是go相關包版本信息的收集單元。記錄了精準的必須依賴信息和從新編譯依賴。github

 

從示例開始

go module的使用其實十分容易上手,下面我會以一個例子來講明。golang

示例的go環境信息:web

$ go versionwindows

go version go1.12.4 darwin/amd64緩存

 

下面這個例子是依賴github.com/sirupsen/logrus 輸出一行日誌。在GOPATH外建立一個mytest的目錄,而後建立一個main.go的文件,內容以下:網絡

 

package main

import (
    log "github.com/sirupsen/logrus"
)

func main() {
        // Add this line for logging filename and line number!
    log.SetReportCaller(true)

    log.Println("hello world")
}

執行ide

go mod init mytest

其實mytest是我指定的module名稱,能夠是任意的命名,可是必定要指定,不然會報錯 go: cannot determine module path for source directory。工具

而後執行go build就會成功編譯,而且多了go.mod和go.sum兩個module相關的文件:ui

$ ls
go.mod    go.sum    main.go    mytest

$ cat go.mod 
module mytest

go 1.12

require github.com/sirupsen/logrus v1.4.2

$ cat go.sum 
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

從示例中能夠看出go.mod文件存放的是工程包依賴信息,而go.sum裏面存放的是依賴包的校驗信息。主要關注go.mod的信息。能夠看到,若是咱們不指定依賴包的版本信息,go build默認是會替咱們去拉取該依賴包的最新版本。

因此能夠總結,go module的使用分爲如下幾步:

  • go mod init $moduleName 初始化module信息。
  • go build或者go test等標準命令自動更新工程的依賴包信息。
  • 若是有須要可使用go get  $packageName@$version,例如go get foo@v1.2.3, go get foo@master, go get foo@e3702bed2,也能夠直接修改go.mod或者使用go mod edit(文章後面會講到)獲取特定的依賴包版本。

以上就是基本的go module工做流程,已經能夠知足平常的工做流程要求,下面會詳細的講解go module的其餘用法。

 

詳細用法

那麼go module一共有多少種玩法呢?直接運行go mod就會有答案:

$ go mod
Go mod provides access to operations on modules.

Note that support for modules is built into all the go commands,
not just 'go mod'. For example, day-to-day adding, removing, upgrading,
and downgrading of dependencies should be done using 'go get'.
See 'go help modules' for an overview of module functionality.

Usage:

    go mod <command> [arguments]

The commands are:

    download    download modules to local cache
    edit        edit go.mod from tools or scripts
    graph       print module requirement graph
    init        initialize new module in current directory
    tidy        add missing and remove unused modules
    vendor      make vendored copy of dependencies
    verify      verify dependencies have expected content
    why         explain why packages or modules are needed

Use "go help mod <command>" for more information about a command.

其中init前面我已經講過了,這裏就再也不重複。

 

download 

下載依賴包到緩存目錄。

 

edit

提供命令版本編輯go.mod的功能,例如go mod edit -fmt go.mod 會格式化go.mod。

用法 go mod edit [flag] [go.mod]

其中flag選項有:

  • -fmt 格式化go.mod文件
  • -require=$package:@version添加依賴,會覆蓋已存在的相同依賴。添加依賴更推薦使用go get,由於go get會更新相關的go.mod文件,而edit只會更新你指定的go.mod文件。
  • -droprequire=$package:@version 移除依賴
  • -replace=$oldPackage=$newPackage 更新已經存在的依賴。一般用於私有倉庫代碼覆蓋共有倉庫。  

這裏我重點說下-replace 選項,由於在生產中常常遇到的一種狀況是因爲這樣那樣的緣由咱們須要fork一個私有倉庫去改動第三方開源庫,例若有個小哥針對logrus作了二次開發github.com/gogap/logrus,這個時候就須要用github.com/gogap/logrus替換以前的第三方開源庫github.com/sirupsen/logrus,操做以下:

$ go mod edit -replace="github.com/sirupsen/logrus=github.com/gogap/logrus@v0.8.2"
$ go build
go: finding github.com/gogap/logrus v0.8.2
go: downloading github.com/gogap/logrus v0.8.2
go: extracting github.com/gogap/logrus v0.8.2

$ cat go.mod 
module mytest

go 1.12

require github.com/sirupsen/logrus v1.4.2

replace github.com/sirupsen/logrus => github.com/gogap/logrus v0.8.2

 

graph

顯示依賴關係(圖)。

$ go mod graph 
mytest github.com/sirupsen/logrus@v1.4.2
github.com/sirupsen/logrus@v1.4.2 github.com/davecgh/go-spew@v1.1.1
github.com/sirupsen/logrus@v1.4.2 github.com/konsorten/go-windows-terminal-sequences@v1.0.1
github.com/sirupsen/logrus@v1.4.2 github.com/pmezard/go-difflib@v1.0.0
github.com/sirupsen/logrus@v1.4.2 github.com/stretchr/objx@v0.1.1
github.com/sirupsen/logrus@v1.4.2 github.com/stretchr/testify@v1.2.2
github.com/sirupsen/logrus@v1.4.2 golang.org/x/sys@v0.0.0-20190422165155-953cdadca894

 

tidy

增長缺失的包而且移除沒有依賴的包。自動去下載依賴包,而且緩存到$GOPATH/pkg/mod目錄下。

須要注意的是,tidy會自動更新依賴包的版本,因此若是不是初建的項目仍是儘可能少用tidy,儘可能用go get精準控制新增的依賴包。

vendor

把依賴包拷貝到vendor目錄底下。前面說了那麼多想必你必定有一個疑問:go build的時候須要現場去拉取依賴包,若是個人編譯機沒有外網(訪問不了github)怎麼辦?vendor就是爲了應用這種狀況,在本地開發機(有外網)執行 go mod vendor 將依賴包拷貝到vendor底下,而後將代碼push到編譯機 執行 go build -mod=vendor。示例:

$ go mod vendor
$ ls
go.mod    go.sum    main.go    mytest    vendor
$ go build -mod=vendor

 

verify

校驗依賴關係

$ go mod verify
all modules verified

 

why

指出爲何須要依賴包。與graph的區別是,why只能解釋某一個特定的依賴包,而graph則是給出完整的依賴關係圖。

$ go mod why github.com/konsorten/go-windows-terminal-sequences
# github.com/konsorten/go-windows-terminal-sequences
mytest
github.com/sirupsen/logrus
github.com/konsorten/go-windows-terminal-sequences

 

同工程下的依賴管理

例如創建一個webserver的工程,目錄爲/Users/saas/src/awesomeProject/webserver,GOPATH設置爲/Users/saas, webserver下的目錄結構爲:

$ tree
.
├── go.mod
├── google
│   └── google.go
├── helloworld
├── server.go
└── userip
    └── userip.go

2 directories, 5 files

其中google目錄爲packge google,userip目錄爲package userip,那麼咱們要在server.go如何引用google和userip這兩個包呢?只需:

import (
    "helloworld/google"
    "helloworld/userip"
)

helloworld 是go mod init helloworld時指定的module名稱,因此helloworld索引到了webserver目錄,helloworld/google指的是webserver底下的google包。若是不指定module的名稱,默認是GOPATH下的路徑,即爲awesomeProject/webserver,引用google包時就須要指定awesomeProject/webserver/google。若是GOPATH沒有指定,又沒有指定module的名字則報錯:

$ export GOPATH=""
$ go mod init
go: cannot determine module path for source directory /Users/saas/src/awesomeProject/webserver (outside GOPATH, no import comments)

指定module就能夠了,即使沒有GOPATH:

$ go mod init helloworld
go: creating new go.mod: module helloworld

go build時默認會用module的名字(base name)給程序名稱,這裏是helloworld。若是module名稱爲 awesomeProject/webserver則是webserver。

 

Goland IDE打開Go Module

上面例子中發如今Goland IDE中helloworld/google會被標紅,說找不到helloworld這個目錄,說明IDE的Go Module功能尚未打開,須要以下設置:

 

總結

文章經過一個打印日誌的例子演示了全部go module的用法,其中包括平常基本用法和全面的用法介紹。新增依賴包的更新推薦使用go get。依賴包的替換推薦使用go mod edit -replace。在編譯機網絡有限制的時候提供了vendor的解決方案。

 

參考

https://github.com/golang/go/wiki/Modules

相關文章
相關標籤/搜索