Golang - GOROOT、GOPATH、Go Modules 三者的關係介紹
文章推薦指數: 80 %
相信初學Golang 語言的時候,一定會對GOROOT、GOPATH 感到疑惑,究竟它們的差別在哪裡?而現在Golang 語言版本已經來到1.13 Version,在1.11 Version ...
0%
相信初學Golang語言的時候,一定會對GOROOT、GOPATH感到疑惑,究竟它們的差別在哪裡?而現在Golang語言版本已經來到1.13Version,在1.11Version新增了一個重大的功能那就是GoModules,所以三者的差別是需要好好釐清的。
GOROOT介紹
在安裝完Golang語言的時候,所謂的安裝路徑其實就是你的GOROOT路徑,也就是說GOROOT存放的Golang語言內建的程式庫的所在位置,而通常你安裝完後,你電腦的環境變數就會設好GOROOT路徑,當你開發Golang程式的時候,當你import內建的程式庫的時候,並不需要額外安裝,而當程式運行後,預設也會先去GOROOT路徑下尋找相對應的程式庫來運行。
首先我們先看目前GOROOT路徑,透過以下指令:
1goenv
這樣可以看到當前有關Golang語言的全部相關環境變數設定,如下:
1234...setGOPATH=C:\Users\kenny\gosetGOROOT=c:\go...
通常如果你是初次安裝Golang語言並且沒做什麼環境變數設定的話,GOROOT設定路徑就是你當初安裝Golang語言的路徑,而GOPATH通常預設會是使用者目錄下的go資料夾。
那假設先在隨便的資料夾目錄下新增main.go檔案,程式碼內容如下:
1234567packagemainimport"fmt"funcmain(){ fmt.Println("HelloWorld")}
然後再執行以下指令:
1gorunmain.go
就會成功輸出HelloWorld的字樣。
gorun其實會幫你將程式碼進行編譯並產生執行檔,而編譯檔跟執行檔事實上是存在一個暫存資料夾裡面,當運行完此程式就會自動刪除。
該指令可以想成類似直譯的方式運行,而不需要做其他任何環境設定,即可運行。
那如果我們將程式碼改成這樣:
12345678packagemainimport"github.com/gin-gonic/gin"funcmain(){ router:=gin.Default() router.Run()}
可以注意到import了一個github.com/gin-gonic/gin套件,這個是別人開發的WebFramework套件,是不存在於官方程式庫的,而是放在GitHub上的第三方套件。
那再執行這份程式碼,會出現以下的錯誤:
123main.go:3:8:cannotfindpackage"github.com/gin-gonic/gin"inanyof:c:\go\src\github.com\gin-gonic\gin(from$GOROOT)C:\Users\kenny\go\src\github.com\gin-gonic\gin(from$GOPATH)
從這個訊息可以看出兩件事情:
當執行Golang程式碼,當需要存取套件時,會先去GOROOT路徑下的src資料夾找同等於我們在程式碼中import的路徑下去找有沒有gin這個資料夾,而這資料夾裡面就是包含了所有有關於該套件的程式庫。
如果在GOROOT路徑下沒有找到,則會往GOPATH路徑下的src資料夾找同等於我們在程式碼中import的路徑下去找有沒有gin這個資料夾。
所以只要GOROOT跟GOPATH路徑下都沒有找到該套件的話,就無法執行該程式碼。
GOPATH介紹
根據上面GOROOT的介紹,我們可以知道官方的程式庫所在位置就是在GOROOT裡面,而GOPATH就是專門存放第三方套件以供我們程式碼的需要。
那通常開發Golang的話,通常會在重新設定GOPATH的位置,例如像我習慣把我所有不同程式語言的專案都統一放在一個資料夾下,在去用語言去分類專案,所以這時候就需要設定GOPATH路徑。
採用指令如下:
1setGOPATH=
而我們要在GOPATH路徑下主動新增src資料夾,所謂src就是代表sourcecode的意思,也就是放我們開發Golang程式碼的相關專案的原始碼。
至於pkg、bin兩個資料夾是做什麼用的之後會提到。
所以一般開發上,會在src下創立專案資料夾,然後在專案資料夾在去新增package資料夾,在去寫相關的go語言程式碼。
例如實際上架構就會像是:
123D:\CodingProject\GolangProject\src\webDemo--->controller--->model--->main.go
假設是web專案的話,在controller跟model有你所設計的程式碼,而main.go就是所謂的主程式入口,裡面定義了mainfunction。
這樣就能透過gorunmain.go來啟動整個專案。
gorun、gobuild、goinstall的差別
前面我們都只有提到用gorun來執行程式碼,但其實還有所謂的gobuild、goinstall等指令可以執行程式碼。
gorun介紹
gorun指令簡單來說就像是直譯的方式來執行程式碼,而在細節上它其實會將程式碼進行編譯,最後產生可執行檔案,然後運行該可執行檔案,產生程式執行後的結果,而可執行檔會放在特定的暫存檔裡面,執行完就會自動刪除了。
通常開發上就是用於假設只是要測試一段程式碼,那就會用gorun來執行。
gobuild介紹
gobuild指令就是將程式碼編譯為執行檔,例如:
1gobuildmain.go
這樣會在main.go當前目錄下產生一個main名稱的可執行檔,如果是Windows的話就是main.exe。
也可以在build後面寫專案的名稱,那麼它會自動在當前專案資料夾下找package為main並且有mainfunction的go檔案,在編譯成可執行檔。
例如:
1gobuildwebDemo
但記得如果把mainfunction的go檔案放置在webDemo資料夾下的另一個package裡面的話,會讀取不到。
也可以透過以下指令指定build出來的可執行檔位置放置在哪:
1gobuild-obin/main.exesrc/webDemo/main.go
-o後面第一個參數代表產生出來執行檔路徑要放哪及名稱要取什麼,第二個參數代表要編譯哪一個go程式碼的路徑。
從這邊就可以知道為什麼我們在GOPATH下要新增一個bin資料夾,依照官方慣例,GOPATH下會有一個bin資料夾專門就是放專案build出來的可執行檔。
但是gobuild有個缺點就是每次編譯程式碼,比較沒有效率,當專案架構越來越大,build的速度也就越來越慢。
goinstall介紹
因應gobuild的缺點,因此有goinstall指令,goinstall可以做兩件事情:
將套件編譯成.afile
如果是main套件則編譯成可執行檔
而有了第一項功能,在下次編譯的時候,就不會將套件的程式碼重新編譯,而是直接用編譯好的.afile。
而.afile就位於GOPATH/pkg裡面。
這就是為什麼golangGO慣例要在GOPATH下新增此三個資料夾,都是有用處的。
來看例子:
假設在src下新增兩個資料夾分別是mypkg跟mytest,而mypkg下新增一個mypkg.go檔案,程式碼如下:
1234567packagemypkgimport"fmt"funcMyFunc(){ fmt.Println("MyFunc")}
在mytest下新增一個mytest.go檔案,程式碼如下:
123456789packagemainimport( "mypkg")funcmain(){mypkg.MyFunc()}
意思就是在mytest.go檔案中用了mypkg套件的相關函式。
而可以透過goinstall指令先將mypkg編譯成.afile;
1goinstallmypkg
以上是在src目錄下執行這個指令,也可以在mypkg下執行以下指令:
1goinstall
就會發現在GOPATH/pkg/windows_amd64/mypkg.a有該檔案。
windows_amd64其實就是由GOOS_GOARCH這兩個環境變數所組合而成的,執行goenv指令就可以看到,通常安裝golang語言時就會根據作業系統設定好名稱了。
特別注意:
goinstall如果要在非GOPATH路徑下使用的話,要先設定GOBIN環境變數,否則會出現錯誤
通常GOBIN環境變數就是設定GOPATH/bin。
GOPATH的缺點
講完了GOROOT、GOPATH,不知道大家有沒有發現GOPATH的一個很大的缺點,那就是你相關的第三方套件只要不是官方程式庫,都需要放置在GOPATH/src的路徑下才可以使用。
也就是說通常我們會使用goget指令來獲取網路上的第三方套件,例如:
1gogetgithub.com/gin-gonic/gin
goget最常用在當我們想用別人公開在GitHub上的套件,可以幫我們從網路上clone到GOPATH/src裡面。
雖然這樣很方便,但是你會發現GOPATH/src下的程式碼會很複雜,除了有你自己開發的專案資料夾,也包含其他第三方程式庫的專案資料夾。
再來,如果你開發的專案採用第三方套件是不同版本怎麼辦?以往的解決方法是要設定多組不同的GOPATH。
雖然社群也有開發相對應的packagemanager,如Vendor、Dep來解決該問題,但畢竟不是官方主導的。
GoModules的誕生
為了解決不被GOPATH的問題,因此官方在1.11開始推出了GoModules的功能。
GoModules解決方式很像是Java看到Maven的做法,將第三方程式庫儲存在本地的空間,並且給程式去引用。
首先要先設定GO111MODULE環境變數,總共可以三種不同的值:
auto
默認值,go命令會根據當前目錄来决定是否啟用modules功能。
需要滿足兩種情形:
該專案目錄不在GOPATH/src/下
當前或上一層目錄存在go.mod檔案
on
go命令會使用modules,而不會GOPATH目錄下查找。
off
go命令將不會支持module功能,尋找套件如以前GOPATH的做法去尋找。
我是建議要開發Go專案就不再使用GOPATH的功能了,而是採用GoModules的做法,因此建議都設定為on。
而採用GoModules,下載下來的第三方套件都在哪呢?其實就位在GOPATH/pkg/mod資料夾裡面。
來看個例子:
在GOPATH/src下建立一個專案資料夾:
1mkdirmodtest
然後在該資料夾下執行以下指令:
1gomodinit
執行之後可以看到會出現一個go.mod檔案,這檔案內容如下:
123modulemodtestgo1.13
在此檔案內可以寫以下幾個關鍵字:
module
定義模組路徑
go
定義go語言version
require
指定依賴的套件,預設是最新版,可以指定版本號
exclude
排除該套件和其版本
replace
使用不同的套件版本並替換原有的套件版本
註解
//單行註解
/*多行註解*/
indirect代表被間接導入的依賴包
假設現在我要引入GitHub上的gin-gonic/gin的套件,如下定義:
12345modulemodtestgo1.13requiregithub.com/gin-gonic/ginv1.5.0
再執行以下指令:
1gomoddownload
會將需要的套件安裝在GOPATH/pkg/mod資料夾裡面。
而且會發現出現一個go.sum的檔案,這個檔案基本上用來記錄套件版本的關係,確保是正確的,是不太需要理會的。
因此當下載完之後就可以在Go程式碼中引用囉:
12345678packagemainimport"github.com/gin-gonic/gin"funcmain(){router:=gin.Default()router.Run()}
這個就是在local8080port開啟一個webserver~
如果將gin版本改成v1.4.0再重新執行gomoddownload,就會發現在GOPATH/pkg/mod裡面gin-gonic會有兩個資料夾分別是[email protected]、[email protected],藉此來區分版本。
當然其實也可以不執行gomoddownload,而直接運行gobuildorgoinstall也會自動將套件安裝在相對應的地方。
還有一種方式直接下載套件而不用在go.mod中定義:
1gogetgithub.com/gin-gonic/[email protected]
只要有開啟gomodules功能,goget就不會像以前一樣在GOPATH/src下放置套件檔案,而是會放在GOPATH/pkg/mod裡面,並且go.mod會寫好引入。
這樣也就不用使用gomoddownload指令了~
總結
GoModules當然還有其他指令可以去玩玩,但就開發Go語言知道這些就差不多了,其他等有遇到在去看指令也可以,簡單來說GoModules是Golang套件管理方式,這樣的方式也相對新手也比較好理解,我一開始碰Golang的時候,也被這些環境變數搞得很亂,畢竟之前設定環境既不像Java、Python、Node.JS那樣,會讓人感到難以上手,畢竟Golang語言當初設計是在Google大公司的情境,並且採用同一個Repository的方式去開發專案的。
我個人很喜歡Golang語言的,我大學大部分碰的靜態語言都是Java,雖然我還是滿喜歡Java,但Java有些時候令人詬病的物件開發方式,確實很煩人,而接觸Golang語言的時候,大部分是採用函數式開發,儘管Golang可以設計一些類似物件的方式,但是個人認為如果將兩者合在一起使用,其實是滿不錯的~
最讚的是Golang是專門開發ServerSide的語言,特別合我的胃口,而且背後是Google大公司當親爹,以及一堆大公司正在積極地採用Golang開發新專案,或是重構舊專案來提升效能。
其實我覺得程式設計師,要會的語言不管是動態或靜態最好都要會各兩種,同時感受不同語言的奧妙之處,在不同情境下用不同語言,有時候還會迸出新想法呢~^^
最後最後!請聽我一言!
如果你還沒有註冊LikeCoin,你可以在文章最下方看到Like的按鈕,點下去後即可申請帳號,透過申請帳號後可以幫我的文章按下Like,而Like最多可以點五次,而你不用付出任何一塊錢,就能給我寫這篇文章的最大的回饋!
相關文章
ConcurrencyinGo書本-AnIntroductiontoConcurrency筆記
Golang-Singleton實現方式探討
Golang-errGroup用法及適用情境
Golang-常見的option設計探討
Golang-深入理解interface常見用法
延伸文章資訊
- 1[Golang] Modules and Packages | PJCHENder 未整理筆記
How to Write Go Code @ golang. ... 若我們在go module 中有使用其他的遠端(第三方)套件,當執行 go install 、 go build 或 go ...
- 2Day20-Go modules - iT 邦幫忙
Go Phishing!30 天用Go 實作Reverse Proxy 之釣魚大作戰系列第20 篇 ... Go modules 是Go 從1.11 版開始提供的套件管理工具,就像Nodejs ...
- 3從一知半解到略懂Go modules
這次花了些時間看了關於go modules 的相關文件,並實際寫個小範例體驗,最後整理成本文分享。 本文環境. macOS 10.15; Go 1.13. 引言. 先前Golang - 從 ...
- 4Golang - GOROOT、GOPATH、Go Modules 三者的關係介紹
相信初學Golang 語言的時候,一定會對GOROOT、GOPATH 感到疑惑,究竟它們的差別在哪裡?而現在Golang 語言版本已經來到1.13 Version,在1.11 Version ...
- 5【Golang】還在把library 放在專案裡?該跟上使用Go Module ...
本篇先以初始專案視角介紹,如果要從舊的轉變成Go Module 請參考:Migrate to go module。 這個會將go.mod 定義的library 都 ... 以下為簡易教學. 首先...