go标准命令详解0.2 go install
搬運自github赫林的go_command_tutorial,絕對干貨,感謝作者。
0.2 go install
命令Go?install用于編譯并安裝指定的代碼包及它們的依賴包。當指定的代碼包的依賴包還沒有被編譯和安裝時,該命令會先去處理依賴包。與go build命令一樣,傳給go install命令的代碼包參數,應該以導入路徑的形式提供。并且,go build命令的絕大多數標記也都可以用于go install命令。實際上,go install命令只比go build命令多做了一件事,即:安裝編譯后的結果文件到指定目錄。
在對go install命令進行詳細說明之前,讓我們先回顧一下goc2p的目錄結構。為了節省篇幅,我們在這里隱藏了代碼包中的源碼文件。如下:
$HOME/golang/goc2p:bin/pkg/src/cnet/logging/helper/ds/pkgtool/- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
我們看到,goc2p項目中有三個子目錄,分別是bin目錄、pkg目錄和src目錄。現在只有src目錄中包含了一些目錄,而其他兩個目錄都是空的。
現在,我們來看看安裝代碼包的規則。
安裝代碼包
如果go install命令后跟的代碼包中僅包含庫源碼文件,那么go install命令會把編譯后的結果文件保存在源碼文件所在工作區的pkg目錄下。對于僅包含庫源碼文件的代碼包來說,這個結果文件就是對應的代碼包歸檔文件。相比之下,我們在使用go build命令對僅包含庫源碼文件的代碼包進行編譯時,是不會在當前工作區的src目錄和pkg目錄下產生任何結果文件的。結果文件會出于編譯的目的被生成在臨時目錄中,但并不會對當前工作區目錄產生任何影響。
如果我們在執行go install命令時不后跟任何代碼包參數,那么命令將試圖編譯當前目錄所對應的代碼包。比如,我們現在要安裝代碼包pkgtool:
hc@ubt:~/golang/goc2p/src/pkgtool$ go install -v -work WORK=D:\cygwin\tmp\go-build758586887 pkgtool- 1
- 2
- 3
- 1
- 2
- 3
我們剛剛說過,執行go install命令后會對指定代碼包先編譯再安裝。其中,編譯代碼包使用了與go build命令相同的程序。所以,執行go install命令后也會首先建立一個名稱以go-build為前綴的臨時目錄。如果我們想強行重新安裝指定代碼包及其依賴包,那么就需要加入標記-a:
hc@ubt:~/golang/goc2p/src/pkgtool$ go install -a -v -work WORK=/tmp/go-build014992994 runtime errors sync/atomic unicode unicode/utf8 sort sync io syscall bytes strings time bufio os path/filepath pkgtool- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
可以看到,代碼包pkgtool僅僅依賴了標準庫中的代碼包。
現在我們再來查看一下goc2p項目目錄:
$HOME/golang/goc2p:bin/pkg/linux_386/pkgtool.asrc/- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
現在pkg目錄中多了一個子目錄。讀過本書第二章的讀者應該已經知道,linux_386被叫做平臺相關目錄。它的名字可以由${GOOS}_${GOARCH}來得到。其中,${GOOS}和${GOARCH}分別是環境變量GOOS和GOARCH的值。上述示例在計算架構為386()且操作系統為Linux的計算機上運行。所以,這里的平臺相關目錄即為linux_386。我們還看到,在goc2p項目中的平臺相關目錄下存在一個文件,名稱是pkgtool.a。這就是代碼包pkgtool的歸檔文件,文件名稱是由代碼包名稱與“.a”后綴組合而來的。
實際上,代碼包的歸檔文件并不都會被保存在pkg目錄的平臺相關目錄下,還可能被保存在這個平臺相關目錄的子目錄下。 下面我們來安裝cnet/ctcp包:
hc@ubt:~/golang/goc2p/src/pkgtool$ go install -a -v -work ../cnet/ctcp WORK=/tmp/go-build083178213 runtime errors sync/atomic unicode unicode/utf8 math sort sync io syscall bytes strings bufio time strconv math/rand os reflect fmt log runtime/cgo logging net cnet/ctcp- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
請注意,我們是在代碼包pkgtool對應的目錄下安裝cnet/ctcp包的。我們使用了一個目錄相對路徑。
實際上,這種提供代碼包位置的方式被叫做本地代碼包路徑方式,也是被所有Go命令接受的一種方式,這包括之前已經介紹過的go build命令。但是需要注意的是,本地代碼包路徑只能以目錄相對路徑的形式呈現,而不能使用目錄絕對路徑。請看下面的示例:
hc@ubt:~/golang/goc2p/src/cnet/ctcp$ go install -v -work ~/golang/goc2p/src/cnet/ctcp can't load package: package /home/hc/golang/goc2p/src/cnet/ctcp: import "/home/hc/golang/goc2p/src/cnet/ctcp": cannot import absolute path- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
從上述示例中的命令提示信息我們可以看到,以目錄絕對路徑的形式提供代碼包位置是不會被Go命令認可的。
這是由于Go認為本地代碼包路徑的表示只能以“./”或“../”開始,再或者直接為“.”或“..”。而代碼包的代碼導入路徑又不允許以“/”開始。所以,這種用絕對路徑表示代碼包位置的方式也就不被支持了。
上述規則適用于所有Go命令。讀者可以自己嘗試一下,比如在執行go build命令時分別以代碼包導入路徑、目錄相對路徑和目錄絕對路徑的形式提供代碼包位置,并查看執行結果。
我們已經通過上面的示例強行的重新安裝了cnet/ctcp包及其依賴包。現在我們再來看一下goc2p的項目目錄:
$HOME/golang/goc2p:bin/pkg/linux_386//cnetctcp.alogging.apkgtool.asrc/- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
我們發現在pkg目錄的平臺相關目錄下多了一個名為cnet的目錄,而在這個目錄下的就是名為ctcp.a的代碼包歸檔文件。由此我們可知,代碼包歸檔文件的存放路徑的相對路徑(相對于當前項目的pkg目錄的平臺相關目錄)即為代碼包導入路徑除去最后一個元素后的路徑。而代碼包歸檔文件的名稱即為代碼包導入路徑中的最后一個元素再加“.a”后綴。再舉一個例子,如果代碼包導入路徑為x/y/z,則它的歸檔文件存放路徑的相對路徑即為x/y/,而這個歸檔文件的名稱即為z.a。
現在來看代碼包pkgtool的歸檔文件的存放路徑。因為它的導入路徑中只有一個元素,所以其歸檔文件就被直接存放到了goc2p項目的pkg目錄的平臺相關目錄下。
此外,我們還發現pkg目錄的平臺相關目錄下還有一個名為logging.a的文件。很顯然,我們并沒有顯式的安裝代碼包logging。這是怎么回事呢?這是因為go install命令在安裝指定的代碼包之前,會先去安裝指定代碼包的依賴包。當依賴包被正確安裝后,指定的代碼包的安裝才會開始。由于代碼包cnet/ctcp依賴于代碼包logging,所以當代碼包logging被成功安裝之后,代碼包cnet/ctcp才會被安裝。
還有一個問題:上述的安裝過程涉及到了那么多代碼包,那為什么goc2p項目的pkg目錄中只包含該項目中代碼包的歸檔文件呢?實際上,go install命令會把標準庫中的代碼包的歸檔文件存放到Go根目錄的pkg目錄中,而把指定代碼包依賴的第三方項目的代碼包的歸檔文件存放到那個項目的pkg目錄下。這樣就實現了Go語言標準庫代碼包的歸檔文件與用戶代碼包的歸檔文件,以及處在不同工作區的用戶代碼包的歸檔文件之間的徹底分離。
安裝命令源碼文件
除了安裝代碼包之外,go install命令還可以安裝命令源碼文件。為了看到安裝命令源碼文件是goc2p項目目錄的變化,我們先把該目錄還原到原始狀態,即清除bin子目錄和pkg子目錄下的所有目錄和文件。然后,我們來安裝代碼包helper/ds下的命令源碼文件showds.go,如下:
hc@ubt:~/golang/goc2p/src$ go install helper/ds/showds.go go install: no install location for directory /home/hc/golang/goc2p/src/helper/ds outside GOPATH- 1
- 2
- 3
- 1
- 2
- 3
這次我們沒能成功安裝。該Go命令認為目錄/home/hc/golang/goc2p/src/helper/ds不在環境GOPATH中。我們可以通過Linux的echo命令來查看一下環境變量GOPATH的值:
hc@ubt:~/golang/goc2p/src$ echo $GOPATH /home/hc/golang/lib:/home/hc/golang/goc2p- 1
- 2
- 1
- 2
環境變量GOPATH的值中確實包含了goc2p項目的根目錄。這到底是怎么回事呢?
作者通過查看Go命令的源碼文件($GOROOT/src/go/*.go)找到了其根本原因。在上一小節我們提到過,在環境變量GOPATH中包含多個工作區目錄路徑時,我們需要在編譯命令源碼文件前先對環境變量GOBIN進行設置。實際上,這個環境變量所指的目錄路徑就是命令程序生成的結果文件的存放目錄。go install命令會把相應的可執行文件放置到這個目錄中。
由于命令go build在編譯庫源碼文件后不會產生任何結果文件,所以自然也不用會在意結果文件的存放目錄。在該命令編譯單一的命令源碼文件時,在結果文件存放目錄無效的情況下會將結果文件(也就是可執行文件)存放到執行該命令時所在的目錄下。因此,即使環境變量GOBIN的值無效,我們在執行go build命令時也不會見到這個錯誤提示信息。
然而,go install命令中一個很重要的步驟就是將結果文件(歸檔文件或者可執行文件)存放到相應的目錄中。所以,命令go install在安裝命令源碼文件時,如果環境變量GOBIN的值無效,則它會在最后檢查結果文件存放目錄的時候發現這一問題,并打印與上述示例所示內容類似的錯誤提示信息,最后直接退出。
這個錯誤提示信息在我們安裝多個庫源碼文件時也有可能遇到。示例如下:
hc@ubt:~/golang/goc2p/src/pkgtool$ go install envir.go fpath.go ipath.go pnode.go util.go go install: no install location for directory /home/hc/golang/goc2p/src/pkgtool outside GOPATH- 1
- 2
- 3
- 1
- 2
- 3
而且,在我們為環境變量GOBIN設置了正確的值之后,這個錯誤提示信息仍然會出現。這是因為,只有在安裝命令源碼文件的時候,命令程序才會將環境變量GOBIN的值作為結果文件的存放目錄。而在安裝庫源碼文件時,在命令程序內部的代表結果文件存放目錄路徑的變量不會被賦值。最后,命令程序會發現它依然是個無效的空值。所以,命令程序會同樣返回一個關于“無安裝位置”的錯誤。這就引出一個結論,我們只能使用安裝代碼包的方式來安裝庫源碼文件,而不能在go install命令羅列并安裝它們。另外,go install命令目前無法接受標記-o以自定義結果文件的存放位置。這也從側面說明了go install命令當前還不支持針對庫源碼文件的安裝操作。
單從上述問題來講,Go工具在執行錯誤識別及其提示信息的細分方面還沒有做到最好。
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的go标准命令详解0.2 go install的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: go标准命令详解0.1 go build
- 下一篇: Golang精编100题