Go專案工程管理及目錄結構
在Go
的官網文件How to Write Go Code中,已經介紹了Go
的專案目錄一般包含以下幾個:
- src 包含專案的原始碼檔案;
- pkg 包含編譯後生成的包/庫檔案;
- bin 包含編譯後生成的可執行檔案。
可以通過下面的例子來說明工程目錄的組織管理。(Windows 7 64位,go version go1.3.3 windows/amd64)
1. 建立一個庫檔案
建立一個庫檔案a.go
並儲存在scr目錄的一個子目錄下面。
package myfunc import "fmt" func Afunc(str string) { fmt.Println("a.go is package mufunc.") fmt.Println(str) }
這時候目錄結構如下:
<dirtest>
|--<src>
|--<mufunc>
|--a.go
2. 建立main
關於main包的位置,可以參照參考資料2,個人建議放在scr/main
下面,畢竟官方推薦包名和檔案存放的資料夾名稱最好相同(雖然包名和資料夾名可以不相同,也就是說一個資料夾下可以包含多個包的.go
檔案)。
package main
import "myfunc"
func main() {
myfunc.Afunc("b.go is package main.")
}
這時候目錄結構如下:
<dirtest>
|--<src>
|--<mufunc>
|--a.go
|--<main>
|--b.go
3. 使用go build
如果這時候使用go build
,你會發現下面的輸出:
E:\dirtest>go build src\main\b.go src\main\b.go:3:8: cannot find package "myfunc" in any of: C:\Program Files\go\src\pkg\myfunc (from $GOROOT) D:\GoLang\src\myfunc (from $GOPATH)
從輸出中我們可以看到,Go
先是從$GOROOT
中查詢包myfunc
,如果沒找到就從$GOPATH
中查詢,結果都沒有找到,我們可以使用go env
輸出Go
的環境變數設定:
E:\dirtest>go env
set GOARCH=amd64
set GOBIN=
set GOCHAR=6
set GOEXE=.exe
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOOS=windows
set GOPATH=D:\GoLang
set GORACE=
set GOROOT=C:\Program Files\go
set GOTOOLDIR=C:\Program Files\go\pkg\tool\windows_amd64
set CC=gcc
set GOGCCFLAGS=-m64 -mthreads -fmessage-length=0
set CXX=g++
set CGO_ENABLED=1
顯然E:\dirtest
這個目錄沒有加到$GOPATH
中,在環境變數中新增該目錄:
儲存後,重新執行(可能需要重新開啟控制檯,讓環境變數生效)go build
,就在當前目錄生成了一個可執行檔案b.exe
。
E:\dirtest\src\main>go env
set GOARCH=amd64
set GOBIN=
set GOCHAR=6
set GOEXE=.exe
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOOS=windows
set GOPATH=D:\GoLang;E:\dirtest
set GORACE=
set GOROOT=C:\Program Files\go
set GOTOOLDIR=C:\Program Files\go\pkg\tool\windows_amd64
set CC=gcc
set GOGCCFLAGS=-m64 -mthreads -fmessage-length=0
set CXX=g++
set CGO_ENABLED=1
E:\dirtest>go build src\main\b.go
E:\dirtest>dir
E:\dirtest 的目錄
2015/01/13 23:11 <DIR> .
2015/01/13 23:11 <DIR> ..
2015/01/13 23:11 1,958,912 b.exe
2015/01/13 22:52 <DIR> src
E:\dirtest>b.exe
a.go is package mufunc.
b.go is package main.
雖然成功執行,但是沒有按照期待的那樣生成在bin目錄下面。為了達到這樣的效果,你需要go install
。注意go install
是針對package,而不是針對單個.go
檔案。
但是如果是當前狀態執行go install
,雖然可以成功,但你會發現,並沒有在專案根目錄E:\dirtest
中建立bin\main.exe
,反而是在D:\GoLang
中建立了。
如果對main包執行go install
呢?
E:\dirtest\src\main>go install
go install: no install location for E:\dirtest\src\main: hidden by D:\GoLang\src
\main
可以看到,輸出提示當前目錄被隱藏。顯然這個順序是對應$GOPATH
的設定的,把$GOPATH
中的路徑順序改一下:
然後在執行go install myfunc
,發現成功地在pkg目錄下面生成了myfunc.a
。同樣執行go install main
,也成功的在bin目錄下生成了main.exe
。此時的目錄結構如下:
<dirtest>
|--<src>
|--<mufunc>
|--a.go
|--<main>
|--b.go
|--<pkg>
|--<windows_amd64>
|--myfunc.a
|--<bin>
|--main.exe
現在就算是成功完成了一個示例“專案”吧...
4. 常見錯誤
除了上面的步驟中出現的錯誤,其實工程目錄管理稍有不慎,就會出現其他問題,例如:
1. 一個資料夾下面包含多個不同包的原始檔。也就是把a.go
和b.go
都放到myfunc
目錄下面會是什麼情況呢?
這時候的目錄結構如下:
<dirtest>
|--<src>
|--<mufunc>
|--a.go
|--b.go
那麼執行go install
和go build
,甚至go run
都會是相同的錯誤:
E:\dirtest\src\myfunc>go install
can't load package: package myfunc: found packages myfunc (a.go) and main (b.go)
in E:\dirtest\src\myfunc
E:\dirtest\src\myfunc>go build
can't load package: package myfunc: found packages myfunc (a.go) and main (b.go)
in E:\dirtest\src\myfunc
E:\dirtest\src\myfunc>go run b.go
b.go:3:8: found packages myfunc (a.go) and main (b.go) in E:\dirtest\src\myfunc
從參考資料3中可以看到,每個子目錄中只能存在一個package,否則編譯時會報錯,所以一個子目錄下面不能包含多個不同包的原始檔。
2. 一個專案能包含多個main()嗎?
簡單測試下,建立一個c.go
,並使用myfunc
包(沒有匯入其他包的情況類似):
package main
import "fmt"
import "myfunc"
func main() {
fmt.Println("This is single c.go")
myfunc.Afunc("c.go is also package main.")
}
執行相應的命令,結果如下:
E:\dirtest\src\main>go build
# main
.\c.go:4: main redeclared in this block
previous declaration at .\b.go:5
E:\dirtest\src\main>go build c.go
# 成功,當前目錄下生成了c.exe
E:\dirtest\src\main>go install
# main
.\c.go:4: main redeclared in this block
previous declaration at .\b.go:5
E:\dirtest\src\main>go install c.go
go install: no install location for .go files listed on command line (GOBIN not
set)
E:\dirtest\src\main>go run c.go
This is single c.go
a.go is package mufunc.
c.go is also package main.
顯然只能是go run
和go build c.go
可行。如果把c.go
移到單獨的目錄下面呢:
E:\dirtest\src\cmain>dir
E:\dirtest\src\cmain 的目錄
2015/01/14 11:27 <DIR> .
2015/01/14 11:27 <DIR> ..
2015/01/14 11:24 147 c.go
E:\dirtest\src\cmain>go build
E:\dirtest\src\cmain>go install
均可以執行成功。go install
在bin目錄下面生成了對應的exe
檔案。看來還是目錄管理的問題。
3. go install: no install location for .go files listed on command line (GOBIN not set)
從上面的示例輸出中就能看到,使用go install
針對單個檔案時,就會出現這個錯誤。預設情況下如果設定了$GOROOT
和$GOPATH
,就會依次尋找$GOROOT/bin
和$GOPATH/bin
。那麼我們如果自定義設定了$GOBIN=E:\dirtest\bin
之後會怎樣?
E:\dirtest\src\cmain>go env
set GOARCH=amd64
set GOBIN=E:\dirtest\bin
set GOCHAR=6
set GOEXE=.exe
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOOS=windows
set GOPATH=E:\dirtest;D:\GoLang
set GORACE=
set GOROOT=C:\Program Files\go
set GOTOOLDIR=C:\Program Files\go\pkg\tool\windows_amd64
set CC=gcc
set GOGCCFLAGS=-m64 -mthreads -fmessage-length=0
set CXX=g++
set CGO_ENABLED=1
E:\dirtest\src\cmain>go install c.go
# 成功在 E:\dirtest\bin 下面生成 c.exe
雖然成功了,但是go install
應該是作用於包級別,而非單個檔案。
4. go build、go install 和 go run 的區別
詳細的可以檢視參考資料4,這裡簡單說一下:
go build
編譯包,如果是main
包則在當前目錄生成可執行檔案,其他包不會生成.a
檔案;go install
編譯包,同時複製結果到$GOPATH/bin
,$GOPATH/pkg
等對應目錄下;go run gofiles...
編譯列出的檔案,並生成可執行檔案然後執行。注意只能用於main
包,否則會出現go run: cannot run non-main package
的錯誤。
此外,go run
是不需要設定$GOPATH
的,但go build
和go install
必須設定。go run
常用來測試一些功能,這些程式碼一般不包含在最終的專案中。
5. 總結
- 一定要管理好目錄
- 多個專案最好都在一個
$GOPATH
下面,即src/proj1
,src/proj2
,etc - 儘量使用
go install
,這樣能夠規範專案整體結構