# go list
`go list`命令的作用是列出指定的代碼包的信息。與其他命令相同,我們需要以代碼包導入路徑的方式給定代碼包。被給定的代碼包可以有多個。這些代碼包對應的目錄中必須直接保存有Go語言源碼文件,其子目錄中的文件不算在內。否則,代碼包將被看做是不完整的。現在我們來試用一下:
```bash
hc@ubt:~$ go list cnet/ctcp pkgtool
cnet/ctcp
pkgtool
```
我們看到,在不加任何標記的情況下,命令的結果信息中只包含了我們指定的代碼包的導入路徑。我們剛剛提到,作為參數的代碼包必須是完整的代碼包。例如:
```bash
hc@ubt:~$ go list cnet pkgtool
can't load package: package cnet: no buildable Go source files in /home/hc/golang/goc2p/src/cnet/
pkgtool
```
這時,`go list`命令報告了一個錯誤——代碼包`cnet`對應的目錄下沒有Go源碼文件。但是命令還是把代碼包pkgtool的導入路徑打印出來了。然而,當我們在執行`go list`命令并加入標記`-e`時,即使參數中包含有不完整的代碼包,命令也不會提示錯誤。示例如下:
```bash
hc@ubt:~$ go list -e cnet pkgtool
cnet
pkgtool
```
標記`-e`的作用是以容錯模式加載和分析指定的代碼包。在這種情況下,命令程序如果在加載或分析的過程中遇到錯誤只會在內部記錄一下,而不會直接把錯誤信息打印出來。我們為了看到錯誤信息可以使用`-json`標記。這個標記的作用是把代碼包的結構體實例用JSON的樣式打印出來。
這里解釋一下,JSON的全稱是Javascript Object Notation。它一種輕量級的承載數據的格式。JSON的優勢在于語法簡單、短小精悍,且非常易于處理。JSON還是一種純文本格式,獨立于編程語言。正因為如此,得到了絕大多數編程語言和瀏覽器的支持,應用非常廣泛。Go語言當然也不例外,在它的標準庫中有專門用于處理和轉換JSON格式的數據的代碼包`encoding/json`。關于JSON格式的具體內容,讀者可以去它的[官方網站](http://www.json.org)查看說明。
在了解了這些基本概念之后,我們來試用一下`-json`標記。示例如下:
```bash
hc@ubt:~$ go list -e -json cnet
{
"Dir": "/home/hc/golang/goc2p/src/cnet",
"ImportPath": "cnet",
"Stale": true,
"Root": "/home/hc/golang/goc2p",
"Incomplete": true,
"Error": {
"ImportStack": [
"cnet"
],
"Pos": "",
"Err": "no Go source files in /home/hc/golang/goc2p/src/cnet"
}
}
```
在上述JSON格式的代碼包信息中,對于結構體中的字段的顯示是不完整的。因為命令程序認為我們指定`cnet`就是不完整的。在名為`Error`的字段中,我們可以看到具體說明。`Error`字段的內容其實也是一個結構體。在JSON格式下,這種嵌套的結構體被完美的展現了出來。`Error`字段所指代的結構體實例的`Err`字段說明了`cnet`不完整的原因。這與我們在沒有使用`-e`標記的情況下所打印出來的錯誤提示信息是一致的。我們再來看`Incomplete`字段。它的值為`true`。這同樣說明`cnet`是一個不完整的代碼包。
實際上,在從這個代碼包結構體實例到JSON格式文本的轉換過程中,所有的值為其類型的空值的字段都已經被忽略了。
現在我們使用帶`-json`標記的`go list`命令列出代碼包`cnet/ctcp`的信息:
```bash
hc@ubt:~$ go list -json cnet/ctcp
{
"Dir": "/home/hc/golang/github/goc2p/src/cnet/ctcp",
"ImportPath": "cnet/ctcp",
"Name": "ctcp",
"Target": "/home/hc/golang/github/goc2p/pkg/darwin_amd64/cnet/ctcp.a",
"Stale": true,
"Root": "/home/hc/golang/github/goc2p",
"GoFiles": [
"base.go",
"tcp.go"
],
"Imports": [
"bufio",
"bytes",
"errors",
"logging",
"net",
"sync",
"time"
],
"Deps": [
"bufio",
"bytes",
"errors",
"fmt",
"internal/singleflight",
"io",
"log",
"logging",
"math",
"math/rand",
"net",
"os",
"reflect",
"runtime",
"runtime/cgo",
"sort",
"strconv",
"strings",
"sync",
"sync/atomic",
"syscall",
"time",
"unicode",
"unicode/utf8",
"unsafe"
],
"TestGoFiles": [
"tcp_test.go"
],
"TestImports": [
"bytes",
"fmt",
"net",
"runtime",
"strings",
"sync",
"testing",
"time"
]
}
```
由于`cnet/ctcp`包是一個完整有效的代碼包,所以我們不使用`-e`標記也是沒有問題的。在上面打印的`cnet/ctcp`包的信息中沒有`Incomplete`字段。這是因為完整的代碼包中的`Incomplete`字段的其類型的空值`false`。它已經在轉換過程中被忽略掉了。另外,在`cnet/ctcp`包的信息中我們看到了很多其它的字段。現在我就來看看在Go命令程序中的代碼包結構體都有哪些公開的字段。如下表。
表0-7 代碼包結構體中的基本字段
字段名稱 | 字段類型 | 字段描述
------------- | --------------- | ---------------
Dir | 字符串(string) | 代碼包對應的目錄。
ImportPath | 字符串(string) | 代碼包的導入路徑。
ImportComment | 字符串(string) | 代碼包聲明語句右邊的用于自定義導入路徑的注釋。
Name | 字符串(string) | 代碼包的名稱。
Doc | 字符串(string) | 代碼包的文檔字符串。
Target | 字符串(string) | 代碼包的安裝路徑。
Shlib | 字符串(string) | 包含該代碼包的共享庫(shared library)的名稱。
Goroot | 布爾(bool) | 該代碼包是否在Go語言安裝目錄下。
Standard | 布爾(bool) | 該代碼包是否屬于標準庫的一部分。
Stale | 布爾(bool) | 該代碼包的最新代碼是否未被安裝。
Root | 字符串(string) | 該代碼包所屬的工作區或Go安裝目錄的路徑。
表0-8 代碼包結構體中與源碼文件有關的字段
字段名稱 | 字段類型 | 字段描述
-------------- | -------------------- | ---------------
GoFiles | 字符串切片([]string) | Go源碼文件的列表。不包含導入了代碼包“C”的源碼文件和測試源碼文件。
CgoFiles | 字符串切片([]string) | 導入了代碼包“C”的源碼文件的列表。
IgnoredGoFiles | 字符串切片([]string) | 忽略編譯的源碼文件的列表。
CFiles | 字符串切片([]string) | 名稱中有“.c”后綴的源碼文件的列表。
CXXFiles | 字符串切片([]string) | 名稱中有“.cc”、“.cxx”或“.cpp”后綴的源碼文件的列表。
MFiles | 字符串切片([]string) | 名稱中“.m”后綴的源碼文件的列表。
HFiles | 字符串切片([]string) | 名稱中有“.h”后綴的源碼文件的列表。
SFiles | 字符串切片([]string) | 名稱中有“.s”后綴的源碼文件的列表。
SwigFiles | 字符串切片([]string) | 名稱中有“.swig”后綴的文件的列表。
SwigCXXFiles | 字符串切片([]string) | 名稱中有“.swigcxx”后綴的文件的列表。
SysoFiles | 字符串切片([]string) | 名稱中有“.syso”后綴的文件的列表。這些文件是需要被加入到歸檔文件中的。
表0-9 代碼包結構體中與Cgo指令有關的字段
字段名稱 | 字段類型 | 字段描述
-------------- | -------------------- | ---------------
CgoCFLAGS | 字符串切片([]string) | 需要傳遞給C編譯器的標記的列表。針對Cgo。
CgoCPPFLAGS | 字符串切片([]string) | 需要傳遞給C預處理器的標記的列表。針對Cgo。
CgoCXXFLAGS | 字符串切片([]string) | 需要傳遞給C++編譯器的標記的列表。針對Cgo。
CgoLDFLAGS | 字符串切片([]string) | 需要傳遞給鏈接器的標記的列表。針對Cgo。
CgoPkgConfig | 字符串切片([]string) | pkg-config的名稱的列表。針對Cgo。
表0-10 代碼包結構體中與依賴信息有關的字段
字段名稱 | 字段類型 | 字段描述
-------------- | -------------------- | ---------------
Imports | 字符串切片([]string) | 該代碼包中的源碼文件顯式導入的依賴包的導入路徑的列表。
Deps | 字符串切片([]string) | 所有的依賴包(包括間接依賴)的導入路徑的列表。
表0-11 代碼包結構體中與錯誤信息有關的字段
字段名稱 | 字段類型 | 字段描述
-------------- | -------------------- | ---------------
Incomplete | 布爾(bool) | 代碼包是否是完整的,也即在載入或分析代碼包及其依賴包時是否有錯誤發生。
Error | \*PackageError類型 | 載入或分析代碼包時發生的錯誤。
DepsErrors | []\*PackageError類型 | 載入或分析依賴包時發生的錯誤。
表0-12 代碼包結構體中與測試源碼文件有關的字段
字段名稱 | 字段類型 | 字段描述
-------------- | -------------------- | ---------------
TestGoFiles | 字符串切片([]string) | 代碼包中的測試源碼文件的名稱列表。
TestImports | 字符串切片([]string) | 代碼包中的測試源碼文件顯示導入的依賴包的導入路徑的列表。
XTestGoFiles | 字符串切片([]string) | 代碼包中的外部測試源碼文件的名稱列表。
XTestImports | 字符串切片([]string) | 代碼包中的外部測試源碼文件顯示導入的依賴包的導入路徑的列表。
代碼包結構體中定義的字段很多,但有些時候我們只需要查看其中的一些字段。那要怎么做呢?標記`-f`可以滿足這個需求。比如這樣:
```bash
hc@ubt:~$ go list -f {{.ImportPath}} cnet/ctcp
cnet/ctcp
```
實際上,`-f`標記的默認值就是`{{.ImportPath}}`。這也正是我們在使用不加任何標記的`go list`命令時依然能看到指定代碼包的導入路徑的原因了。
標記`-f`的值需要滿足標準庫的代碼包``text/template`中定義的語法。比如,`{{.S}}`代表根結構體的`S`字段的值。在`go list`命令的場景下,這個根結構體就是指定的代碼包所對應的結構體。如果`S`字段的值也是一個結構體的話,那么`{{.S.F}}`就代表根結構體的`S`字段的值中的`F`字段的值。如果我們要查看`cnet/ctcp`包中的命令源碼文件和庫源碼文件的列表,可以這樣使用`-f`標記:
```bash
hc@ubt:~$ go list -f {{.GoFiles}} cnet/ctcp
[base.go tcp.go]
```
如果我們想查看不完整的代碼包`cnet`的錯誤提示信息,還可以這樣:
```bash
hc@ubt:~$ go list -e -f {{.Error.Err}} cnet
no buildable Go source files in /home/hc/golang/goc2p/src/cnet
```
我們還可以利用代碼包`text/template`中定義的強大語法讓`go list`命令輸出定制化更高的代碼包信息。比如:
```bash
hc@ubt:~$ go list -e -f 'The package {{.ImportPath}} is {{if .Incomplete}}incomplete!{{else}}complete.{{end}}' cnet
The package cnet is incomplete!
```bash
hc@ubt:~$ go list -f 'The imports of package {{.ImportPath}} is [{{join .Imports ", "}}].' cnet/ctcp
The imports of package cnet/ctcp is [bufio, bytes, errors, logging, net, sync, time].
```
其中,`join`是命令程序在`text/template`包原有語法之上自定義的語法,在底層使用標準庫代碼包`strings`中的`Join`函數。關于更多的語法規則,請讀者查看代碼包`text/template`的相關文檔。
另外,`-tags`標記也可以被`go list`接受。它與我們在講`go build`命令時提到的`-tags`標記是一致的。讀者可以查看代碼包`go/build``的文檔以了解細節。
`go list`命令很有用。它可以為我們提供指定代碼包的更深層次的信息。這些信息往往是我們無法從源碼文件中直觀看到的。