拜拜了,GOPATH君!新版本Golang的包管理入門教程
Go 1.11和1.12實現了對包管理的初步支援,Go的新依賴管理系統使依賴版本資訊明確且易於管理。
Using Go Modules - The Go Blog
新的包管理模式有什麼不同?
作為Go語言的推廣者,常常被問到各種關於Go語言的基礎特性問題。
其中,關於包管理方面的問題會讓我非常尷尬,因為Go的包管理在1.11之前與Python、Node、Java比較起來真的只能算是“僅僅可用”而已。
因為:
- 在不使用額外的工具的情況下,Go的依賴包需要手工下載,
- 第三方包沒有版本的概念,如果第三方包的作者做了不相容升級,會讓開發者很難受
- 協作開發時,需要統一各個開發成員本地
$GOPATH/src
- 引用的包引用了已經轉移的包,而作者沒改的話,需要自己修改引用。
- 第三方包和自己的包的原始碼都在src下,很混亂。對於混合技術棧的專案來說,目錄的存放會有一些問題
新的包管理模式解決了以上問題
- 自動下載依賴包
- 專案不必放在GOPATH/src內了
- 專案內會生成一個go.mod檔案,列出包依賴
- 所以來的第三方包會準確的指定版本號
- 對於已經轉移的包,可以用replace 申明替換,不需要改程式碼
開始吧用一個小例子來說明問題!
準備工作
- 升級golang 版本到 1.12Go下載
- 新增環境變數GO111MODULE為on或者auto
GO111MODULE=auto
準備完畢,非常簡單吧!!
建立一個專案
首先,在$GOPATH/src
路徑外的你喜歡的地方建立一個目錄,cd 進入目錄,新建一個hello.go檔案,內容如下
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, world!")
}
初始化模組
在當前目錄下,命令列執行 go mod init + 模組名稱 初始化模組
go mod init hello
執行完後,會在當前專案目錄下生成一個go.mod檔案,這是一個關鍵檔案,之後的包的管理都是通過這個檔案管理。
官方說明:除了go.mod之外,go命令還維護一個名為go.sum的檔案,其中包含特定模組版本內容的預期加密雜湊
go命令使用go.sum檔案確保這些模組的未來下載檢索與第一次下載相同的位,以確保專案所依賴的模組不會出現意外更改,無論是出於惡意、意外還是其他原因。 go.mod和go.sum都應檢入版本控制。
go.sum不需要手工維護,所以可以不用太關注。
生成出來的檔案包含模組名稱和當前的go版本號
module hello
go 1.12
注意:子目錄裡是不需要init的,所有的子目錄裡的依賴都會組織在根目錄的go.mod檔案裡
來吧,搞點小事情,看看go.mod怎麼工作的
接下來,讓你的專案依賴一下第三方包
以大部分人都熟悉的beego為例吧!
修改Hello.go檔案:
package main
import "github.com/astaxie/beego"
func main() {
beego.Run()
}
按照過去的做法,要執行hello.go需要執行go get
命令 下載beego包到 $GOPATH/src
但是,使用了新的包管理就不在需要這樣做了
直接go run hello.go
稍等片刻… go 會自動查詢程式碼中的包,下載依賴包,並且把具體的依賴關係和版本寫入到go.mod和go.sum檔案中。
檢視go.mod,它會變成這樣:
module hello
go 1.12
require github.com/astaxie/beego v1.11.1
require
關鍵子是引用,後面是包,最後v1.11.1 是引用的版本號
這樣,一個使用Go包管理方式建立專案的小例子就完成了。
那麼,接下來,幾個小問題來了
問題一:依賴的包下載到哪裡了?還在GOPATH裡嗎?
不在。
使用Go的包管理方式,依賴的第三方包被下載到了$GOPATH/pkg/mod
路徑下。
如果你成功運行了本例,可以在您的$GOPATH/pkg/mod
下找到一個這樣的包github.com/astaxie/[email protected]
問題二: 依賴包的版本是怎麼控制的?
在上一個問題裡,可以看到最終下載在$GOPATH/pkg/mod
下的包github.com/astaxie/[email protected]
最後會有一個版本號 1.11.1,也就是說,$GOPATH/pkg/mod
裡可以儲存相同包的不同版本。
版本是在go.mod中指定的。
如果,在go.mod中沒有指定,go命令會自動下載程式碼中的依賴的最新版本,本例就是自動下載最新的版本。
如果,在go.mod用require語句指定包和版本 ,go命令會根據指定的路徑和版本下載包,
指定版本時可以用latest
,這樣它會自動下載指定包的最新版本;
依賴包的版本號是什麼?是包的釋出者標記的版本號,格式為 vn.n.n (n代表數字),本例中的beego的歷史版本可以在其程式碼倉庫release看到Releases · astaxie/beego · GitHub
如果包的作者還沒有標記版本,預設為 v0.0.0
問題三: 可以把專案放在$GOPATH/src下嗎?
可以。
但是go會根據GO111MODULE的值而採取不同的處理方式
預設情況下,GO111MODULE=auto
自動模式
- auto自動模式下,專案在
$GOPATH/src
裡會使用$GOPATH/src
的依賴包,在$GOPATH/src外,就使用go.mod 裡 require的包 - on開啟模式,1.12後,無論在
$GOPATH/src
裡還是在外面,都會使用go.mod 裡 require的包 - off關閉模式,就是老規矩。
問題三: 依賴包中的地址失效了怎麼辦?比如http://golang.org/x/… 下的包都無法下載怎麼辦?
在go快速發展的過程中,有一些依賴包地址變更了。
以前的做法
- 修改原始碼,用新路徑替換import的地址
- git clone 或 go get 新包後,copy到$GOPATH/src裡舊的路徑下
無論什麼方法,都不便於維護,特別是多人協同開發時。
使用go.mod就簡單了,在go.mod檔案裡用 replace 替換包,例如
replace golang.org/x/text => github.com/golang/text latest
這樣,go會用http://github.com/golang/text替代http://golang.org/x/text,原理就是下載http://github.com/golang/text的最新版本到$GOPATH/pkg/mod/golang.org/x/text
下。
問題四: init生成的go.mod的模組名稱有什麼用?
本例裡,用 go mod init hello 生成的go.mod檔案裡的第一行會申明module hello
因為我們的專案已經不在$GOPATH/src
裡了,那麼引用自己怎麼辦?就用模組名+路徑。
例如,在專案下新建目錄 utils,建立一個tools.go檔案:
package utils
import “fmt”
func PrintText(text string) {
fmt.Println(text)
}
在根目錄下的hello.go檔案就可以 import “hello/utils” 引用utils
package main
import (
"hello/utils"
"github.com/astaxie/beego"
)
func main() {
utils.PrintText("Hi")
beego.Run()
}
問題五:以前老專案如何用新的包管理
- 如果用auto模式,把專案移動到
$GOPATH/src
外 - 進入目錄,執行
go mod init + 模組名稱
go build
或者go run
一次
關於Go 1.12的包管理介紹大致就到此了
根據官方的說法,從Go 1.13開始,模組管理模式將是Go語言開發的預設模式。
所以,Pick起來吧!
有問題或者需要討論的朋友,可以給我留言,共同學習,一起進步