go标准命令详解0.8 go list

搬运自github赫林的go_command_tutorial,绝对干货,感谢作者。

0.8 go list

go list命令的作用是列出指定的代码包的信息。与其他命令相同,我们需要以代码包导入路径的方式给定代码包。被给定的代码包可以有多个。这些代码包对应的目录中必须直接保存有Go语言源码文件,其子目录中的文件不算在内。否则,代码包将被看做是不完整的。现在我们来试用一下:

hc@ubt:~$ go list cnet/ctcp pkgtool
cnet/ctcp
pkgtool

我们看到,在不加任何标记的情况下,命令的结果信息中只包含了我们指定的代码包的导入路径。我们刚刚提到,作为参数的代码包必须是完整的代码包。例如:

hc@ubt:~$ go list cnet pkgtool
can't load package: package cnet: no Go source files in /home/hc/golang/goc2p /src/cnet pkgtool

这时,go list命令报告了一个错误——代码包cnet对应的目录下没有Go源码文件。但是命令还是把代码包pkgtool的导入路径打印出来了。然而,当我们在执行go list命令并加入标记-e时,即使参数中包含有不完整的代码包,命令也不会提示错误。示例如下:

hc@ubt:~$ go list -e cnet pkgtool
cnet
pkgtool

标记-e的作用是以容错模式加载和分析指定的代码包。在这种情况下,命令程序如果在加载或分析的过程中遇到错误只会在内部记录一下,而不会直接把错误信息打印出来。我们为了看到错误信息可以使用-json标记。这个标记的作用是把代码包的结构体实例用JSON的样式打印出来。

这里解释一下,JSON的全称是Javascript Object Notation。它一种轻量级的承载数据的格式。JSON的优势在于语法简单、短小精悍,且非常易于处理。JSON还是一种纯文本格式,独立于编程语言。正因为如此,得到了绝大多数编程语言和浏览器的支持,应用非常广泛。Go语言当然也不例外,在它的标准库中有专门用于处理和转换JSON格式的数据的代码包encoding/json。关于JSON格式的具体内容,读者可以去它的官方网站查看说明。

在了解了这些基本概念之后,我们来试用一下-json标记。示例如下:

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的信息:

hc@ubt:~$ go list -json cnet/ctcp
{
        "Dir": "/home/freej/mybook/goc2p/src/cnet/ctcp",
        "ImportPath": "cnet/ctcp",
        "Name": "ctcp",
        "Target": "/home/freej/mybook/goc2p/pkg/linux_386/cnet/ctcp.a",
        "Stale": true,
        "Root": "/home/freej/mybook/goc2p",
        "GoFiles": [
                "base.go",
                "tcp.go"
        ],
        "Imports": [
                "bufio",
                "errors",
                "logging",
                "net",
                "sync",
                "time"
        ],
        "Deps": [
                "bufio",
                "bytes",
                "errors",
                "fmt",
                "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",
                "strings",
                "sync",
                "testing",
                "time"
        ]
}

由于cnet/ctcp包是一个完整有效的代码包,所以我们不使用-e标记也是没有问题的。在上面打印的cnet/ctcp包的信息中没有Incomplete字段。这是因为完整的代码包中的Incomplete字段的其类型的空值false。它已经在转换过程中被忽略掉了。另外,在cnet/ctcp包的信息中我们看到了很多其它的字段。现在我就来看看在Go命令程序中的代码包结构体都有哪些公开的字段。如下表。

表0-7 代码包结构体中的基本字段

| 字段名称 | 字段类型 | 字段描述 |
| Dir | 字符串(string) | 代码包对应的目录。 |
| ImportPath | 字符串(string) | 代码包的导入路径。 |
| Name | 字符串(string) | 代码包的名称。 |
| Doc | 字符串(string) | 代码包的文档字符串。 |
| Target | 字符串(string) | 代码包的安装路径。 |
| Goroot | 布尔(bool) | 代码包是否在Go安装目录下。 |
| Standard | 布尔(bool) | 代码包是否属于标准库的一部分。 |
| Stale | 布尔(bool) | 代码包能否被go install命令安装。 |
| Root | 字符串(string) | 代码包所属的工作区或Go安装目录的路径。 |

表0-8 代码包结构体中与源码文件有关的字段

| 字段名称 | 字段类型 | 字段描述 |
| GoFiles | 字符串切片([]string) | Go源码文件的数组。不包含导入了代码包“C”的源码文件和测试源码文件。 |
| CgoFiles | 字符串切片([]string) | 导入了代码包“C”的源码文件的数组。 |
| IgnoredGoFiles | 字符串切片([]string) | 需要被编译器忽略的源码文件的数组。 |
| CFiles | 字符串切片([]string) | 名称中有“.c”后缀的文件的数组。 |
| HFiles | 字符串切片([]string) | 名称中有“.h”后缀的文件的数组。 |
| SFiles | 字符串切片([]string) | 名称中有“.s”后缀的文件的数组。 |
| SysoFiles | 字符串切片([]string) | 名称中有“.syso”后缀的文件的数组。这些文件需要被加入到归档文件中。 |
| SwigFiles | 字符串切片([]string) | 名称中有“.swig”后缀的文件的数组。 |
| SwigCXXFiles | 字符串切片([]string) | 名称中有“.swigcxx”后缀的文件的数组。 |

表0-9 代码包结构体中与Cgo指令有关的字段

| 字段名称 | 字段类型 | 字段描述 |
| CgoCFLAGS | 字符串切片([]string) | 需要传递给C编译器的标记的数组。针对于Cgo。 |
| CgoLDFLAGS | 字符串切片([]string) | 需要传递给链接器的标记的数组。针对于Cgo。 |
| CgoPkgConfig | 字符串切片([]string) | pkg-config的名称的数组。针对于Cgo。 |

表0-10 代码包结构体中与依赖信息有关的字段

| 字段名称 | 字段类型 | 字段描述 |
| Imports | 字符串切片([]string) | 代码包中的源码文件显示导入的依赖包的导入路径的数组。 |
| Deps | 字符串切片([]string) | 所有的依赖包(包括间接依赖)的导入路径的数组。 |

表0-11 代码包结构体中与错误信息有关的字段

| 字段名称 | 字段类型 | 字段描述 |
| Incomplete | 布尔(bool) | 代码包是否是完整的,也即在载入或分析代码包及其依赖包时是否有错误发生。 |
| Error | *PackageError类型 | 载入或分析代码包时发生的错误。 |
| Error | *PackageError类型的数组([]*PackageError) | 载入或分析代码包的依赖包时发生的错误。 |

表0-12 代码包结构体中与测试源码文件有关的字段

| 字段名称 | 字段类型 | 字段描述 |
| TestGoFiles | 字符串切片([]string) | 代码包中的测试源码文件的数组。 |
| TestImports | 字符串切片([]string) | 代码包中的测试源码文件显示导入的依赖包的导入路径的数组。 |
| XTestGoFiles | 字符串切片([]string) | 代码包中的外部测试源码文件的数组。 |
| XTestImports | 字符串切片([]string) | 代码包中的外部测试源码文件显示导入的依赖包的导入路径的数组。 |

代码包结构体中定义的字段很多,但有些时候我们只需要查看其中的一些字段。那要怎么做呢?标记-f可以满足这个需求。比如这样:

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标记:

hc@ubt:~$ go list -f {{.GoFiles}} cnet/ctcp
[base.go tcp.go]

如果我们想查看不完整的代码包cnet的错误提示信息,还可以这样:

hc@ubt:~$ go list -e -f {{.Error.Err}} cnet no Go source files in D:\Kanbox\gitrepo\goc2p\src\cnet

我们还可以利用代码包text/template中定义的强大语法让go list命令输出定制化更高的代码包信息。比如:

hc@ubt:~$ go list -e -f 'The package {{.ImportPath}} is {{if .Incomplete}} incomplete!{{else}}complete.{{end}}' cnet The package cnet is incomplete! hc@ubt:~$ go list -f 'The imports of package {{.ImportPath}} is [{{join .Imports ", "}}].' cnet/ctcp The imports of package cnet/ctcp is [bufio, errors, logging, net, sync, time].

其中,join是命令程序在text/template包原有语法之上自定义的语法,在底层使用标准库代码包strings中的Join函数。关于更多的语法规则,请读者查看代码包text/template的相关文档。

另外,-tags标记也可以被go list接受。它与我们在讲go build命令时提到的-tags标记是一致的。读者可以查看代码包`go/build的文档以了解细节。

go list命令很有用。它可以为我们提供指定代码包的更深层次的信息。这些信息往往是我们无法从源码文件中直观看到的。

你可能感兴趣的:(go语言)