問題描述
vscode 無法以 run
模式運行 go 項目(只能以 debug
模式調試),並且有如下報錯。
圖中被遮蓋的部分是項目內的 package,並非第三方 package,也就是說在以 run
模式運行 go 項目時無法找到其他的 go 文件,只能找到入口文件。
初步排查
找不到其他文件,首先想到的是 GO_PATH 的問題,但是項目使用了 go mod,允許在 GO_PATH 之外的路徑創建項目,所以這個懷疑點排除。接下來懷疑 vscode 的配置有問題,每個 vscode 項目中都有 .launch.json 文件,配置運行代碼時的環境,下面是項目中的 .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
模式會不會默認的工作路徑不同呢?於是在 main 函數裏使用 os.Getwd()
打印一下當前的路徑,結果如下:
-
debug
模式:項目所在目錄 -
run
模式:用戶目錄
基本可以確認,run
模式下的工作路徑設置不正確,導致找不到路徑。在 .launch.json 中加入 cwd
參數,手動填入項目路徑。
{
// 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 後運行程序,輸出的工作目錄仍然是用戶目錄,cwd
參數並沒有生效。
探究 vscode 的 debug 流程
至此,bug 的氣息越來越濃厚,cwd
參數沒有生效,肯定有問題!
一不做二不休,索性看看 vscode 的調試流程吧,用一個很暴力的方式,看看點擊運行按鈕後,vscode 到底是如何運行 go 程序的。
package main
import "time"
func main() {
time.Sleep(10000000000)
}
運行程序後,使用 ps -ef|grep go
查看進程。
截圖中三個進程從上到下均是父子關係,也就是說在 vscode 中即便使用 run
模式運行,也不是直接執行 go run xxxx.go
,這與 Goland 等其他 IDE 的行爲是不同的。vscode 首先調用了 language server 中的 node,執行了 go extention(vscode 的 go 擴展,安裝後才支持 go 語言項目)中的一個 goDebug.js
,而後 goDebug.js
中調用了 go run xxxx.go
。(圖中 /tmp 路徑下的 main 文件是 go run 執行過程中生成的二進制文件)
接下來查看 goDebug.js
的邏輯,找到了調用 go run
的代碼。
this.debugProcess = spawn(getBinPathWithPreferredGopath('go', []), runArgs, { env });
查看代碼上面幾行的邏輯,根據參數的命名,可以猜測出來,.launch.json 中的配置在這裏是可以獲取到的。接下來直接修改 js 文件,進行調試,證實上述的猜測,由於我們無法直接看到 node goDebug.js 的輸出,所以通過寫入文件的方式進行調試。
fs.writeFile('test.log', this.debugProcess.cwd(), function (err) {}
加入這句後再次運行,我們可以看到 test.log 文件中已經打印出了這個進程的工作路徑,也就是 go run 的工作路徑,是用戶目錄。至此,可以將問題縮小到:在 node 調用 go run 時沒有將 .launch.json 文件中的 cwd 傳給子進程(go run)。
spawn
是 nodejs 中的函數,看一下 spawn 的文檔可以發現,spawn 有三個參數 child_process.spawn(command[, args][, options])
第三個參數 options 中可以指定 cwd 工作路徑。而 goDebug.js
這段啓動子進程的代碼並沒有設置 cwd,只設置了env
參數,這就是 run
模式無法運行 go 程序的原因。
解決方案
在發現這個問題時,vscode go extention的最新版本是0.13,這個問題暫時只能通過修改 goDebug.js 的源碼解決,如下圖所示加入註釋中的代碼,將 cwd
參數傳入子進程,就可以解決問題。
同時,這個 bug 已經被解決,可以參考 ISSUE #3096,程序員在解決另一個問題這個 ISSUE 的問題時,“順手”把 cwd
的問題修復了。在 vscode go extention 0.14版發佈後(已發佈),將 go extension 更新到最新版就可以正常運行和調試 go 項目了。
參考資料
Debugging in Visual Studio Code