問題描述
vscode 無法以 run 模式運(yùn)行 go 項(xiàng)目(只能以 debug 模式調(diào)試),并且有如下報(bào)錯(cuò)。

圖中被遮蓋的部分是項(xiàng)目內(nèi)的 package,并非第三方 package,也就是說在以 run 模式運(yùn)行 go 項(xiàng)目時(shí)無法找到其他的 go 文件,只能找到入口文件。
初步排查
找不到其他文件,首先想到的是 GO_PATH 的問題,但是項(xiàng)目使用了 go mod,允許在 GO_PATH 之外的路徑創(chuàng)建項(xiàng)目,所以這個(gè)懷疑點(diǎn)排除。接下來懷疑 vscode 的配置有問題,每個(gè) vscode 項(xiàng)目中都有 .launch.json 文件,配置運(yùn)行代碼時(shí)的環(huán)境,下面是項(xiàng)目中的 .launch.json。
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceRoot}/src/main.go",
"env": {},
"args": []
}
]
}
可以看到 .launch.json 里沒有指定程序的工作目錄,debug 模式和 run 模式會(huì)不會(huì)默認(rèn)的工作路徑不同呢?于是在 main 函數(shù)里使用 os.Getwd() 打印一下當(dāng)前的路徑,結(jié)果如下:
-
debug模式:項(xiàng)目所在目錄 -
run模式:用戶目錄
基本可以確認(rèn),run 模式下的工作路徑設(shè)置不正確,導(dǎo)致找不到路徑。在 .launch.json 中加入 cwd 參數(shù),手動(dòng)填入項(xiàng)目路徑。
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceRoot}/src/main.go",
"cwd": "${workspaceRoot}",
"env": {},
"args": []
}
]
}
但是修改 .launch.json 后運(yùn)行程序,輸出的工作目錄仍然是用戶目錄,cwd 參數(shù)并沒有生效。
探究 vscode 的 debug 流程
至此,bug 的氣息越來越濃厚,cwd 參數(shù)沒有生效,肯定有問題!
一不做二不休,索性看看 vscode 的調(diào)試流程吧,用一個(gè)很暴力的方式,看看點(diǎn)擊運(yùn)行按鈕后,vscode 到底是如何運(yùn)行 go 程序的。
package main
import "time"
func main() {
time.Sleep(10000000000)
}
運(yùn)行程序后,使用 ps -ef|grep go 查看進(jìn)程。

截圖中三個(gè)進(jìn)程從上到下均是父子關(guān)系,也就是說在 vscode 中即便使用 run 模式運(yùn)行,也不是直接執(zhí)行 go run xxxx.go,這與 Goland 等其他 IDE 的行為是不同的。vscode 首先調(diào)用了 language server 中的 node,執(zhí)行了 go extention(vscode 的 go 擴(kuò)展,安裝后才支持 go 語言項(xiàng)目)中的一個(gè) goDebug.js,而后 goDebug.js 中調(diào)用了 go run xxxx.go。(圖中 /tmp 路徑下的 main 文件是 go run 執(zhí)行過程中生成的二進(jìn)制文件)
接下來查看 goDebug.js 的邏輯,找到了調(diào)用 go run 的代碼。
this.debugProcess = spawn(getBinPathWithPreferredGopath('go', []), runArgs, { env });
查看代碼上面幾行的邏輯,根據(jù)參數(shù)的命名,可以猜測出來,.launch.json 中的配置在這里是可以獲取到的。接下來直接修改 js 文件,進(jìn)行調(diào)試,證實(shí)上述的猜測,由于我們無法直接看到 node goDebug.js 的輸出,所以通過寫入文件的方式進(jìn)行調(diào)試。
fs.writeFile('test.log', this.debugProcess.cwd(), function (err) {}
加入這句后再次運(yùn)行,我們可以看到 test.log 文件中已經(jīng)打印出了這個(gè)進(jìn)程的工作路徑,也就是 go run 的工作路徑,是用戶目錄。至此,可以將問題縮小到:在 node 調(diào)用 go run 時(shí)沒有將 .launch.json 文件中的 cwd 傳給子進(jìn)程(go run)。
spawn 是 nodejs 中的函數(shù),看一下 spawn 的文檔可以發(fā)現(xiàn),spawn 有三個(gè)參數(shù) child_process.spawn(command[, args][, options]) 第三個(gè)參數(shù) options 中可以指定 cwd 工作路徑。而 goDebug.js 這段啟動(dòng)子進(jìn)程的代碼并沒有設(shè)置 cwd,只設(shè)置了env 參數(shù),這就是 run 模式無法運(yùn)行 go 程序的原因。

解決方案
在發(fā)現(xiàn)這個(gè)問題時(shí),vscode go extention的最新版本是0.13,這個(gè)問題暫時(shí)只能通過修改 goDebug.js 的源碼解決,如下圖所示加入注釋中的代碼,將 cwd 參數(shù)傳入子進(jìn)程,就可以解決問題。

同時(shí),這個(gè) bug 已經(jīng)被解決,可以參考 ISSUE #3096,程序員在解決另一個(gè)問題這個(gè) ISSUE 的問題時(shí),“順手”把 cwd 的問題修復(fù)了。在 vscode go extention 0.14版發(fā)布后(已發(fā)布),將 go extension 更新到最新版就可以正常運(yùn)行和調(diào)試 go 項(xiàng)目了。
參考資料
Debugging in Visual Studio Code