Go 1.18:工作區模式workspace mode

coding進階發表於2022-04-10

背景

Go 1.18除了引入泛型(generics)、模糊測試(Fuzzing)之外,另外一個重大功能是引入了工作區模式(workspace mode)。

工作區模式對本地同時開發多個有依賴的Module的場景非常有用。

舉個例子,我們現在有2個Go module專案處於開發階段,其中一個是example.com/main,另外一個是example.com/util。其中example.com/main這個module需要使用example.com/util這個module裡的函式。

我們來看看Go 1.18版本前後如何處理這種場景。

Go 1.18之前怎麼做

在Go 1.18之前,對於上述場景有2個處理方案:

方案1:被依賴的模組及時提交程式碼到程式碼倉庫

這個方案很好理解,既然example.com/main這個module依賴了example.com/util這個module,那為了example.com/main能使用到example.com/util的最新程式碼,需要做2個事情

  • 本地開發過程中,如果example.com/util有修改,都馬上提交程式碼到程式碼倉庫,然後打tag
  • 緊接著example.com/main更新依賴的example.com/util的版本號(tag),可以使用go get -u命令。

這種方案比較繁瑣,每次example.com/util有修改,都要提交程式碼,否則example.com/main這個module就無法使用到最新的example.com/util

方案2:go.mod裡使用replace指令

為了解決方案1的痛點,於是有了方案2:在go.mod裡使用replace指令。

通過replace指令,我們可以直接使用example.com/util這個module的本地最新程式碼,而不用把example.com/util的程式碼提交到程式碼倉庫。

為了方便大家理解,我們直接上程式碼。程式碼目錄結構如下:

module
|
|------main
|        |---main.go
|        |---go.mod        
|------util
|        |---util.go
|        |---go.mod

main目錄下的main.go程式碼如下:

//main.go
package main

import (
    "fmt"

    "example.com/util"
)

func main() {
    result := util.Add(1, 2)
    fmt.Println(result)
}

main目錄下的go.mod內容如下:

module example.com/main

go 1.16

require example.com/util v1.0.0

replace example.com/util => ../util

util目錄下的util.go程式碼如下:

// util.go
package util

func Add(a int, b int) int {
    return a + b
}

util目錄下的go.mod內容如下:

module example.com/util

go 1.16

這裡最核心的是example.com/main這個module的go.mod,最後一行使用了replace指令。

module example.com/main

go 1.16

require example.com/util v1.0.0

replace example.com/util => ../util

通過replace指令,使用go命令編譯程式碼的時候,會找到本地的util目錄,這樣example.com/main就可以使用到本地最新的example.com/util程式碼。進入main目錄,執行程式碼,結果如下所示:

$ cd main
$ go run main.go
3

但是這種方案也有個問題,我們在提交example.com/main這個module的程式碼到程式碼倉庫時,需要刪除最後的replace指令,否則其他開發者下載後會編譯報錯,因為他們本地可能沒有util目錄,或者util目錄的路徑和你的不一樣。

程式碼開源地址:Go 1.18之前使用replace指令

Go 1.18工作區模式

為了解決方案2的痛點,在Go 1.18裡新增了工作區模式(workspace mode)。

該模式下不再需要在go.mod裡使用replace指令,而是新增一個go.work檔案。

話不多說,直接上程式碼。程式碼目錄結構如下:

workspace
|------go.work
|
|------main
|        |---main.go
|        |---go.mod        
|------util
|        |---util.go
|        |---go.mod

main目錄下的main.go程式碼如下:

//main.go
package main

import (
    "fmt"

    "example.com/util"
)

func main() {
    result := util.Add(1, 2)
    fmt.Println(result)
}

main目錄下的go.mod內容如下:沒有了方案2裡最後一行的replace指令

module example.com/main

go 1.16

require example.com/util v1.0.0

util目錄下的util.go程式碼如下:

// util.go
package util

func Add(a int, b int) int {
    return a + b
}

util目錄下的go.mod內容如下:

module example.com/util

go 1.16

go.work內容如下:

go 1.18

use (
    ./main
    ./util
)

在workspace目錄下執行如下命令即可自動生成go.work

$ go1.18beta1 work init main util

go1.18beta1 work init後面跟的mainutil都是Module對應的目錄。

如果go命令執行的當前目錄或者父目錄有go.work檔案,或者通過GOWORK環境變數指定了go.work的路徑,那go命令就會進入工作區模式。在工作區模式下,go就可以通過go.work下的module路徑找到並使用本地的module程式碼。

在main目錄或者workspace目錄,都可以執行main.go,結果如下所示:

$ go1.18beta1 run main/main.go 
3
$ cd main/
$ go1.18beta1 run main.go
3

這種模式下,我們對example.com/main 沒有任何本地侵入性修改,不用像方案2那樣,提交程式碼前還需要更新go.mod檔案。example.com/main裡的內容都可以直接提交到程式碼倉庫。

程式碼開源地址:Go 1.18工作區模式

注意go.work不需要提交到程式碼倉庫中,僅限於本地開發使用。

總結

為了解決多個有依賴的Module本地同時開發的問題,Go 1.18引入了工作區模式。

工作區模式是對已有的Go Module開發模式的優化,關於工作區模式的更多細節可以參考本文最後的References。

開源地址

文章和示例程式碼開源在GitHub: Go語言初級、中級和高階教程

公眾號:coding進階。關注公眾號可以獲取最新Go面試題和技術棧。

個人網站:Jincheng's Blog

知乎:無忌

References

相關文章