「go install」指令用來編譯並安裝指定的程式碼包及它們的依賴包;當指定的程式碼包的依賴包還沒有被編譯和安裝時,該指令會先去處理依賴包。 「go install」指令會將編譯的中間檔案放在GOPATH的pkg目錄下,並且固定地將編譯結果放在GOPATH的bin目錄下。
本教學操作環境:windows7系統、GO 1.18版本、Dell G3電腦。
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/
我們看到,goc2p專案中有三個子目錄,分別是bin目錄、pkg目錄和src目錄。現在只有src目錄中包含了一些目錄,而其他兩個目錄都是空的。
現在,我們來看看安裝程式碼套件的規則。
安裝程式碼包
如果go install
命令後跟的程式碼包中僅包含庫源碼文件,那麼go install
指令會把編譯後的結果檔保存在原始碼檔案所在工作區的pkg目錄下。對於僅包含庫源碼文件的程式碼包來說,這個結果文件就是對應的程式碼包歸檔文件(也叫靜態連結庫文件,名稱以.a結尾)。相較之下,我們在使用go build
指令對僅包含庫原始碼檔案的程式碼包進行編譯時,是不會在目前工作區的src目錄以及pkg目錄下產生任何結果檔的。結果檔案會出於編譯的目的而產生在臨時目錄中,但並不會使目前工作區目錄產生任何變化。
如果我們在執行go install
指令時不後面跟著任何程式碼包參數,那麼指令將試圖編譯目前目錄所對應的程式碼包。例如,我們現在要安裝程式碼包pkgtool
:
hc@ubt:~/golang/goc2p/src/pkgtool$ go install -v -work WORK=D:\cygwin\tmp\go-build758586887 pkgtool
我們在前面說過,執行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 strings bytes bufio time os path/filepath pkgtool
可以看到,程式碼套件pkgtool
只依賴了Go語言標準庫中的程式碼包。
現在我們再來查看goc2p專案目錄:
$HOME/golang/goc2p: bin/ pkg/ linux_386/ pkgtool.a src/
現在pkg目錄中多了一個子目錄。讀過0.0節的讀者應該已經知道,linux_386被叫做平台相關目錄。它的名字可以由${GOOS}_${GOARCH}來得到。其中,${GOOS}和${GOARCH}分別是目前作業系統中的環境變數GOOS和GOARCH的值。如果它們不存在,那麼Go語言就會使用其內部的預定值。上述範例在計算架構為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 sync sort io syscall internal/singleflight bytes strings strconv bufio math/rand time reflect os fmt log runtime/cgo logging net cnet/ctcp
請注意,我們是在程式碼包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
從上述範例中的命令提示資訊我們可知,以目錄絕對路徑的形式提供程式碼包位置是不會被Go指令認可的。
这是由于Go认为本地代码包路径的表示只能以“./”或“../”开始,再或者直接为“.”或“..”,而代码包的代码导入路径又不允许以“/”开始。所以,这种用绝对路径表示代码包位置的方式也就不能被支持了。
上述规则适用于所有Go命令。读者可以自己尝试一下,比如在执行go build命令时分别以代码包导入路径、目录相对路径和目录绝对路径的形式提供代码包位置,并查看执行结果。
我们已经通过上面的示例强行的重新安装了cnet/ctcp包及其依赖包。现在我们再来看一下goc2p的项目目录:
$HOME/golang/goc2p: bin/ pkg/ linux_386/ /cnet ctcp.a logging.a pkgtool.a src/
我们发现在pkg目录的平台相关目录下多了一个名为cnet的目录,而在这个目录下的就是名为ctcp.a的代码包归档文件。由此我们可知,代码包归档文件的存放目录的相对路径(相对于当前工作区的pkg目录的平台相关目录)即为代码包导入路径除去最后一个元素后的路径。而代码包归档文件的名称即为代码包导入路径中的最后一个元素再加“.a”后缀。再举一个例子,如果代码包导入路径为x/y/z,则它的归档文件存放路径的相对路径即为x/y/,而这个归档文件的名称即为z.a。
回顾代码包pkgtool的归档文件的存放路径。因为它的导入路径中只有一个元素,所以其归档文件就被直接存放到了goc2p项目的pkg目录的平台相关目录下了。
此外,我们还发现pkg目录的平台相关目录下还有一个名为logging.a的文件。很显然,我们并没有显式的安装代码包logging。这是因为go install命令在安装指定的代码包之前,会先去安装指定代码包的依赖包。当依赖包被正确安装后,指定的代码包的安装才会开始。由于代码包cnet/ctcp依赖于goc2p项目(即当前工作区)中的代码包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 .go files listed on command line (GOBIN not set)
这次我们没能成功安装。该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
环境变量GOPATH的值中确实包含了goc2p项目的根目录。这到底是怎么回事呢?
我通过查看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 .go files listed on command line (GOBIN not set)
而且,在我们为环境变量GOBIN设置了正确的值之后,这个错误提示信息仍然会出现。这是因为,只有在安装命令源码文件的时候,命令程序才会将环境变量GOBIN的值作为结果文件的存放目录。而在安装库源码文件时,在命令程序内部的代表结果文件存放目录路径的那个变量不会被赋值。最后,命令程序会发现它依然是个无效的空值。所以,命令程序会同样返回一个关于“无安装位置”的错误。这就引出一个结论,我们只能使用安装代码包的方式来安装库源码文件,而不能在go install命令罗列并安装它们。另外,go install命令目前无法接受标记-o以自定义结果文件的存放位置。这也从侧面说明了go install命令不支持针对库源码文件的安装操作。
至此,我们对怎样用go install命令来安装代码包以及命令源码文件进行了说明。如果你已经熟知了go build命令,那么理解这些内容应该不在话下。
以上是go install指令有什麼用的詳細內容。更多資訊請關注PHP中文網其他相關文章!