golang的包管理是一直是為人詬病之處,從golang1.5引入的vendor機制,到准官方工具dep,目前為止還沒一個簡便的解決方案。
不過現在go modules隨著golang1.11的發布而和我們見面了,這是官方提倡的新的包管理,乃至專案管理機制,可以不再需要GOPATH的存在。
go modules的初始化
現在modules機制仍在早期階段,所以golang提供了一個環境變數“GO111MODULE”,預設值為auto,如果目前的目錄裡有go.mod檔案,就使用go modules,否則使用舊的GOPATH和vendor機制,因為在modules機制下go get只會下載go modules,這一行為會在以後版本中成為預設值,這裡我們保持auto即可,如果你想直接使用modules而不需要從GOPATH過度,那麼把“GO111MODULE”設定為on。
modules和傳統的GOPATH不同,不需要包含例如src,bin這樣的子目錄,一個原始碼目錄甚至是空目錄都可以作為module,只要其中包含有go.mod檔案。
我們就用一個空目錄來建立我們的第一個module:
要初始化modules,需要使用如下命令(假設已經安裝配置好golang1.11):
go mod init [module name]
我們的module叫test,所以就是:
go mod init test
初始完成後會在目錄下產生一個go.mod檔案,裡面的內容只有一行“module test”。
包管理
那麼我們怎麼進行包管理呢?別擔心,當我們使用go build,go test以及go list時,go會自動得更新go.mod檔案,將依賴關係寫入其中。
如果你想手動處理依賴關係,那麼使用如下的命令:
go mod tidy
這條命令會自動更新依賴關係,並且將包下載放入cache。
下面我們使用chromedp的一個簡單example作為實驗代碼main.go,看下go modules是如何處理包的依賴關係的。
我們手動運行go mod tidy:
尋找並下載包
我們發現多了一個go.sum,我們看看它裡面是什麼內容:
沒錯,你已經猜到了,這是我們直接引用的package和它自身需要的以來的版本記錄,go modules就是根據這些去找到需要的packages的。
順帶一提,如果我們不做任何修改,預設會使用最新的包版本,如果包打過tag,那麼就會使用最新的那個tag對應的版本。
下面我們使用go build來編譯我們的代碼:
go build
值得注意的是,新增了一個編譯選項“-mod”,它有如下的可選值:
go build -mod=readonly
在這個模式下任何會導致依賴關係變動的情況都將導致build失敗,前面提到過build能尋找並更新依賴關係,使用這個選項可以檢查依賴關係的變動。
go build -mod=vendor
意思是忽略cache裡的包,只使用vendor目錄裡的版本。
構建完成後目錄結構如下:
我們的代碼成功構建了,包管理都由go modules替我們完成了。
包的版本控制
包管理的另外一項重要功能就是包的版本控制。modules同樣可以做到。
在介紹版本控制之前,我們要先明確一點,如果上層目錄和下層目錄的go.mod裡有相同的package規則,那麼上層目錄的無條件覆蓋下層目錄,目的是為了main module的構建不會被依賴的package所影響。
那麼我們看看go.mod長什麼樣:
module testrequire github.com/chromedp/chromedp v0.1.2
如果有多個依賴,可以是這樣的:
module github.com/chromedp/chromedprequire (github.com/chromedp/cdproto v0.0.0-20180713053126-e314dc107013github.com/disintegration/imaging v1.4.2github.com/gorilla/websocket v1.2.0github.com/knq/sysutil v0.0.0-20180306023629-0218e141a794github.com/mailru/easyjson v0.0.0-20180606163543-3fdea8d05856golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81)
前面部分是包的名字,也就是import時需要寫的部分,而空格之後的是版本號碼,版本號碼遵循如下規律:
vX.Y.Z-pre.0.yyyymmddhhmmss-abcdefabcdefvX.0.0-yyyymmddhhmmss-abcdefabcdefvX.Y.(Z+1)-0.yyyymmddhhmmss-abcdefabcdefvX.Y.Z
也就是版本號碼+時間戳記+hash,我們自己指定版本時只需要制定版本號碼即可,沒有版本tag的則需要找到對應commit的時間和hash值。
預設使用最新版本的package。
現在我們要修改依賴關係了,我們想使用chromedp 的v0.1.0版本,怎麼辦呢?
只需要如下命令:
go mod edit -require="github.com/chromedp/chromedp@v0.1.0"
@後面加上你需要的版本號碼。go.mod已經修改了:
module testrequire github.com/chromedp/chromedp v0.1.0
我們還需要讓go modules更新依賴關係,這裡我們手動go mod tidy之後:
注意顏色較深的那兩行,我們已經切換到了v0.1.0版本了。
go modules是一個很大的主題,以後我還將進一步介紹它。
因為go1.11剛發布不久,這篇檔案作為探路,必定會有錯誤和疏漏,歡迎大家指正!