diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/0.0.md b/0.0.md index f56a126..1ee2a59 100644 --- a/0.0.md +++ b/0.0.md @@ -1,9 +1,26 @@ -## 0.0 标准命令详解 +# 标准命令详解 -为了让讲解更具关联性,也为了让读者能够更容易的理解这些命令和工具,本教程并不会按照这些命令的字典顺序讲解它们,而会按照我们在实际开发过程中通常的使用顺序以及它们的重要程度的顺序推进说明。 我们先从```go build```命令开始。 +Go语言的1.5版本在标准命令方面有了重大变更。这倒不是说它们的用法有多大的变化,而是说它们的底层支持已经大变样了。让我们先来对比一下`$GOROOT/pkg/tool/<平台相关目录>`中的内容。以下简称此目录为Go工具目录。 +**插播:** 平台相关目录即以 `_` 命名的目录,用于存放因特定平台的不同而不同的代码包归档文件或可执行文件。其中,`` 代表特定平台的操作系统代号,而 `` 则代表特定平台的计算架构代号。使用 `go env` 命令便可查看它们在你的计算机中的实际值。 +1.4版本的Go工具目录的内容如下: -## links - * [目录](catalog.md) - * 下一节: [go build](0.1.md) \ No newline at end of file +```bash +5a 5l 6g 8c addr2line dist objdump tour +5c 6a 6l 8g cgo fix pack vet +5g 6c 8a 8l cover nm pprof yacc +``` + +下面是Go 1.5版本的: + +```bash +addr2line asm compile dist fix nm pack tour vet +api cgo cover doc link objdump pprof trace yacc +``` + +可以看到,1.5版本的目录内容精简了不少。这是因为Go 1.5的编译器、链接器都已经完全用Go语言重写了。而在这之前,它们都是用C语言写的,因此不得不为每类平台编写不同的程序并生成不同的文件。例如,8g、6g和5g分别是gc编译器在x86(32bit)、x86-64(64bit)和ARM计算架构的计算机上的实现程序。相比之下,用Go语言实现的好处就是,编译器和链接器都将是跨平台的了。简要来说,Go 1.5版本的目录中的文件 compile 即是统一后的编译器,而文件 link 则是统一后的链接器。 + +本教程并不会讲解Go语言的编译器、链接器以及其它工具是怎样被编写出来的,并只会关注于怎样用好包含它们在内的Go语言自带的命令和工具。 + +为了让讲解更具关联性,也为了让读者能够更容易的理解这些命令和工具,本教程并不会按照这些命令的字典顺序描述它们,而会按照我们在实际开发过程中通常的使用顺序以及它们的重要程度来逐一进行说明。现在,我们就先从`go build`命令开始。 diff --git a/0.1.md b/0.1.md index a6c92c7..78dd48d 100644 --- a/0.1.md +++ b/0.1.md @@ -1,232 +1,216 @@ -### 0.1 go build +# go build +`go build`命令用于编译我们指定的源码文件或代码包以及它们的依赖包。 -```go build```命令用来编译指定的代码或代码包及它们的依赖包。 - -例如,如果我们在执行```go build```命令时不后跟任何代码包,那么命令将试图编译当前目录所对应的代码包。例如,我们想编译goc2p项目的代码包```logging```。其中一个方法是进入```logging```目录并直接执行该命令: - - hc@ubt:~/golang/goc2p/src/logging$ go build - -因为在代码包```logging```中只有库源码文件和测试源码文件,所以在执行```go build```命令之后不会在当前目录和goc2p项目的pkg目录中产生任何文件。 - -还有另外一种编译```logging```包的方式: - - hc@ubt:~/golang/goc2p/src$ go build logging - -在这里,我们把代码包```logging```的导入路径作为参数传递给```go build```命令。又例如,如果我们要编译代码包```cnet/ctcp```,只需要在任意目录下执行命令```go build cnet/ctcp```即可。 - -当然,我们也可以通过指定多个Go源码文件来完成编译行为: - - hc@ubt:~/golang/goc2p/src$ go build logging/base.go logging/console_logger.go logging/log_manager.go logging/tag.go - -但是,使用这种方法会有一个限制。作为参数的多个Go源码文件必须在同一个目录中。也就是说,如果我们想用一个命令既编译```logging```包又编译```basic```包是不可能的。不过别担心,```go build```命令能够在需要的时候去编译它们。假设有一个导入路径为```app```的代码包,同时依赖了```logging```包和```basic```包。那么在执行命令```go build app```的时候,该工具就会自动的在编译```app```包之前去编译它的所有依赖包,包括```logging```包和```basic```包。 - -注意,```go build```命令既不能编译包含多个命令源码文件的代码包,也不能同时编译多个命令源码文件。因为,如果把多个命令源码文件作为一个整体看待,那么每个文件中的main函数就属于重名函数,在编译时会抛出重复定义错误。假如,在goc2p项目的代码包```cmd```(此代码包仅用于示例目的,并不会永久存在于该项目中)中包含有两个命令源码文件showds.go和initpkg_demo.go,那么我们在使用```go build```命令同时编译它们时就会失败。示例如下: - - hc@ubt:~/golang/goc2p/src/cmd$ go build showds.go initpkg_demo.go - # command-line-arguments - ./initpkg_demo.go:19: main redeclared in this block - previous declaration at ./showds.go:56 - -请注意上面示例中的“command-line-arguments”。在这个位置上应该显示的是作为参数的源码文件所属代码包的导入路径。但是,这里显示的并不是它们所属的代码包的导入路径```cmd```。这是因为,命令程序在分析参数的时候如果发现第一个参数是Go源码文件而不是代码包,则会在内部生成一个虚拟代码包。这个虚拟代码包的导入路径和名称都会是“command-line-arguments”。在其他基于编译流程的命令程序中也有与之一致的操作。比如```go install```命令和```go run```命令。 - -现在我们使用```go build```命令编译单一命令源码文件。我们在执行命令时加入一个标记```-v```。这个标记的意义在于可以使命令把执行过程中构建的包名打印出来。我们会在稍后对这个标记进行详细说明。现在我们先来看一个示例: - - hc@ubt:~/golang/goc2p/src/basic/pkginit$ ls - initpkg_demo.go - hc@ubt:~/golang/goc2p/src/basic/pkginit$ go build -v initpkg_demo.go - command-line-arguments - hc@ubt:~/golang/goc2p/src/basic/pkginit$ ls - initpkg_demo initpkg_demo.go - -我们在执行命令```go build -v initpkg_demo.go ```之后被打印出的“command-line-arguments”就是命令程序为命令源码文件initpkg_demo.go生成的虚拟代码包的包名。 - -```go build```命令会把编译命令源码文件后生成的结果文件存放到执行该命令时所在的目录下。这个所说的结果文件就是与命令源码文件对应的可执行文件。它的名称会与命令源码文件的主文件名相同。 - -我们可以自定义生成的可执行文件的名字,示例如下: - - hc@ubt:~/golang/goc2p/src/basic/pkginit$ go build -o initpkg initpkg_demo.go - hc@ubt:~/golang/goc2p/src/basic/pkginit$ ls - initpkg initpkg_demo.go - -使用```-o```标记可以指定输出文件(在这个示例中是可执行文件)的名称。它是最常用的一个```go build```命令标记。但需要注意的是,当使用标记```-o```的时候,不能同时对多个代码包进行编译。 - -除此之外,还有一些标记在我们日常开发过程中可能会被用到。如下表。 - -_表0-1 ```go build```命令的常用标记说明_ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 标记名称 - - 标记描述 -
- -o - - 指定输出文件。 -
- -a - - 强行对所有涉及到的代码包(包括标准库中的代码包)进行重新构建,即使它们已经是最新的了。 -
- -n - - 打印构建期间所用到的其它命令,但是并不真正执行它们。 -
- -p n - - 构建的并行数量(n)。默认情况下并行数量与CPU数量相同。 -
- -race - - 开启数据竞争检测。此标记目前仅在linux/amd64、darwin/amd64和windows/amd64平台下被支持。 -
- -v - - 打印出被构建的代码包的名字。 -
- -work - - 打印出临时工作目录的名字,并且取消在构建完成后对它的删除操作。 -
- -x - - 打印出构建期间所用到的其它命令。 -
+例如,如果我们在执行`go build`命令时不后跟任何代码包,那么命令将试图编译当前目录所对应的代码包。例如,我们想编译goc2p项目的代码包`logging`。其中一个方法是进入`logging`目录并直接执行该命令: + +```bash +hc@ubt:~/golang/goc2p/src/logging$ go build +``` + +因为在代码包`logging`中只有库源码文件和测试源码文件,所以在执行`go build`命令之后不会在当前目录和goc2p项目的pkg目录中产生任何文件。 + +**插播:**Go语言的源码文件有三大类,即:命令源码文件、库源码文件和测试源码文件。他们的功用各不相同,而写法也各有各的特点。命令源码文件总是作为可执行的程序的入口。库源码文件一般用于集中放置各种待被使用的程序实体(全局常量、全局变量、接口、结构体、函数等等)。而测试源码文件主要用于对前两种源码文件中的程序实体的功能和性能进行测试。另外,后者也可以用于展现前两者中程序的使用方法。 + +另外一种编译`logging`包的方法是: + +```bash +hc@ubt:~/golang/goc2p/src$ go build logging +``` +在这里,我们把代码包`logging`的导入路径作为参数传递给`go build`命令。另一个例子:如果我们要编译代码包`cnet/ctcp`,只需要在任意目录下执行命令`go build cnet/ctcp`即可。 + +**插播:**之所以这样的编译方法可以正常执行,是因为我们已经在环境变量`GOPATH`中加入了goc2p项目的根目录(即`~/golang/goc2p/`)。这时,goc2p项目的根目录就成为了一个工作区目录。只有这样,Go语言才能正确识别我们提供的goc2p项目中某个代码包的导入路径。而代码包的导入路径是指,相对于Go语言自身的源码目录(即`$GOROOT/src`)或我们在环境变量`GOPATH`中指定的某个目录的`src`子目录下的子路径。例如,这里的代码包`logging`的绝对路径是`~/golang/goc2p/src/logging`。而不论goc2p项目的根文件夹被放在哪儿,`logging`包的导入路径都是`logging`。显而易见,我们在称呼一个代码包的时候总是以其导入路径作为其称谓。 + +言归正传,除了上面的简单用法,我们还可以同时编译多个Go源码文件: + +```bash +hc@ubt:~/golang/goc2p/src$ go build logging/base.go logging/console_logger.go logging/log_manager.go logging/tag.go +``` + +但是,使用这种方法会有一个限制。作为参数的多个Go源码文件必须在同一个目录中。也就是说,如果我们想用这种方法既编译`logging`包又编译`basic`包是不可能的。不过别担心,在需要的时候,那些被编译目标依赖的代码包会被`go build`命令自动的编译。例如,如果有一个导入路径为`app`的代码包,同时依赖了`logging`包和`basic`包。那么在执行`go build app`的时候,该命令就会自动的在编译`app`包之前去检查`logging`包和`basic`包的编译状态。如果发现它们的编译结果文件不是最新的,那么该命令就会先去的编译这两个代码包,然后再编译`app`包。 + +注意,`go build`命令在编译只包含库源码文件的代码包(或者同时编译多个代码包)时,只会做检查性的编译,而不会输出任何结果文件。 + +另外,`go build`命令既不能编译包含多个命令源码文件的代码包,也不能同时编译多个命令源码文件。因为,如果把多个命令源码文件作为一个整体看待,那么每个文件中的main函数就属于重名函数,在编译时会抛出重复定义错误。假如,在goc2p项目的代码包`cmd`(此代码包仅用于示例目的,并不会永久存在于该项目中)中包含有两个命令源码文件showds.go和initpkg_demo.go,那么我们在使用`go build`命令同时编译它们时就会失败。示例如下: + +```bash +hc@ubt:~/golang/goc2p/src/cmd$ go build showds.go initpkg_demo.go +# command-line-arguments +./initpkg_demo.go:19: main redeclared in this block + previous declaration at ./showds.go:56 +``` + +请注意上面示例中的`command-line-arguments`。在这个位置上应该显示的是作为编译目标的源码文件所属的代码包的导入路径。但是,这里显示的并不是它们所属的代码包的导入路径`cmd`。这是因为,命令程序在分析参数的时候如果发现第一个参数是Go源码文件而不是代码包,则会在内部生成一个虚拟代码包。这个虚拟代码包的导入路径和名称都会是`command-line-arguments`。在其他基于编译流程的命令程序中也有与之一致的操作,比如`go install`命令和`go run`命令。 + +另一方面,如果我们编译的多个属于`main`包的源码文件中没有`main`函数的声明,那么就会使编译器立即报出“未定义`main`函数声明”的错误并中止编译。换句话说,在我们同时编译多个`main`包的源码文件时,要保证其中有且仅有一个`main`函数声明,否则编译是无法成功的。 + +现在我们使用`go build`命令编译单一命令源码文件。我们在执行命令时加入一个标记`-v`。这个标记的意义在于可以使命令把执行过程中构建的包名打印出来。我们会在稍后对这个标记进行详细说明。现在我们先来看一个示例: + +```bash +hc@ubt:~/golang/goc2p/src/basic/pkginit$ ls +initpkg_demo.go +hc@ubt:~/golang/goc2p/src/basic/pkginit$ go build -v initpkg_demo.go +command-line-arguments +hc@ubt:~/golang/goc2p/src/basic/pkginit$ ls +initpkg_demo initpkg_demo.go +``` + +我们在执行命令`go build -v initpkg_demo.go`之后被打印出的`command-line-arguments`就是命令程序为命令源码文件initpkg_demo.go生成的虚拟代码包的包名。顺带说一句, + +命令`go build`会把编译命令源码文件后生成的结果文件存放到执行该命令时所在的目录下。这个所说的结果文件就是与命令源码文件对应的可执行文件。它的名称会与命令源码文件的主文件名相同。 + +顺便说一下,如果我们有多个声明为属于`main`包的源码文件,且其中只有一个文件声明了`main`函数的话,那么是可以使用`go build`命令同时编译它们的。在这种情况下,不包含`main`函数声明的那几个源码文件会被视为库源码文件(理所当然)。如此编译之后的结果文件的名称将会与我们指定的编译目标中最左边的那个源码文件的主文件名相同。 + +其实,除了让Go语言编译器自行决定可执行文件的名称,我们还可以自定义它。示例如下: + +```bash +hc@ubt:~/golang/goc2p/src/basic/pkginit$ go build -o initpkg initpkg_demo.go +hc@ubt:~/golang/goc2p/src/basic/pkginit$ ls +initpkg initpkg_demo.go +``` + +使用`-o`标记可以指定输出文件(在这个示例中指的是可执行文件)的名称。它是最常用的一个`go build`命令标记。但需要注意的是,当使用标记`-o`的时候,不能同时对多个代码包进行编译。 + +标记`-i`会使`go build`命令安装那些编译目标依赖的且还未被安装的代码包。这里的安装意味着产生与代码包对应的归档文件,并将其放置到当前工作区目录的`pkg`子目录的相应子目录中。在默认情况下,这些代码包是不会被安装的。 + +除此之外,还有一些标记不但受到`go build`命令的支持,而且对于后面会提到的`go install`、`go run`、`go test`等命令同样是有效的。下表列出了其中比较常用的标记。 + +_表0-1 `go build`命令的常用标记说明_ + +标记名称 | 标记描述 +------------ | ------------- +-a | 强行对所有涉及到的代码包(包含标准库中的代码包)进行重新构建,即使它们已经是最新的了。 +-n | 打印编译期间所用到的其它命令,但是并不真正执行它们。 +-p n | 指定编译过程中执行各任务的并行数量(确切地说应该是并发数量)。在默认情况下,该数量等于CPU的逻辑核数。但是在`darwin/arm`平台(即iPhone和iPad所用的平台)下,该数量默认是`1`。 +-race | 开启竞态条件的检测。不过此标记目前仅在`linux/amd64`、`freebsd/amd64`、`darwin/amd64`和`windows/amd64`平台下受到支持。 +-v | 打印出那些被编译的代码包的名字。 +-work | 打印出编译时生成的临时工作目录的路径,并在编译结束时保留它。在默认情况下,编译结束时会删除该目录。 +-x | 打印编译期间所用到的其它命令。注意它与`-n`标记的区别。 我们在这里忽略了一些并不常用的或作用于编译器或连接器的标记。在本小节的最后将会对这些标记进行简单的说明。如果读者有兴趣,也可以查看Go语言的官方文档以获取相关信息。 -下面我们就用其中几个标记来查看一下在构建代码包```logging```时创建的临时工作目录的路径: +下面我们就用其中几个标记来查看一下在构建代码包`logging`时创建的临时工作目录的路径: - hc@ubt:~/golang/goc2p/src$ go build -v -work logging - WORK=/tmp/go-build888760008 - logging +```bash +hc@ubt:~/golang/goc2p/src$ go build -v -work logging +WORK=/tmp/go-build888760008 +logging +``` -上面命令的结果输出的第一行是为了编译```logging```包,Go创建的一个临时工作目录,这个目录被创建到了Linux的临时目录下。输出的第二行是对标记```-v```的响应,意味着这个命令执行时仅编译了```logging```包。关于临时工作目录的用途和内容,我们会在讲解```go run```命令和```go test```命令的时候详细说明。 +上面命令的结果输出的第一行是为了编译`logging`包,Go创建的一个临时工作目录,这个目录被创建到了Linux的临时目录下。输出的第二行是对标记`-v`的响应。这意味着此次命令执行时仅编译了`logging`包。关于临时工作目录的用途和内容,我们会在讲解`go run`命令和`go test`命令的时候详细说明。 现在我们再来看看如果强制重新编译会涉及到哪些代码包: - hc@ubt:~/golang/goc2p/src$ go build -a -v -work logging - WORK=/tmp/go-build929017331 - runtime - errors - sync/atomic - math - unicode/utf8 - unicode - sync - io - syscall - strings - time - strconv - os - reflect - fmt - log - logging - -怎么会多编译了这么多代码包呢?代码包```logging```中的代码直接依赖了标准库中的```runtime```包、```strings```包、```fmt```包和```log```包。那么其他的代码包为什么也会被重新编译呢? +```bash +hc@ubt:~/golang/goc2p/src$ go build -a -v -work logging +WORK=/tmp/go-build929017331 +runtime +errors +sync/atomic +math +unicode/utf8 +unicode +sync +io +syscall +strings +time +strconv +reflect +os +fmt +log +logging +``` + +怎么会多编译了这么多代码包呢?可以确定的是,代码包`logging`中的代码直接依赖了标准库中的`runtime`包、`strings`包、`fmt`包和`log`包。那么其他的代码包为什么也会被重新编译呢? 从代码包编译的角度来说,如果代码包A依赖代码包B,则称代码包B是代码包A的依赖代码包(以下简称依赖包),代码包A是代码包B的触发代码包(以下简称触发包)。 -```go build```命令在执行时,编译程序会先查找目标代码包的所有依赖包,以及这些依赖包的依赖包,直至找到最深层的依赖包为止。在此过程中,如果发现有循环依赖的情况,编译程序就会输出错误信息并立即退出。此过程完成之后,所有的依赖关系形成了一棵含有重复元素的依赖树。对于依赖树中的一个节点(代码包),其直接分支节点(依赖包),是按照代码包导入路径的字典序从左到右排列的。最左边的分支节点会最先被编译。编译程序会依此设定每个代码包的编译优先级。 +`go build`命令在执行时,编译程序会先查找目标代码包的所有依赖包,以及这些依赖包的依赖包,直至找到最深层的依赖包为止。在此过程中,如果发现有循环依赖的情况,编译程序就会输出错误信息并立即退出。此过程完成之后,所有的依赖关系也就形成了一棵含有重复元素的依赖树。对于依赖树中的一个节点(代码包)来说,它的直接分支节点(前者的依赖包),是按照代码包导入路径的字典序从左到右排列的。最左边的分支节点会最先被编译。编译程序会依此设定每个代码包的编译优先级。 + +执行`go build`命令的计算机如果拥有多个逻辑CPU核心,那么编译代码包的顺序可能会存在一些不确定性。但是,它一定会满足这样的约束条件:`依赖代码包 -> 当前代码包 -> 触发代码包`。 + +标记`-p n`可以限制编译过程中任务执行的并发数量,`n`默认为当前计算机的CPU逻辑核数。如果在执行`go build`命令时加入标记`-p 1`,那么就可以保证代码包编译顺序严格按照预先设定好的优先级进行。现在我们再来编译`logging`包: + +```bash +hc@ubt:~/golang/goc2p/src$ go build -a -v -work -p 1 logging +WORK=/tmp/go-build114039681 +runtime +errors +sync/atomic +sync +io +math +syscall +time +os +unicode/utf8 +strconv +reflect +fmt +log +unicode +strings +logging +``` + +我们可以认为,以上示例中所显示的代码包的顺序,就是`logging`包直接或间接依赖的代码包按照优先级从高到低排列后的排序。 + +另外,如果在命令中加入标记`-n`,那么编译程序只会输出所用到的命令而不会真正运行。在这种情况下,编译过程不会使用并发模式。 + +在本节的最后,我们对一些并不太常用的标记进行简要的说明: + ++ `-asmflags` + +此标记可以后跟另外一些标记,如`-D`、`-I`、`-S`等。这些后跟的标记用于控制Go语言编译器编译汇编语言文件时的行为。 -执行```go build```命令的计算机如果是多CPU的,那么编译代码包的顺序可能会有一些不确定性。但一定会满足这样的约束条件:```依赖代码包 -> 当前代码包 -> 触发代码包```。 ++ `-buildmode` -标记```-p n```可以限制编译代码包时的并发数量,```n```默认为当前计算机的CPU数量。如果在执行```go build````命令时加入标记```-p 1```,就可以保证代码包编译顺序严格按照预先设定好的优先级进行。现在我们再来构建```logging```包: +此标记用于指定编译模式,使用方式如`-buildmode=default`(这等同于默认情况下的设置)。此标记支持的编译模式目前有6种。借此,我们可以控制编译器在编译完成后生成静态链接库(即.a文件,也就是我们之前说的归档文件)、动态链接库(即.so文件)或/和可执行文件(在Windows下是.exe文件)。 - hc@ubt:~/golang/goc2p/src$ go build -a -v -work -p 1 logging - WORK=/tmp/go-build114039681 - runtime - errors - sync/atomic - sync - io - math - syscall - time - os - unicode/utf8 - strconv - reflect - fmt - log - unicode - strings - logging ++ `-compiler` +此标记用于指定当前使用的编译器的名称。其值可以为`gc`或`gccgo`。其中,gc编译器即为Go语言自带的编译器,而gccgo编译器则为GCC提供的Go语言编译器。而GCC则是GNU项目出品的编译器套件。GNU是一个众所周知的自由软件项目。在开源软件界不应该有人不知道它。好吧,如果你确实不知道它,赶紧去google吧。 -我们可以认为,以上示例中所显示的代码包的顺序,就是```logging```包直接或间接依赖的代码包按照优先级从高到低的排序。 ++ `-gccgoflags` -另外,如果在命令中加入标记```-n```,则编译程序只会输出所用到的命令而不会真正运行。在这种情况下,编译过程不会使用并发模式。 +此标记用于指定需要传递给gccgo编译器或链接器的标记的列表。 -关于```go build```命令可接受但不常用的标记的说明如下: ++ `-gcflags` -+ -ccflags:需要传递给每一个5c、6c或者8c编译器的参数的列表。 +此标记用于指定需要传递给`go tool compile`命令的标记的列表。 -+ -compiler:指定作为运行时编译器的编译器名称。其值可以为gccgo或gc。 ++ `-installsuffix` -+ -gccgoflags:需要传递给每一个gccgo编译器或链接器的参数的列表。 +为了使当前的输出目录与默认的编译输出目录分离,可以使用这个标记。此标记的值会作为结果文件的父目录名称的后缀。其实,如果使用了`-race`标记,这个标记会被自动追加且其值会为`race`。如果我们同时使用了`-race`标记和`-installsuffix`,那么在`-installsuffix`标记的值的后面会再被追加`_race`,并以此来作为实际使用的后缀。 -+ -gcflags:需要传递给每一个5g、6g或者8g编译器的参数的列表。 ++ `-ldflags` -+ -installsuffix;为了使当前的输出的目录与默认的编译输出目录分离,可以使用这个标记。此标记的值会作为结果文件的父目录名称的后缀。实际上,如果使用了```-race```标记,这个值会被自动设置为```race```。如果同时使用了这两个标记,则会在```-installsuffix```标记的值的后面再加上```_race```,以此来作为实际使用的后缀。 +此标记用于指定需要传递给`go tool link`命令的标记的列表。 -+ -ldflags:需要传递给每一个5l、6l或者8l链接器的参数的列表。 ++ `-linkshared` -+ -tags:在实际编译期间需要考虑满足的编译标签(也可被称为编译约束)的列表。可以查看代码包go/build的文档已获得更多的关于编译标签的信息。 +此标记用于与`-buildmode=shared`一同使用。后者会使作为编译目标的非`main`代码包都被合并到一个动态链接库文件中,而前者则会在此之上进行链接操作。 -其中,gccgo是GNU项目针对于Go语言出品的编译器。GNU是一个众所周知的自由软件工程项目。在开源软件界不应该有人不知道它。好吧,如果你确实不知道它,赶紧去google吧。 ++ `-pkgdir` -gc是Go语言的官方编译器。8g、6g和5g分别是gc编译器在x86(32bit)计算架构、x86-64(64bit)计算架构和ARM计算架构的计算机上的编译程序。 +使用此标记可以指定一个目录。编译器会只从该目录中加载代码包的归档文件,并会把编译可能会生成的代码包归档文件放置在该目录下。 -8c、6c和5c分别是Plan 9版本的gc编译器在x86(32bit)计算架构、x86-64(64bit)计算架构和ARM计算架构的计算机上的编译程序。而Plan 9是一个分布式操作系统,由贝尔实验室的计算科学研究中心在1980年代中期至2002年开发,以作为UNIX的后继者。Plan 9操作系统与Go语言的渊源很深。Go语言的三个设计者Robert Griesemer、Rob Pike和Ken Thompson不但是C语言和Unix操作系统的设计者,也同样是Plan 9操作系统的开发者。 ++ `-tags` -8l、6l和5l分别是在x86(32bit)计算架构、x86-64(64bit)计算架构和ARM计算架构的计算机上的链接器。 +此标记用于指定在实际编译期间需要受理的编译标签(也可被称为编译约束)的列表。这些编译标签一般会作为源码文件开始处的注释的一部分,例如,在`$GOROOT/src/os/file_posix.go`开始处的注释为: -顺便提一下,Go语言的专用环境变量GOARCH对应于x86(32bit)计算架构、x86-64(64bit)计算架构和ARM计算架构的值分别为386、amd64和arm。 +```go +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows +``` +最后一行注释即包含了与编译标签有关的内容。大家可以查看代码包`go/build`的文档已获得更多的关于编译标签的信息。 ++ `-toolexec` -## links - * [目录](catalog.md) - * 上一节: [标准命令详解](0.0.md) - * 下一节: [go install](0.2.md) +此标记可以让我们去自定义在编译期间使用一些Go语言自带工具(如`vet`、`asm`等)的方式。 diff --git a/0.10.md b/0.10.md index cc06f8c..5d6c56f 100644 --- a/0.10.md +++ b/0.10.md @@ -1,4 +1,4 @@ -### 0.10 go fix与go tool fix +# go fix与go tool fix 命令```go fix```会把指定代码包的所有Go语言源码文件中的旧版本代码修正为新版本的代码。这里所说的版本即Go语言的版本。代码包的所有Go语言源码文件不包括其子代码包(如果有的话)中的文件。修正操作包括把对旧程序调用的代码更换为对新程序调用的代码、把旧的语法更换为新的语法,等等。 @@ -83,10 +83,3 @@ _表0-15 ```go tool fix```命令的标记说明_ } 上述示例的输出结果中有这样一行提示信息:“standard input: fixed netipv6zone”。其中,“standard input”表明源码是从标准输入而不是源码文件中获取的,而“fixed netipv6zone”则表示名为netipv6zone的修正操作发现输入的源码中有需要修正的地方,并且已经修正完毕。另外,我们还可以看到,输出结果中的代码已经经过了格式化。 - - - -## links - * [目录](catalog.md) - * 上一节: [go fmt与gofmt](0.9.md) - * 下一节: [go vet与go tool vet](0.11.md) diff --git a/0.11.md b/0.11.md index b83f177..1706101 100644 --- a/0.11.md +++ b/0.11.md @@ -1,4 +1,4 @@ -### 0.11 go vet与go tool vet +# go vet与go tool vet 命令```go vet```是一个用于检查Go语言源码中静态错误的简单工具。与大多数Go命令一样,```go vet```命令可以接受```-n```标记和```-x```标记。```-n```标记用于只打印流程中执行的命令而不真正执行它们。```-n```标记也用于打印流程中执行的命令,但不会取消这些命令的执行。示例如下: @@ -1446,11 +1446,4 @@ _表0-19 格式化字符串中动词的格式要求_ 如果标记``-asmdecl```有效(标记值不为```false```),那么命令程序会对汇编语言的源码文件进行检查。对汇编语言源码文件及相应编写规则的解读已经超出了本书的范围,所以我们并不在这里对此项检查进行描述。如果读者有兴趣的话,可以查看此项检查的程序的源码文件asmdecl.go。它在Go语言安装目录的子目录src/cmd/vet下。 -至此,我们对```go vet```命令和```go tool vet```命令进行了全面详细的介绍。之所以花费如此大的篇幅来介绍这两个命令,不仅仅是为了介绍此命令的使用方法,更是因为此命令程序的检查工作涉及到了很多我们在编写Go语言代码时需要避免的“坑”。由此我们也可以知晓应该怎样正确的编写Go语言代码。同时,我们也应该在开发Go语言程序的过程中经常使用```go tool vet```命来检查代码。 - - - -## links - * [目录](catalog.md) - * 上一节: [go fix与go tool fix](0.10.md) - * 下一节: [go tool pprof](0.12.md) \ No newline at end of file +至此,我们对```go vet```命令和```go tool vet```命令进行了全面详细的介绍。之所以花费如此大的篇幅来介绍这两个命令,不仅仅是为了介绍此命令的使用方法,更是因为此命令程序的检查工作涉及到了很多我们在编写Go语言代码时需要避免的“坑”。由此我们也可以知晓应该怎样正确的编写Go语言代码。同时,我们也应该在开发Go语言程序的过程中经常使用```go tool vet```命来检查代码。 \ No newline at end of file diff --git a/0.12.md b/0.12.md index 03a78c6..ab5d22f 100644 --- a/0.12.md +++ b/0.12.md @@ -1,4 +1,4 @@ -### 0.12 go tool pprof +# go tool pprof 我们可以使用```go tool pprof```命令来交互式的访问概要文件的内容。命令将会分析指定的概要文件,并会根据我们的要求为我们提供高可读性的输出信息。 @@ -26,7 +26,7 @@ } } -在函数```startCPUProfile```中,我们首先创建了一个用于存放CPU使用情况记录的文件。这个文件就是CPU概要文件,其绝对路径由```*cpuProfile```的值表示。然后,我们把这个文件的实例作为参数传入到函数```pprof.StartCPUProfile``中。如果此函数没有返回错误,就说明记录操作已经开始。需要注意的是,只有CPU概要文件的绝对路径有效时此函数才会开启记录操作。 +在函数```startCPUProfile```中,我们首先创建了一个用于存放CPU使用情况记录的文件。这个文件就是CPU概要文件,其绝对路径由```*cpuProfile```的值表示。然后,我们把这个文件的实例作为参数传入到函数```pprof.StartCPUProfile```中。如果此函数没有返回错误,就说明记录操作已经开始。需要注意的是,只有CPU概要文件的绝对路径有效时此函数才会开启记录操作。 如果我们想要在某一时刻停止CPU使用情况记录操作,就需要调用下面这个函数: @@ -520,7 +520,7 @@ _表0-23 pprof工具在交互模式下支持的命令_ gv -scale 0 (pprof) sh: 1: gv: not found -现在,输出信息有提示我们没有找到命令gv。gv是自由软件工程项目GNU(GNU's Not Unix)中的一款开源软件,用来以图形化的方式查看PDF文档。我们以同样的方式安装它。在Debian的Linux发行版及其衍生版下,执行命令```sudo apt-get install gv```,在Redhat的Linux发行版及其衍生版下,执行命令```yum install gv```。软件gv被安装好后,我们再次执行gv命令。在运行着图形界面软件的Linux操作系统下,会弹出这样一个窗口。如图5-3。 +现在,输出信息有提示我们没有找到命令gv。gv是自由软件工程项目GNU(GNU's Not Unix)中的一款开源软件,用来以图形化的方式查看PDF文档。我们以同样的方式安装它。在Debian的Linux发行版及其衍生版下,执行命令```sudo apt-get install gv```,在Redhat的Linux发行版及其衍生版下,执行命令```yum install gv```。软件gv被安装好后,我们再次执行gv命令。在运行着图形界面软件的Linux操作系统下,会弹出这样一个窗口。如图0-3。 ![pprof工具的gv命令的执行结果](images/0-3.jpg) @@ -790,10 +790,3 @@ _图0-4 使用kcachegrind工具查看概要数据_ 在本小节中,我们只使用```go tool pprof```命令对CPU概要文件进行了查看和分析。读者可以试着对内存概要文件和程序阻塞概要文件进行分析。 相对于普通的编程方式来讲,并发编程都是复杂的。所以,我们就更需要像pprof这样的工具为我们保驾护航。大家可以将本小节当作一篇该工具的文档,并在需要时随时查看。 - - - -## links - * [目录](catalog.md) - * 上一节: [go vet与go tool vet](0.11.md) - * 下一节: [go tool cgo](0.13.md) \ No newline at end of file diff --git a/0.13.md b/0.13.md index 8a5417c..c3e1612 100644 --- a/0.13.md +++ b/0.13.md @@ -1,4 +1,4 @@ -### 0.13 go tool cgo +# go tool cgo cgo也是一个Go语言自带的特殊工具。一般情况下,我们使用命令```go tool cgo```来运行它。这个工具可以使我们创建能够调用C语言代码的Go语言源码文件。这使得我们可以使用Go语言代码去封装一些C语言的代码库,并提供给Go语言代码或项目使用。 @@ -52,7 +52,7 @@ cgo也是一个Go语言自带的特殊工具。一般情况下,我们使用命 上面这段代码被保存在了Go语言库源码文件math.go中,并与源码文件rand.go在同一个代码包目录。在Go语言函数```Sqrt```中的```C.sqrt```是一个在C语言标准代码库math.h中的函数。它会返回参数的平方根。但是在第一行代码中,我们接收由函数C.sqrt返回的两个值。其中,第一个值即为C语言函数```sqrt```的结果。而第二个值就是我们上面所说的那个作为错误提示信息的变量。实际上,这个变量的类型是Go语言的```error```接口类型。它包装了一个C语言的全局变量errno。这个全局变量被定义在了C语言代码库errno.h中。cgo工具在为我们生成C语言源码文件时会默认引入两个C语言标准代码库,其中一个就是errno.h。所以我们并不用在Go语言源码文件中使用指令符#include显式的引入这个代码库。cgo工具默认为我们引入的另一个是C语言标准代码库string.h。它包含了很多用于字符串处理和内存处理的函数。 -在我们以“C.*”的形式调用C语言代码库时,有一点需要特别注意。在C语言中,如果一个函数的参数是一个具有固定尺寸的数组,那么实际上这个函数所需要的是指向这个数组的第一个元素的指针。C编译器能够正确识别和处理这个调用惯例。它可以自行获取到这个指针并传给函数。但是,这在我们使用cgo工具调用C语言代码库时是行不通的。在Go语言中,我们必须显式的将这个指向数组的第一个元素的指针传递给C语言的函数,像这样:``C.func1(&x[0])````。 +在我们以“C.*”的形式调用C语言代码库时,有一点需要特别注意。在C语言中,如果一个函数的参数是一个具有固定尺寸的数组,那么实际上这个函数所需要的是指向这个数组的第一个元素的指针。C编译器能够正确识别和处理这个调用惯例。它可以自行获取到这个指针并传给函数。但是,这在我们使用cgo工具调用C语言代码库时是行不通的。在Go语言中,我们必须显式的将这个指向数组的第一个元素的指针传递给C语言的函数,像这样:```C.func1(&x[0])```。 另一个需要特别注意的地方是,在C语言中没有像Go语言中独立的字符串类型。C语言使用最后一个元素为‘\0’的字符数组来代表字符串。在Go语言的字符串和C语言的字符串之间进行转换的时候,我们就需要用到代码包```C```中的```C.C.CString```、```C.GoString```和```C.GoStringN```等函数。这些转换操作是通过对字符串数据的拷贝来完成的。Go语言内存管理器并不能感知此类内存分配操作。因为它们是由C语言代码引发的。所以,我们在使用与```C.CString```函数类似的会导致内存分配操作的函数之后,需要调用代码包C的free函数以手动的释放内存。这里有一个小技巧,即我们可以把对```C.free```函数的调用放到```defer```语句中或者放入在defer之后的匿名函数中。这样就可以保证在退出当前函数之前释放这些被分配的内存了。请看下面这个示例: @@ -68,7 +68,7 @@ cgo也是一个Go语言自带的特殊工具。一般情况下,我们使用命 在前面我们已经提到过,在导入代码包C的语句之上可以加入若干个为cgo工具而写的若干注释行(也被叫做序文)。并且,以#include和一个空格开始的注释行可以用来引入一个C语言的接口文件。我们也把序文中这种形式的字符串叫做指令符。指令符```#cgo```的用途是为编译器和连接器提供标记。这些标记在编译当前源码文件中涉及到代码包```C```的那部分代码时会被用到。 -标记```CFLAGS```和```LDFLAGS``可以被放在指令符```#cgo```之后,并用于定制编译器gcc的行为。gcc(GNU Compiler Collection,GNU编译器套装),是一套由GNU开发的开源的编程语言编译器。它是GNU项目的关键部分,也是类Unix操作系统(也包括Linux操作系统)中的标准编译器。gcc(特别是其中的C语言编译器)也常被认为是跨平台编译器的事实标准。gcc原名为GNU C语言编译器(GNU C Compiler),因为它原本只能处理C语言。不过,gcc变得可以处理更多的语言。现在,gcc中包含了很多针对特定编程语言的编译器。我们在本节第一小节的末尾提及的gccgo就是这款套件中针对Go语言的编译器。标记CFLAGS可以指定用于gcc中的C编译器的选项。它尝尝用于指定头文件(.h文件)的路径。而标记LDFLAGS则可以指定gcc编译器会用到的一些优化参数,也可以用来告诉链接器需要用到的C语言代码库文件的位置。 +标记```CFLAGS```和```LDFLAGS```可以被放在指令符```#cgo```之后,并用于定制编译器gcc的行为。gcc(GNU Compiler Collection,GNU编译器套装),是一套由GNU开发的开源的编程语言编译器。它是GNU项目的关键部分,也是类Unix操作系统(也包括Linux操作系统)中的标准编译器。gcc(特别是其中的C语言编译器)也常被认为是跨平台编译器的事实标准。gcc原名为GNU C语言编译器(GNU C Compiler),因为它原本只能处理C语言。不过,gcc变得可以处理更多的语言。现在,gcc中包含了很多针对特定编程语言的编译器。我们在本节第一小节的末尾提及的gccgo就是这款套件中针对Go语言的编译器。标记CFLAGS可以指定用于gcc中的C编译器的选项。它尝尝用于指定头文件(.h文件)的路径。而标记LDFLAGS则可以指定gcc编译器会用到的一些优化参数,也可以用来告诉链接器需要用到的C语言代码库文件的位置。 为了清晰起见,我们可以把这些标记及其值拆分成多个注释行,并均以指令符```#cgo```作为前缀。另外,在指令符```#cgo```和标记之间,我们也可以加入一些可选的内容,即环境变量GOOS和GOARCH中的有效值。这样,我们就可以使这些标记只在某些操作系统和/或某些计算架构的环境下起作用了。示例如下: @@ -500,8 +500,8 @@ Go语言可以使它的函数被C语言代码所用。这是通过使用一个 hc@ubt:~/golang/goc2p/basic/cgo/lib$ go build # basic/cgo/lib - /tmp/go-build477634277/basic/cgo/lib/_obj/go_export.cgo2.o: In function `_cgo_cc103c85817e_Cfunc_CFunction1': - ./go_export.go:34: undefined reference to `CFunction1' + /tmp/go-build477634277/basic/cgo/lib/_obj/go_export.cgo2.o: In function '_cgo_cc103c85817e_Cfunc_CFunction1': + ./go_export.go:34: undefined reference to 'CFunction1' collect2: ld return 1 构建并没有成功完成。根据错误提示信息我们获知,C语言函数```CFunction1```未被定义。这个问题的原因是我们并没有在Go语言源码文件go_export.go的序文中写入C语言函数```CFunction1```的实现,也即未对它进行定义。我们之前说过,在这种情况下,对应函数的定义应该被放到其它Go语言源码文件的序文或者C语言源码文件中。现在,我们在当前目录下创建一个新的Go语言源码文件go_export_def.go。其内容如下: @@ -528,7 +528,7 @@ Go语言可以使它的函数被C语言代码所用。这是通过使用一个 显然,这次的构建成功完成。当然单独构建代码包```basic/cgo/lib```并不是必须的。我们在这里是为了检查该代码包中的代码(包括Go语言代码和C语言代码)是否都能够被正确编译。 -还记得goc2p项目的代码包basic/cgo中的命令源码文件cgo_demo.go。现在我们在它的main函数的最后加入一行新代码:```cgo.CallCFunc()```,即调用在代码包``basic/cgo/lib```中的库源码文件go_export.go的函数。然后,我们运行这个命令源码文件: +还记得goc2p项目的代码包basic/cgo中的命令源码文件cgo_demo.go。现在我们在它的main函数的最后加入一行新代码:```cgo.CallCFunc()```,即调用在代码包```basic/cgo/lib```中的库源码文件go_export.go的函数。然后,我们运行这个命令源码文件: hc@ubt:~/golang/goc2p/basic/cgo$ go run cgo_demo.go The square root of 2.330000 is 1.526434. @@ -556,10 +556,3 @@ Go语言可以使它的函数被C语言代码所用。这是通过使用一个 GoFunction1() is called. 在上例中,我们首先删除了代码包```basic/cgo/lib```目录下的子目录_obj,以此来保证原始的测试环境。然后,我们直接运行了命令源码文件cgo_demo.go。在这个源码文件中,包含了对代码包```basic/cgo/lib```中函数的调用语句,而在这些函数中又包含了对代码包C的引用。从输出信息我们可以看出,命令源码文件cgo_demo.go的运行成功的完成了。这也验证了标准go命令在这方面的功能。不过,有时候我们还是很有必要单独使用```go tool cgo```命令,比如对相关的Go语言代码和C语言代码的功能进行验证或者需要通过标记定制化运行cgo工具的时候。另外,如果我们通过标准go命令构建或者安装直接或间接导入了代码C的命令源码文件,那么在生成的可执行文件中就会包含动态导入数据和动态链接器信息。我们可以使用```go tool cgo```命令查看可执行文件中的这些信息。 - - - -## links - * [目录](catalog.md) - * 上一节: [go tool pprof](0.12.md) - * 下一节: [go env](0.14.md) \ No newline at end of file diff --git a/0.14.md b/0.14.md index a222c22..a4b836d 100644 --- a/0.14.md +++ b/0.14.md @@ -1,4 +1,4 @@ -### 0.14 go env +# go env 命令```go env```用于打印Go语言的环境信息。其中的一些信息我们在之前已经多次提及,但是却没有进行详细的说明。在本小节,我们会对这些信息进行深入介绍。我们先来看一看```go env```命令情况下都会打印出哪些Go语言通用环境信息。 @@ -211,9 +211,3 @@ GOTOOLDIR的值指明了Go工具目录的绝对路径。根据GOROOT、GOHOSTOS 上例的```go env```命令的输出信息中,每一行对一个环境信息的值,且其顺序与我们输入的环境信息名称的顺序一致。比如,386为环境信息GOARCH,而8则是环境信息GOCHAR的值。 ```go env```命令能够让我们对当前的Go语言环境进行简要的了解。通过它,我们也可以对当前安装的Go语言的环境设置进行简单的检查。 - - - -## links - * [目录](catalog.md) - * 上一节: [go tool cgo](0.13.md) \ No newline at end of file diff --git a/0.2.md b/0.2.md index f6866e1..fe19f11 100644 --- a/0.2.md +++ b/0.2.md @@ -1,19 +1,21 @@ -### 0.2 go install +# go install -命令```go install```用于编译并安装指定的代码包及它们的依赖包。当指定的代码包的依赖包还没有被编译和安装时,该命令会先去处理依赖包。与```go build```命令一样,传给```go install```命令的代码包参数,应该以导入路径的形式提供。并且,```go build```命令的绝大多数标记也都可以用于```go install```命令。实际上,```go install```命令只比```go build```命令多做了一件事,即:安装编译后的结果文件到指定目录。 +命令`go install`用于编译并安装指定的代码包及它们的依赖包。当指定的代码包的依赖包还没有被编译和安装时,该命令会先去处理依赖包。与`go build`命令一样,传给`go install`命令的代码包参数应该以导入路径的形式提供。并且,`go build`命令的绝大多数标记也都可以用于`go install`命令。实际上,`go install`命令只比`go build`命令多做了一件事,即:安装编译后的结果文件到指定目录。 -在对```go install```命令进行详细说明之前,让我们先回顾一下goc2p的目录结构。为了节省篇幅,我们在这里隐藏了代码包中的源码文件。如下: +在对`go install`命令进行详细说明之前,让我们先回顾一下goc2p的目录结构。为了节省篇幅,我在这里隐藏了代码包中的源码文件。如下: - $HOME/golang/goc2p: - bin/ - pkg/ - src/ - cnet/ - logging/ - helper/ - ds/ - pkgtool/ +```go +$HOME/golang/goc2p: + bin/ + pkg/ + src/ + cnet/ + logging/ + helper/ + ds/ + pkgtool/ +``` 我们看到,goc2p项目中有三个子目录,分别是bin目录、pkg目录和src目录。现在只有src目录中包含了一些目录,而其他两个目录都是空的。 @@ -21,147 +23,154 @@ **安装代码包** -如果```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 - -我们刚刚说过,执行```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 - -可以看到,代码包```pkgtool```仅仅依赖了标准库中的代码包。 +如果`go install`命令后跟的代码包中仅包含库源码文件,那么`go install`命令会把编译后的结果文件保存在源码文件所在工作区的pkg目录下。对于仅包含库源码文件的代码包来说,这个结果文件就是对应的代码包归档文件(也叫静态链接库文件,名称以.a结尾)。相比之下,我们在使用`go build`命令对仅包含库源码文件的代码包进行编译时,是不会在当前工作区的src目录以及pkg目录下产生任何结果文件的。结果文件会出于编译的目的被生成在临时目录中,但并不会使当前工作区目录产生任何变化。 + +如果我们在执行`go install`命令时不后跟任何代码包参数,那么命令将试图编译当前目录所对应的代码包。比如,我们现在要安装代码包`pkgtool`: + +```go +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`: + +```go +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目录中多了一个子目录。读过本书第二章的读者应该已经知道,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 - -请注意,我们是在代码包```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```依赖于代码包```logging```,所以当代码包```logging```被成功安装之后,代码包```cnet/ctcp```才会被安装。 - -还有一个问题:上述的安装过程涉及到了那么多代码包,那为什么goc2p项目的pkg目录中只包含该项目中代码包的归档文件呢?实际上,```go install```命令会把标准库中的代码包的归档文件存放到Go根目录的pkg目录中,而把指定代码包依赖的第三方项目的代码包的归档文件存放到那个项目的pkg目录下。这样就实现了Go语言标准库代码包的归档文件与用户代码包的归档文件,以及处在不同工作区的用户代码包的归档文件之间的彻底分离。 +```go +$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`包: + +```go +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`命令。但是需要注意的是,本地代码包路径只能以目录相对路径的形式呈现,而不能使用目录绝对路径。请看下面的示例: + +```go +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的项目目录: + +```go +$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,如下: +除了安装代码包之外,`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 +```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的值: +这次我们没能成功安装。该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 +```go +hc@ubt:~/golang/goc2p/src$ echo $GOPATH +/home/hc/golang/lib:/home/hc/golang/goc2p +``` 环境变量GOPATH的值中确实包含了goc2p项目的根目录。这到底是怎么回事呢? -作者通过查看Go命令的源码文件($GOROOT/src/go/*.go)找到了其根本原因。在上一小节我们提到过,在环境变量GOPATH中包含多个工作区目录路径时,我们需要在编译命令源码文件前先对环境变量GOBIN进行设置。实际上,这个环境变量所指的目录路径就是命令程序生成的结果文件的存放目录。```go install```命令会把相应的可执行文件放置到这个目录中。 +我通过查看Go命令的源码文件找到了其根本原因。在上一小节我们提到过,在环境变量GOPATH中包含多个工作区目录路径时,我们需要在编译命令源码文件前先对环境变量GOBIN进行设置。实际上,这个环境变量所指的目录路径就是命令程序生成的结果文件的存放目录。`go install`命令会把相应的可执行文件放置到这个目录中。 -由于命令```go build```在编译库源码文件后不会产生任何结果文件,所以自然也不用会在意结果文件的存放目录。在该命令编译单一的命令源码文件时,在结果文件存放目录无效的情况下会将结果文件(也就是可执行文件)存放到执行该命令时所在的目录下。因此,即使环境变量GOBIN的值无效,我们在执行```go build```命令时也不会见到这个错误提示信息。 +由于命令`go build`在编译库源码文件后不会产生任何结果文件,所以自然也不用会在意结果文件的存放目录。在该命令编译单一的命令源码文件或者包含一个命令源码文件和多个库源码文件时,在结果文件存放目录无效的情况下会将结果文件(也就是可执行文件)存放到执行该命令时所在的目录下。因此,即使环境变量GOBIN的值无效,我们在执行`go build`命令时也不会见到这个错误提示信息。 -然而,```go install```命令中一个很重要的步骤就是将结果文件(归档文件或者可执行文件)存放到相应的目录中。所以,命令```go install```在安装命令源码文件时,如果环境变量GOBIN的值无效,则它会在最后检查结果文件存放目录的时候发现这一问题,并打印与上述示例所示内容类似的错误提示信息,最后直接退出。 +然而,`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 - -而且,在我们为环境变量GOBIN设置了正确的值之后,这个错误提示信息仍然会出现。这是因为,只有在安装命令源码文件的时候,命令程序才会将环境变量GOBIN的值作为结果文件的存放目录。而在安装库源码文件时,在命令程序内部的代表结果文件存放目录路径的变量不会被赋值。最后,命令程序会发现它依然是个无效的空值。所以,命令程序会同样返回一个关于“无安装位置”的错误。这就引出一个结论,我们只能使用安装代码包的方式来安装库源码文件,而不能在```go install```命令罗列并安装它们。另外,```go install```命令目前无法接受标记```-o```以自定义结果文件的存放位置。这也从侧面说明了```go install```命令当前还不支持针对库源码文件的安装操作。 - -单从上述问题来讲,Go工具在执行错误识别及其提示信息的细分方面还没有做到最好。 - +```go +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`命令不支持针对库源码文件的安装操作。 -## links - * [目录](catalog.md) - * 上一节: [go build](0.1.md) - * 下一节: [go get](0.3.md) +至此,我们对怎样用`go install`命令来安装代码包以及命令源码文件进行了说明。如果你已经熟知了`go build`命令,那么理解这些内容应该不在话下。 diff --git a/0.3.md b/0.3.md index 6359bb7..1125a1c 100644 --- a/0.3.md +++ b/0.3.md @@ -1,318 +1,215 @@ -### 0.3 go get +# go get +```go +hc@ubt:~$ go get github.com/hyper-carrot/go_lib/logging +``` +命令`go get`可以根据要求和实际情况从互联网上下载或更新指定的代码包及其依赖包,并对它们进行编译和安装。在上面这个示例中,我们从著名的代码托管站点Github上下载了一个项目(或称代码包),并安装到了环境变量GOPATH中包含的第一个工作区中。与此同时,我们也知道了这个代码包的导入路径就是github.com/hyper-carrot/go_lib/logging。 - hc@ubt:~$ go get github.com/hyper-carrot/go_lib - - -命令```go get```可以根据要求和实际情况从互联网上下载或更新指定的代码包及其依赖包,并对它们进行编译和安装。在上面这个示例中,我们从著名的代码托管站点Github上下载了一个项目(或称代码包),并安装到了环境变量GOPATH中包含的第一个工作区中。在本机中,这个代码包的导入路径就是github.com/hyper-carrot/go_lib。 - -一般情况下,为了分离自己与第三方的代码,我们会设置两个及以上的工作区。我们现在新建一个目录路径为~/golang/lib的工作区,并把这个工作区路径作为环境变量GOPATH值中的第一个目录路径。注意,环境变量GOPATH中包含的路径不能与环境变量GOROOT的值重复。如此一来,如果我们再使用```go get```命令下载和安装代码包,那么这些代码包就都会被安装在这个新的工作区中了。我们暂且把这个工作区叫做Lib工作区。假如我们在Lib工作区建立和设置完毕之后运行了上面示例中的命令,那么这个代码包就应该会被保存在Lib工作的src目录下,并且已经被安装妥当,如下所示: +一般情况下,为了分离自己与第三方的代码,我们会设置两个或更多的工作区。我们现在有一个目录路径为/home/hc/golang/lib的工作区,并且它是环境变量GOPATH值中的第一个目录路径。注意,环境变量GOPATH中包含的路径不能与环境变量GOROOT的值重复。好了,如果我们使用`go get`命令下载和安装代码包,那么这些代码包都会被安装在上面这个工作区中。我们暂且把这个工作区叫做Lib工作区。在我们运行`go get github.com/hyper-carrot/go_lib/logging`之后,这个代码包就应该会被保存在Lib工作的src目录下,并且已经被安装妥当,如下所示: - $HOME/golang/lib: - bin/ - pkg/ - linux_386/ - github.com/ - hyper-carrot/ - go_lib.a - src/ +```go +/home/hc/golang/lib: + bin/ + pkg/ + linux_386/ github.com/ + hyper-carrot/ + go_lib/ + logging.a + src/ + github.com/ hyper-carrot/ - go_lib/ - ... + go_lib/ + logging/ + ... +``` -从另一个角度来说,如果我们想把一个项目上传到Github站点(或其他代码托管站点)上,并被其他人使用的话,那么我们就应该把这个项目当做一个代码包来看待。我们在之前已经提到过原因,```go get```命令会将项目下的所有子目录和源码文件存放到工作区src目录下,而src目录下的所有子目录都会是某个代码包导入路径的一部分或者全部。也就是说,我们应该直接在项目目录下存放子代码包和源码文件,并且直接存放在项目目录下的源码文件所声明的包名应该与该项目名相同(除非它是命令源码文件)。这样做可以让其他人使用```go get```命令从Github站点上下载你的项目之后直接就能使用它。 +另一方面,如果我们想把一个项目上传到Github网站(或其他代码托管网站)上并被其他人使用的话,那么我们就应该把这个项目当做一个代码包来看待。其实我们在之前已经提到过原因,`go get`命令会将项目下的所有子目录和源码文件存放到第一个工作区的src目录下,而src目录下的所有子目录都会是某个代码包导入路径的一部分或者全部。也就是说,我们应该直接在项目目录下存放子代码包和源码文件,并且直接存放在项目目录下的源码文件所声明的包名应该与该项目名相同(除非它是命令源码文件)。这样做可以让其他人使用`go get`命令从Github站点上下载你的项目之后直接就能使用它。 -实际上,像goc2p项目这样直接以项目根目录的路径作为工作区路径的做法是不被推荐的。之所以这样做主要是想让读者更容易的理解Go语言的工程结构和工作区概念,也可以让读者看到另一种项目结构。 +实际上,像goc2p项目这样直接以项目根目录的路径作为工作区路径的做法是不被推荐的。之所以这样做主要是想让读者更容易的理解Go语言的工程结构和工作区概念,也可以让读者看到另一种项目结构。当然,如果你的项目使用了[gb](https://github.com/constabulary/gb)这样的工具那就是另外一回事了。这样的项目的根目录就应该被视为一个工作区(但是你不必把它加入到GOPATH环境变量中)。它应该由`git clone`下载到Go语言工作区之外的某处,而不是使用`go get`命令。 -** 远程导入路径分析** +**远程导入路径分析** -实际上,```go get```命令所做的动作也被叫做代码包远程导入,而传递给该命令的作为代码包导入路径的参数又被叫做代码包远程导入路径。 +实际上,`go get`命令所做的动作也被叫做代码包远程导入,而传递给该命令的作为代码包导入路径的那个参数又被叫做代码包远程导入路径。 -实际上,```go get```命令不仅可以从像Github这样著名的代码托管站点上下载代码包,还可以从任何命令支持的代码版本控制系统(英文为Version Control System,简称为VCS)检出代码包。任何代码托管站点都是通过某个或某些代码版本控制系统来提供代码上传下载服务的。所以,更严格的讲,```go get```命令所做的是从代码版本控制系统的远程仓库中检出/更新代码包并对其进行编译和安装。 +`go get`命令不仅可以从像Github这样著名的代码托管站点上下载代码包,还可以从任何命令支持的代码版本控制系统(英文为Version Control System,简称为VCS)检出代码包。任何代码托管站点都是通过某个或某些代码版本控制系统来提供代码上传下载服务的。所以,更严格地讲,`go get`命令所做的是从代码版本控制系统的远程仓库中检出/更新代码包并对其进行编译和安装。 该命令所支持的VCS的信息如下表: _表0-2 ```go get```命令支持的VCS_ - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 名称 - - 主命令 - - 说明 -
- Mercurial - - hg - - Mercurial是一种轻量级分布式版本控制系统,采用Python语言实现,易于学习和使用,扩展性强。 -
- Git - - git - - Git最开始是Linux Torvalds为了帮助管理 Linux 内核开发而开发的一个开源的分布式版本控制软件。但现在已被广泛使用。它是被用来进行有效、高速的各种规模项目的版本管理。 -
- Subversion - - svn - - Subversion是一个版本控制系统,也是第一个将分支概念和功能纳入到版本控制模型的系统。但相对于Git和Mercurial而言,它只算是传统版本控制系统的一员。 -
- Bazaar - - bzr - - Bazaar是一个开源的分布式版本控制系统。但相比而言,用它来作为VCS的项目并不多。 -
- -```go get ```命令在检出代码包之前必须要知道代码包远程导入路径所对应的版本控制系统和远程仓库的URL。 - -如果该代码包在本地工作区中已经存在,则会直接通过分析其路径来确定这几项信息。```go get ```命令支持的几个版本控制系统都有一个共同点,那就是会在检出的项目目录中存放一个元数据目录,名称为“.”前缀加其主命令名。例如,Git会在检出的项目目录中加入名为“.git”的子目录。所以,这样就很容易判定代码包所用的版本控制系统。另外,又由于代码包已经存在,我们只需通过代码版本控制系统的更新命令来更新代码包,因此也就不需要知道其远程仓库的URL了。对于已存在于本地工作区的代码包,除非要求更新代码包,否则```go get ```命令不会进行重复下载。如果想要求更新代码包,可以在执行```go get```命令时加入```-u```标记。这一标记会稍后介绍。 - -如果本地工作区中不存在该代码包,那么就只能通过对代码包远程导入路径进行分析来获取相关信息了。首先,```go get```命令会对代码包远程导入路径进行静态分析。为了使分析过程更加方便快捷,```go get```命令程序中已经预置了著名代码托管站点的信息。如下表: +名称 | 主命令 | 说明 +---------- | ------- | ----- +Mercurial | hg | Mercurial是一种轻量级分布式版本控制系统,采用Python语言实现,易于学习和使用,扩展性强。 +Git | git | Git最开始是Linux Torvalds为了帮助管理 Linux 内核开发而开发的一个开源的分布式版本控制软件。但现在已被广泛使用。它是被用来进行有效、高速的各种规模项目的版本管理。 +Subversion | svn | Subversion是一个版本控制系统,也是第一个将分支概念和功能纳入到版本控制模型的系统。但相对于Git和Mercurial而言,它只算是传统版本控制系统的一员。 +Bazaar | bzr | Bazaar是一个开源的分布式版本控制系统。但相比而言,用它来作为VCS的项目并不多。 + +`go get `命令在检出代码包之前必须要知道代码包远程导入路径所对应的版本控制系统和远程仓库的URL。 + +如果该代码包在本地工作区中已经存在,则会直接通过分析其路径来确定这几项信息。`go get `命令支持的几个版本控制系统都有一个共同点,那就是会在检出的项目目录中存放一个元数据目录,名称为“.”前缀加其主命令名。例如,Git会在检出的项目目录中加入一个名为“.git”的子目录。所以,这样就很容易判定代码包所用的版本控制系统。另外,又由于代码包已经存在,我们只需通过代码版本控制系统的更新命令来更新代码包,因此也就不需要知道其远程仓库的URL了。对于已存在于本地工作区的代码包,除非要求强行更新代码包,否则`go get`命令不会进行重复下载。如果想要强行更新代码包,可以在执行`go get`命令时加入`-u`标记。这一标记会稍后介绍。 + +如果本地工作区中不存在该代码包,那么就只能通过对代码包远程导入路径进行分析来获取相关信息了。首先,`go get`命令会对代码包远程导入路径进行静态分析。为了使分析过程更加方便快捷,`go get`命令程序中已经预置了几个著名代码托管网站的信息。如下表: _表0-3 预置的代码托管站点的信息_ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 名称 - - 主域名 - - 支持的VCS - - 代码包远程导入路径示例 -
- Bitbucket - - bitbucket.org - - Git, Mercurial - - bitbucket.org/user/project -
- bitbucket.org/user/project/sub/directory -
- GitHub - - github.com - - Git - - github.com/user/project -
- github.com/user/project/sub/directory -
- Google Code - - code.google.com - - Git, Mercurial, Subversion - - code.google.com/p/project -
- code.google.com/p/project/sub/directory -
- code.google.com/p/project.subrepository -
- code.google.com/p/project.subrepository/sub/directory -
- Launchpad - - launchpad.net - - Bazaar - - launchpad.net/project -
- launchpad.net/project/series -
- launchpad.net/project/series/sub/directory -
- launchpad.net/~user/project/branch -
- launchpad.net/~user/project/branch/sub/directory -
- -一般情况下,代码包远程导入路径中的第一个元素就是代码托管站点的主域名。在静态分析的时候,```go get```命令会将代码包远程导入路径与预置的代码托管站点的主域名进行匹配。如果匹配成功,则在对代码包远程导入路径的初步检查后返回正常的返回值或错误信息。如果匹配不成功,则会再对代码包远程导入路径进行动态分析。至于动态分析的过程,我们就不在这里赘述了。 - -如果对代码包远程导入路径的静态分析或/和动态分析成功并获取到对应的版本控制系统和远程仓库URL,那么```go get```命令就会进行代码包检出或更新的操作。随后,```go get```命令会在必要时以同样的方式检出或更新这个代码包的所有依赖包。 -**命令特有标记** -命令```go get```可以接受所有可用于```go build```命令和```go install```命令的标记。这是因为```go get```命令的内部步骤中完全包含了编译和安装这两个动作。另外,```go get```命令还有一些特有的标记,如下表所示: +名称 | 主域名 | 支持的VCS | 代码包远程导入路径示例 +--------------------------- | --------------- | -------------------------- | -------- +Bitbucket | bitbucket.org | Git, Mercurial | bitbucket.org/user/project
bitbucket.org/user/project/sub/directory +GitHub | github.com | Git | github.com/user/project
github.com/user/project/sub/directory +Google Code Project Hosting | code.google.com | Git, Mercurial, Subversion | code.google.com/p/project
code.google.com/p/project/sub/directory
code.google.com/p/project.subrepository
code.google.com/p/project.subrepository/sub/directory +Launchpad | launchpad.net | Bazaar | launchpad.net/project
launchpad.net/project/series
launchpad.net/project/series/sub/directory
launchpad.net/~user/project/branch
launchpad.net/~user/project/branch/sub/directory +IBM DevOps Services | hub.jazz.net | Git | hub.jazz.net/git/user/project
hub.jazz.net/git/user/project/sub/directory -_表0-4 ```go get```命令的特有标记说明_ - - - - - - - - - - - - - - - - - -
- 标记名称 - - 标记描述 -
- -d - - 让命令只执行下载动作,而不执行安装动作。 -
- -fix - - 让命令在下载代码包后先执行修正动作,而后再进行编译和安装。 -
- -u - - 让命令利用网络来更新已有代码包及其依赖包。默认情况下,该命令只会从网络上下载本地不存在的代码包,而不会更新已有的代码包。 -
- -为了更好的理解这几个特有标记,我们先清除Lib工作区的src目录和pkg目录中的所有子目录和文件。现在我们使用带有```-d```标记的```go get```命令来下载同样的代码包: - - - hc@ubt:~$ go get -d github.com/hyper-carrot/go_lib +一般情况下,代码包远程导入路径中的第一个元素就是代码托管网站的主域名。在静态分析的时候,`go get`命令会将代码包远程导入路径与预置的代码托管站点的主域名进行匹配。如果匹配成功,则在对代码包远程导入路径的初步检查后返回正常的返回值或错误信息。如果匹配不成功,则会再对代码包远程导入路径进行动态分析。至于动态分析的过程,我就不在这里详细展开了。 +如果对代码包远程导入路径的静态分析或/和动态分析成功并获取到对应的版本控制系统和远程仓库URL,那么`go get`命令就会进行代码包检出或更新的操作。随后,`go get`命令会在必要时以同样的方式检出或更新这个代码包的所有依赖包。 -现在,让我们再来看一下Lib工作区的目录结构: +**自定义代码包远程导入路径** - $HOME/golang/lib: - bin/ - pkg/ - src/ - github.com/ - hyper-carrot/ - go_lib/ - ... +如果你想把你编写的(被托管在不同的代码托管网站上的)代码包的远程导入路径统一起来,或者不希望让你的代码包中夹杂某个代码托管网站的域名,那么你可以选择自定义你的代码包远程导入路径。这种自定义的实现手段叫做“导入注释”。导入注释的写法示例如下: -我们可以看到,```go get```命令只将代码包下载到了Lib工作区(环境变量GOPATH中的第一个目录)的src目录,而没有进行后续的编译和安装动作。 +```go +package analyzer // import "hypermind.cn/talon/analyzer" +``` -我们知道,绝大多数计算机编程语言在进行升级和演进过程中,不可能保证100%的向后兼容(Backward Compatibility)。在计算机世界中,向后兼容是指在一个程序或者代码库在更新到较新的版本后,用旧的版本程序创建的软件和系统仍能被正常操作或使用,或在旧版本的代码库的基础上编写的程序仍能正常编译运行的能力。Go语言的开发者们已想到了这点,并提供了官方的代码升级工具——```fix```。```fix```工具可以修复因Go语言规范变更而造成的语法级别的错误。关于fix工具,我们将放在本节的稍后位置予以说明。 +代码包`analyzer`实际上属于我的一个网络爬虫项目。这个项目的代码被托管在了Github网站上。它的网址是:[https://github.com/hyper-carrot/talon](https://github.com/hyper-carrot/talon)。如果用标准的导入路径来下载`analyzer`代码包的话,命令应该这样写`go get github.com/hyper-carrot/talon/analyzer`。不过,如果我们像上面的示例那样在该代码包中的一个源码文件中加入导入注释的话,这样下载它就行不通了。我们来看一看这个导入注释。 -假设我们本机安装的Go语言版本是1.3,但我们的程序需要用到一个很早之前用Go语言的0.9版本开发的代码包。那么我们在使用```go get```命令的时候可以加入```-fix```标记。这个标记的作用是在检出代码包之后,先对该代码包中不符合Go语言1.3版本的语言规范的语法进行修正,然后再下载它的依赖包,最后再对它们进行编译和安装。 +导入注释的写法如同一条代码包导入语句。不同的是,它出现在了单行注释符`//`的右边,因此Go语言编译器会忽略掉它。另外,它必须出现在源码文件的第一行语句(也就是代码包声明语句)的右边。只有符合上述这两个位置条件的导入注释才是有效的。再来看其中的引号部分。被双引号包裹的应该是一个符合导入路径语法规则的字符串。其中,`hypermind.cn`是我自己的一个域名。实际上,这也是用来替换掉我想隐去的代码托管网站域名及部分路径(这里是`github.com/hyper-carrot`)的那部分。在`hypermind.cn`右边的依次是我的项目的名称以及要下载的那个代码包的相对路径。这些与其标准导入路径中的内容都是一致的。为了清晰起见,我们再来做下对比。 -标记```-u```的意图和执行的动作都比较简单。我们在执行```go get```命令时加入```-u```标记就意味着,如果在本地工作区中已存在相关的代码包,那么就是用对应的代码版本控制系统的更新命令更新它,并进行编译和安装。这相当于强行更新指定的代码包及其依赖包。我们来看如下示例: +```go +github.com/hyper-carrot/talon/analyzer // 标准的导入路径 +hypermind.cn /talon/analyzer // 导入注释中的导入路径 +``` - hc@ubt:~$ go get -v github.com/hyper-carrot/go_lib +你想用你自己的域名替换掉标准导入路径中的哪部分由你自己说了算。不过一般情况下,被替换的部分包括代码托管网站的域名以及你在那里的用户ID就可以了。这足以达到我们最开始说的那两个目的。 +虽然我们在talon项目中的所有代码包中都加入了类似的导入注释,但是我们依然无法通过`go get hypermind.cn/talon/analyzer`命令来下载这个代码包。因为域名`hypermind.cn`所指向的网站并没有加入相应的处理逻辑。具体的实现步骤应该是这样的: -因为我们在之前已经检出并安装了代码包```go_lib```,所以我们执行上面这条命令后什么也没发生。还记得加入标记```-v```标记意味着会打印出被构建的代码包的名字吗?现在我们使用标记```-u```来强行更新代码包: +1. 编写一个可处理HTTP请求的程序。这里无所谓用什么编程语言去实现。当然,我推荐你用Go语言去做。 - hc@ubt:~$ go get -v -u github.com/hyper-carrot/go_lib - github.com/hyper-carrot/go_lib (download) - github.com/hyper-carrot/go_lib/logging - github.com/hyper-carrot/go_lib - -其中,带“(download)”后缀意味着命令从远程仓库检出或更新了代码包。从打印出的信息可以看到,```go get```命令先更新了参数指定的已存在于本地工作区的代码包,而后编译了它的唯一依赖包,最后编译了该代码包。我们还可以加上一个```-x```标记,以打印出用到的命令。读者可以自己试用一下它。 +2. 将这个处理程序与`hypermind.cn/talon`这个路径关联在一起,并总是在作为响应的HTML文档的头中写入下面这行内容: -**智能的下载** + ```html + + ``` + hypermind.cn/talon/analyzer熟悉HTML的读者都应该知道,这行内容会被视为HTML文档的元数据。它实际上`go get`命令的文档中要求的写法。它的模式是这样的: -命令```go get```还有一个很值得称道的功能。在使用它检出或更新代码包之后,它会寻找与本地已安装Go语言的版本号相对应的标签(tag)或分支(branch)。比如,本机安装Go语言的版本是1.x,那么```go get```命令会在该代码包的远程仓库中寻找名为“go1”的标签或者分支。如果找到指定的标签或者分支,则将本地代码包的版本切换到此标签或者分支。如果没有找到指定的标签或者分支,则将本地代码包的版本切换到主干的最新版本。 +```html + +``` -前面我们说在执行```go get```命令时也可以加入```-x```标记,这样可以看到```go get```命令执行过程中所使用的所有命令。不知道读者是否已经自己尝试了。下面我们还是以代码包```github.com/hyper-carrot/go_lib```为例,并且通过之前示例中的命令的执行此代码包已经被检出到本地。这时我们再次更新这个代码包: +实际上,`content`属性中的`import-prefix`的位置上应该填入我们自定义的远程代码包导入路径的前缀。这个前缀应该与我们的处理程序关联的那个路径相一致。而`vcs`显然应该代表与版本控制系统有关的标识。还记得表0-2中的主命令列吗?这里的填入内容就应该该列中的某一项。在这里,由于talon项目使用的是Git,所以这里应该填入`git`。至于`repo-root`,它应该是与该处理程序关联的路径对应的Github网站的URL。在这里,这个路径是`hypermind.cn/talon`,那么这个URL就应该是`https://github.com/hyper-carrot/talon`。后者也是talon项目的实际网址。 +好了,在我们做好上述处理程序之后,`go get hypermind.cn/talon/analyzer`命令的执行结果就会是正确的。`analyzer`代码包及其依赖包中的代码会被下载到GOPATH环境变量中的第一个工作区目录的src子目录中,然后被编译并安装。 - hc@ubt:~$ go get -v -u -x github.com/hyper-carrot/go_lib - github.com/hyper-carrot/go_lib (download) - cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib - git fetch - cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib - git show-ref - cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib - git checkout origin/master - WORK=/tmp/go-build034263530 - -在上述示例中,```go get```命令通过```git fetch```命令将所有远程分支更新到本地,而后有用```git show-ref```命令列出本地和远程仓库中记录的代码包的所有分支和标签。最后,当确定没有名为“go1”的标签或者分支后,```go get```命令使用```git checkout origin/master```命令将代码包的版本切换到主干的最新版本。下面,我们在本地增加一个名为“go1”的标签,看看```go get```命令的执行过程又会发生什么改变: - - hc@ubt:~$ cd ~/golang/lib/src/github.com/hyper-carrot/go_lib - hc@ubt:~/golang/lib/src/github.com/hyper-carrot/go_lib$ git tag go1 - hc@ubt:~$ go get -v -u -x github.com/hyper-carrot/go_lib - github.com/hyper-carrot/go_lib (download) - cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib - git fetch - cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib - git show-ref - cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib - git show-ref tags/go1 origin/go1 - cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib - git checkout tags/go1 - WORK=/tmp/go-build636338114 +注意,具体的代码包源码存放路径会是/home/hc/golang/lib/src/hypermind.cn/talon/analyzer。也就是说,存放路径(包括代码包源码文件以及相应的归档文件的存放路径)会遵循导入注释中的路径(这里是`hypermind.cn/talon/analyzer`),而不是原始的导入路径(这里是`github.com/hyper-carrot/talon/analyzer`)。另外,我们只需在talon项目的每个代码包中的某一个源码文件中加入导入注释,但这些导入注释中的路径都必须是一致的。在这之后,我们就只能使用`hypermind.cn/talon/`作为talon项目中的代码包的导入路径前缀了。一个反例如下: + +```go +hc@ubt:~$ go get github.com/hyper-carrot/talon/analyzer +package github.com/hyper-carrot/talon/analyzer: code in directory /home/hc/golang/lib/src/github.com/hyper-carrot/talon/analyzer expects import "hypermind.cn/talon/analyzer" +``` + +与自定义的代码包远程导入路径有关的内容我们就介绍到这里。从中我们也可以看出,Go语言为了让使用者的项目与代码托管网站隔离所作出的努力。只要你有自己的网站和一个不错的域名,这就很容易搞定并且非常值得。这会在你的代码包的使用者面前强化你的品牌,而不是某个代码托管网站的。当然,使你的代码包导入路径整齐划一是最直接的好处。 + +OK,言归正传,我下面继续关注`go get`这个命令本身。 + +**命令特有标记** + +`go get`命令可以接受所有可用于`go build`命令和`go install`命令的标记。这是因为`go get`命令的内部步骤中完全包含了编译和安装这两个动作。另外,`go get`命令还有一些特有的标记,如下表所示: + +_表0-4 ```go get```命令的特有标记说明_ + +标记名称 | 标记描述 +--------- | ------- +-d | 让命令程序只执行下载动作,而不执行安装动作。 +-f | 仅在使用`-u`标记时才有效。该标记会让命令程序忽略掉对已下载代码包的导入路径的检查。如果下载并安装的代码包所属的项目是你从别人那里Fork过来的,那么这样做就尤为重要了。 +-fix | 让命令程序在下载代码包后先执行修正动作,而后再进行编译和安装。 +-insecure | 允许命令程序使用非安全的scheme(如HTTP)去下载指定的代码包。如果你用的代码仓库(如公司内部的Gitlab)没有HTTPS支持,可以添加此标记。请在确定安全的情况下使用它。 +-t | 让命令程序同时下载并安装指定的代码包中的测试源码文件中依赖的代码包。 +-u | 让命令利用网络来更新已有代码包及其依赖包。默认情况下,该命令只会从网络上下载本地不存在的代码包,而不会更新已有的代码包。 + +为了更好的理解这几个特有标记,我们先清除Lib工作区的src目录和pkg目录中的所有子目录和文件。现在我们使用带有`-d`标记的`go get`命令来下载同样的代码包: + +```go +hc@ubt:~$ go get -d github.com/hyper-carrot/go_lib/logging +``` + +现在,让我们再来看一下Lib工作区的目录结构: + +```go +/home/hc/golang/lib: + bin/ + pkg/ + src/ + github.com/ + hyper-carrot/ + go_lib/ + logging/ + ... +``` + +我们可以看到,`go get`命令只将代码包下载到了Lib工作区的src目录,而没有进行后续的编译和安装动作。这个加入`-d`标记的结果。 + +再来看`-fix`标记。我们知道,绝大多数计算机编程语言在进行升级和演进过程中,不可能保证100%的向后兼容(Backward Compatibility)。在计算机世界中,向后兼容是指在一个程序或者代码库在更新到较新的版本后,用旧的版本程序创建的软件和系统仍能被正常操作或使用,或在旧版本的代码库的基础上编写的程序仍能正常编译运行的能力。Go语言的开发者们已想到了这点,并提供了官方的代码升级工具——`fix`。`fix`工具可以修复因Go语言规范变更而造成的语法级别的错误。关于`fix`工具,我们将放在本节的稍后位置予以说明。 + +假设我们本机安装的Go语言版本是1.5,但我们的程序需要用到一个很早之前用Go语言的0.9版本开发的代码包。那么我们在使用`go get`命令的时候可以加入`-fix`标记。这个标记的作用是在检出代码包之后,先对该代码包中不符合Go语言1.5版本的语言规范的语法进行修正,然后再下载它的依赖包,最后再对它们进行编译和安装。 + +标记`-u`的意图和执行的动作都比较简单。我们在执行`go get`命令时加入`-u`标记就意味着,如果在本地工作区中已存在相关的代码包,那么就是用对应的代码版本控制系统的更新命令更新它,并进行编译和安装。这相当于强行更新指定的代码包及其依赖包。我们来看如下示例: + +```go +hc@ubt:~$ go get -v github.com/hyper-carrot/go_lib/logging +``` + +因为我们在之前已经检出并安装了这个代码包,所以我们执行上面这条命令后什么也没发生。还记得加入标记`-v`标记意味着会打印出被构建的代码包的名字吗?现在我们使用标记`-u`来强行更新代码包: + +```go +hc@ubt:~$ go get -v -u github.com/hyper-carrot/go_lib/logging +github.com/hyper-carrot/go_lib (download) +``` -将这两个示例进行对比,我们会很容易发现它们之间的区别。第二个示例的命令执行过程中使用```git show-ref```查看所有分支和标签,当发现有匹配的信息又通过```git show-ref tags/go1 origin/go1```命令进行精确查找,在确认无误后将本地代码包的版本切换到标签“go1”之上。 +其中,“(download)”后缀意味着命令从远程仓库检出或更新了该行显示的代码包。如果我们要查看附带`-u`的`go get`命令到底做了些什么,还可以加上一个`-x`标记,以打印出用到的命令。读者可以自己试用一下它。 -命令```go get```的这一功能是非常有用的。我们的代码在直接或间接依赖某些同时针对多个Go语言版本开发的代码包时,可以自动的检出其正确的版本。也可以说,```go get```命令内置了一定的代码包多版本依赖管理的功能。 +**智能的下载** +命令`go get`还有一个很值得称道的功能。在使用它检出或更新代码包之后,它会寻找与本地已安装Go语言的版本号相对应的标签(tag)或分支(branch)。比如,本机安装Go语言的版本是1.x,那么`go get`命令会在该代码包的远程仓库中寻找名为“go1”的标签或者分支。如果找到指定的标签或者分支,则将本地代码包的版本切换到此标签或者分支。如果没有找到指定的标签或者分支,则将本地代码包的版本切换到主干的最新版本。 + +前面我们说在执行`go get`命令时也可以加入`-x`标记,这样可以看到`go get`命令执行过程中所使用的所有命令。不知道读者是否已经自己尝试了。下面我们还是以代码包`github.com/hyper-carrot/go_lib`为例,并且通过之前示例中的命令的执行此代码包已经被检出到本地。这时我们再次更新这个代码包: + +```go +hc@ubt:~$ go get -v -u -x github.com/hyper-carrot/go_lib +github.com/hyper-carrot/go_lib (download) +cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib +git fetch +cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib +git show-ref +cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib +git checkout origin/master +WORK=/tmp/go-build034263530 +``` + +在上述示例中,`go get`命令通过`git fetch`命令将所有远程分支更新到本地,而后有用`git show-ref`命令列出本地和远程仓库中记录的代码包的所有分支和标签。最后,当确定没有名为“go1”的标签或者分支后,`go get`命令使用`git checkout origin/master`命令将代码包的版本切换到主干的最新版本。下面,我们在本地增加一个名为“go1”的标签,看看`go get`命令的执行过程又会发生什么改变: + +```go +hc@ubt:~$ cd ~/golang/lib/src/github.com/hyper-carrot/go_lib +hc@ubt:~/golang/lib/src/github.com/hyper-carrot/go_lib$ git tag go1 +hc@ubt:~$ go get -v -u -x github.com/hyper-carrot/go_lib +github.com/hyper-carrot/go_lib (download) +cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib +git fetch +cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib +git show-ref +cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib +git show-ref tags/go1 origin/go1 +cd /home/hc/golang/lib/src/github.com/hyper-carrot/go_lib +git checkout tags/go1 +WORK=/tmp/go-build636338114 +``` + +将这两个示例进行对比,我们会很容易发现它们之间的区别。第二个示例的命令执行过程中使用`git show-ref`查看所有分支和标签,当发现有匹配的信息又通过`git show-ref tags/go1 origin/go1`命令进行精确查找,在确认无误后将本地代码包的版本切换到标签“go1”之上。 +命令`go get`的这一功能是非常有用的。我们的代码在直接或间接依赖某些同时针对多个Go语言版本开发的代码包时,可以自动的检出其正确的版本。也可以说,`go get`命令内置了一定的代码包多版本依赖管理的功能。 -## links - * [目录](catalog.md) - * 上一节: [go install](0.2.md) - * 下一节: [go clean](0.4.md) +到这里,我向大家介绍了`go get`命令的使用方式。`go get`命令与之前介绍的两个命令一样,是我们编写Go语言程序、构建Go语言项目时必不可少的辅助工具。 diff --git a/0.4.md b/0.4.md index bd2b19d..8c6796c 100644 --- a/0.4.md +++ b/0.4.md @@ -1,37 +1,33 @@ -### 0.4 go clean +# go clean -执行```go clean```命令会删除掉执行其它命令时产生的一些文件和目录,包括: +执行`go clean`命令会删除掉执行其它命令时产生的一些文件和目录,包括: -1. 在使用```go build```命令时在当前代码包下生成的与包名同名或者与Go源码文件同名的可执行文件。在Windows下,则是与包名同名或者Go源码文件同名且带有“.exe”后缀的文件。 +1. 在使用`go build`命令时在当前代码包下生成的与包名同名或者与Go源码文件同名的可执行文件。在Windows下,则是与包名同名或者Go源码文件同名且带有“.exe”后缀的文件。 -2. 在执行```go test```命令并加入```-c```标记时在当前代码包下生成的以包名加“.test”后缀为名的文件。在Windows下,则是以包名加“.test.exe”后缀为名的文件。我们会在后面专门介绍```go test```命令。 +2. 在执行`go test`命令并加入`-c`标记时在当前代码包下生成的以包名加“.test”后缀为名的文件。在Windows下,则是以包名加“.test.exe”后缀为名的文件。我们会在后面专门介绍`go test`命令。 -3. 如果执行```go clean```命令时带有标记```-i```,则会同时删除安装(执行```go install```命令)当前代码包时所产生的结果文件。如果当前代码包中只包含库源码文件,则结果文件指的就是在工作区的pkg目录的相应目录下的归档文件。如果当前代码包中只包含一个命令源码文件,则结果文件指的就是在工作区的bin目录下的可执行文件。 +3. 如果执行`go clean`命令时带有标记`-i`,则会同时删除安装当前代码包时所产生的结果文件。如果当前代码包中只包含库源码文件,则结果文件指的就是在工作区的pkg目录的相应目录下的归档文件。如果当前代码包中只包含一个命令源码文件,则结果文件指的就是在工作区的bin目录下的可执行文件。 -4. 还有一些目录和文件是在编译Go或C源码文件时留在相应目录中的。包括:“_obj”和“_test”目录,名称为“_testmain.go”、“test.out”、“build.out”或“a.out”的文件,名称以“.5”、“.6”、“.8”、“.a”、“.o”或“.so”为后缀的文件。这些目录和文件是在执行```go build```命令时生成在临时目录中的。如果你忘记了这个临时目录是怎么回事儿,可以再回顾一下前面关于```go build```命令的介绍。临时目录的名称以```go-build```为前缀。 +4. 还有一些目录和文件是在编译Go或C源码文件时留在相应目录中的。包括:“_obj”和“_test”目录,名称为“_testmain.go”、“test.out”、“build.out”或“a.out”的文件,名称以“.5”、“.6”、“.8”、“.a”、“.o”或“.so”为后缀的文件。这些目录和文件是在执行`go build`命令时生成在临时目录中的。如果你忘记了这个临时目录是怎么回事儿,可以再回顾一下前面关于`go build`命令的介绍。临时目录的名称以`go-build`为前缀。 -5. 如果执行```go clean```命令时带有标记```-r```,则还包括当前代码包的所有依赖包的上述目录和文件。 +5. 如果执行`go clean`命令时带有标记`-r`,则还包括当前代码包的所有依赖包的上述目录和文件。 -我们再以goc2p项目的```logging```为例。为了能够反复体现每个标记的作用,我们会使用标记```-n```。使用标记```-n```会让命令在执行过程中打印用到的系统命令,但不会真正执行它们。如果想既打印命令又执行命令则需使用标记```-x```。现在我们来试用一下```go clean```命令: +我们再以goc2p项目的`logging`为例。为了能够反复体现每个标记的作用,我们会使用标记`n`。使用标记`-n`会让命令在执行过程中打印用到的系统命令,但不会真正执行它们。如果想既打印命令又执行命令则需使用标记`-x`。现在我们来试用一下`go clean`命令: - hc@ubt:~/golang/goc2p/src$ go clean -x logging - cd /home/hc/golang/goc2p/src/logging - rm -f logging logging.exe logging.test logging.test.exe +```bash +hc@ubt:~/golang/goc2p/src$ go clean -x logging +cd /home/hc/golang/goc2p/src/logging +rm -f logging logging.exe logging.test logging.test.exe +``` -现在,我们加上标记```-i```: - - hc@ubt:~/golang/goc2p/src$ go clean -x -i logging - cd /home/hc/golang/goc2p/src/logging - rm -f logging logging.exe logging.test logging.test.exe - rm -f /home/hc/golang/goc2p/pkg/linux_386/logging.a +现在,我们加上标记`-i`: + +```bash +hc@ubt:~/golang/goc2p/src$ go clean -x -i logging +cd /home/hc/golang/goc2p/src/logging +rm -f logging logging.exe logging.test logging.test.exe +rm -f /home/hc/golang/goc2p/pkg/linux_386/logging.a +``` -如果再加上标记```-r```又会打印出哪些命令呢?请读者自己试一试吧。 - - - -## links - * [目录](catalog.md) - * 上一节: [go get](0.3.md) - * 下一节: [godoc](0.5.md) - \ No newline at end of file +如果再加上标记`-r`又会打印出哪些命令呢?请读者自己试一试吧。 \ No newline at end of file diff --git a/0.5.md b/0.5.md index 0678b1f..51e202e 100644 --- a/0.5.md +++ b/0.5.md @@ -1,88 +1,195 @@ -### 0.5 godoc +# go doc与godoc -命令```godoc```是一个很强大的工具,用于展示指定代码包的文档。我们可以通过运行```go get code.google.com/p/go.tools/cmd/godoc```安装它。 +## go doc -该命令有两种模式可供选择。如果在执行命令时不加入```-http```标记,则该命令就以命令行模式运行。在打印纯文本格式的文档到标准输出后,命令执行就结束了。比如,我们用命令行模式查看代码包fmt的文档: +`go doc`命令可以打印附于Go语言程序实体上的文档。我们可以通过把程序实体的标识符作为该命令的参数来达到查看其文档的目的。 - hc@ubt:~$ godoc fmt +**插播:**所谓Go语言的程序实体,是指变量、常量、函数、结构体以及接口。而程序实体的标识符即是代表它们的名称。标识符又分非限定标识符和限定标识符。其中,限定标识符一般用于表示某个代码包中的程序实体或者某个结构体类型中的方法或字段。例如,标准库代码包`io`中的名为`EOF`的变量用限定标识符表示即`io.EOF`。又例如,如果我有一个`sync.WaitGroup`类型的变量`wg`并且想调用它的`Add`方法,那么可以这样写`wg.Add()`。其中,`wg.Add`就是一个限定标识符,而后面的`()`则代表了调用操作。 + +下面说明怎样使用`go doc`命令。先来看一下`go doc`命令可接受的标记。 + +_表0-5 ```go doc```命令的标记说明_ + +标记名称 | 标记描述 +---------- | ------- +-c | 加入此标记后会使`go doc`命令区分参数中字母的大小写。默认情况下,命令是大小写不敏感的。 +-cmd | 加入此标记后会使`go doc`命令同时打印出`main`包中的可导出的程序实体(其名称的首字母大写)的文档。默认情况下,这部分文档是不会被打印出来的。 +-u | 加入此标记后会使`go doc`命令同时打印出不可导出的程序实体(其名称的首字母小写)的文档。默认情况下,这部分文档是不会被打印出来的。 + +这几个标记的意图都非常简单和明确,大家可以根据实际情况选用。 + +`go doc`命令可以后跟一个或两个参数。当然,我们也可以不附加任务参数。如果不附加参数,那么`go doc`命令会试图打印出当前目录所代表的代码包的文档及其中的包级程序实体的列表。 + +例如,我要在goc2p项目的`loadgen`代码包所在目录中运行`go doc`命令的话,那么就会是这样: + +```bash +hc@ubt:~/golang/goc2p/src/loadgen$ go doc +package loadgen // import "loadgen" + +func NewGenerator( + caller lib.Caller, + timeoutNs time.Duration, + lps uint32, + durationNs time.Duration, + resultCh chan *lib.CallResult) (lib.Generator, error) +``` + +如果你需要指定代码包或程序实体,那么就需要在`go doc`命令后附上参数了。例如,只要我本地的goc2p项目的所在目录存在于GOPATH环境变量中,我就可以在任意目录中敲入`go doc loadgen`。如此得到的输出一定是与上面那个示例一致的。 + +看过`loadgen`代码包中源码的读者会知道,其中只有一个可导出的程序实体,即`NewGenerator`函数。这也是上述示例中如此输出的原因。该代码包中的结构体类型`myGenerator`是不可导出,但是我们只需附加`-u`标记便可查看它的文档了: + +```bash +hc@ubt:~$ go doc -u loadgen.myGenerator +type myGenerator struct { + caller lib.Caller // 调用器。 + timeoutNs time.Duration // 处理超时时间,单位:纳秒。 + lps uint32 // 每秒载荷量。 + durationNs time.Duration // 负载持续时间,单位:纳秒。 + concurrency uint32 // 并发量。 + tickets lib.GoTickets // Goroutine票池。 + stopSign chan byte // 停止信号的传递通道。 + cancelSign byte // 取消发送后续结果的信号。 + endSign chan uint64 // 完结信号的传递通道,同时被用于传递调用执行计数。 + callCount uint64 // 调用执行计数。 + status lib.GenStatus // 状态。 + resultCh chan *lib.CallResult // 调用结果通道。 +} + + 载荷发生器的实现。 + +func (gen *myGenerator) Start() +func (gen *myGenerator) Status() lib.GenStatus +func (gen *myGenerator) Stop() (uint64, bool) +func (gen *myGenerator) asyncCall() +func (gen *myGenerator) genLoad(throttle <-chan time.Time) +func (gen *myGenerator) handleStopSign(callCount uint64) +func (gen *myGenerator) init() error +func (gen *myGenerator) interact(rawReq *lib.RawReq) *lib.RawResp +func (gen *myGenerator) sendResult(result *lib.CallResult) bool +``` + +如此一来,`loadgen.myGenerator`类型的文档、字段和方法都尽收眼底。注意,这里我们使用到了限定标识符。下面再进一步,如果你只想查看`loadgen.myGenerator`类型的`init`方法的文档,那么只要续写这个限定标识符就可以了,像这样: + +```bash +hc@ubt:~$ go doc -u loadgen.myGenerator.init +func (gen *myGenerator) init() error + + 初始化载荷发生器。 +``` + +注意,结构体类型中的字段的文档是无法被单独打印的。另外,`go doc`命令根据参数查找代码包或程序实体的顺序是:先Go语言根目录(即GOROOT所环境变量指定的那个目录)后工作区目录(即GOPATH环境变量包含的那些目录)。并且,在前者或后者中,`go doc`命令的查找顺序遵循字典序。因此,如果某个工作区目录中的代码包与标准库中的包重名了,那么它是无法被打印出来的。`go doc`命令只会打印出第一个匹配的代码包或程序实体的文档。 + +我们在前面说过,`go doc`命令还可以接受两个参数。这是一种更加精细的指定代码包或程序实体的方式。一个显著的区别是,如果你想打印标准库代码包`net/http`中的结构体类型`Request`的文档,那么可以这样敲入`go doc`命令: + +```bash +go doc http.Request +``` +注意,这里并没有写入`net/http`代码包的导入路径,而只是写入了其中的最后一个元素`http`。但是如果你把`http.Request`拆成两个参数(即` http Request`)的话,命令程序就会什么也查不到了。因为这与前一种用法的解析方式是不一样的。正确的做法是,当你指定两个参数时,作为第一个参数的代码包名称必须是完整的导入路径,即:在敲入命令`go doc net/http Request`后,你会得到想要的结果。 + +最后,在给定两个参数时,`go doc`会打印出所有匹配的文档,而不是像给定一个参数时那样只打印出第一个匹配的文档。这对于查找只有大小写不同的多个方法(如`New`和`new`)的文档来说非常有用。 + +## godoc +命令`godoc`是一个很强大的工具,同样用于展示指定代码包的文档。在Go语言的1.5版本中,它是一个内置的标准命令。 + +该命令有两种模式可供选择。如果在执行命令时不加入`-http`标记,则该命令就以命令行模式运行。在打印纯文本格式的文档到标准输出后,命令执行就结束了。比如,我们用命令行模式查看代码包fmt的文档: + +```bash +hc@ubt:~$ godoc fmt +``` -由于篇幅原因,我们在本小节中略去了文档查询结果。读者可以自己运行一下上述命令。在该命令被执行之后,我们就可以看到编排整齐有序的文档内容了。这包括代码包```fmt```的综述和所有可导出成员的声明、文档以及例子。 +为了节省篇幅,我们在这里略去了文档查询结果。读者可以自己运行一下上述命令。在该命令被执行之后,我们就可以看到编排整齐有序的文档内容了。这包括代码包`fmt`及其中所有可导出的包级程序实体的声明、文档和例子。 -有时候我们只是想查看某一个函数或者结构体类型的文档,那么我们可以将这个函数或者结构体的名称加入命令的最后面,像这样: +有时候我们只是想查看某一个函数或者结构体类型的文档,那么我们可以将这个函数或者结构体的名称加入命令的后面,像这样: - hc@ubt:~$ godoc fmt Printf +```bash +hc@ubt:~$ godoc fmt Printf +``` 或者: - hc@ubt:~$ godoc os File +```bash +hc@ubt:~$ godoc os File +``` -如果我们想同时查看一个代码包中的几个函数的文档,则仅需将函数或者结构体名称追加到命令后面。比如我们要查看代码包```fmt```中函数```Printf```和函数```Println```的文档: +如果我们想同时查看一个代码包中的几个函数的文档,则仅需将函数或者结构体名称追加到命令后面。比如我们要查看代码包`fmt`中函数`Printf`和函数`Println`的文档: - hc@ubt:~$ godoc fmt Printf Println +```bash +hc@ubt:~$ godoc fmt Printf Println +``` -如果我们不但想在文档中查看可导出成员的声明,还想看到它们的源码,那么我们可以在执行```godoc```命令的时候加入标记```-src```,比如这样: +如果我们不但想在文档中查看可导出的程序实体的声明,还想看到它们的源码,那么我们可以在执行`godoc`命令的时候加入标记`-src`,比如这样: - hc@ubt:~$ godoc -src fmt Printf +```bash +hc@ubt:~$ godoc -src fmt Printf +``` -Go语言为程序使用示例代码设立了专有的规则。我们在这里暂不讨论这个规则的细节。只需要知道正因为有了这个专有规则,使得```godoc```命令可以根据这些规则提取相应的示例代码并把它们加入到对应的文档中。如果我们想在查看代码包```net```中的结构体```Listener```的文档的同时查看关于它的示例代码,那么我们只需要在执行命令时加入标记```-ex```。使用方法如下: +Go语言为程序使用示例代码设立了专有的规则。我们在这里暂不讨论这个规则的细节。只需要知道正因为有了这个专有规则,使得`godoc`命令可以根据这些规则提取相应的示例代码并把它们加入到对应的文档中。如果我们想在查看代码包`net`中的结构体类型`Listener`的文档的同时查看关于它的示例代码,那么我们只需要在执行命令时加入标记`-ex`。使用方法如下: - hc@ubt:~$ godoc -ex net Listener +```bash +hc@ubt:~$ godoc -ex net/http FileServer +``` + +注意,我们在使用`godoc`命令时,只能把代码包和程序实体的标识符拆成两个参数。也就是说,`godoc`命令不支持前文所述的`go doc`命令的单参数用法。 -在实际的Go语言环境中,我们可能会遇到一个命令源码文件所产生的可执行文件与代码包重名的情况。比如本节介绍的命令```go```和官方代码包```go```。现在我们要明确的告诉```godoc```命令要查看可执行文件go的文档,我们需要在名称前加入“cmd/”前缀: +在实际的Go语言环境中,我们可能会遇到一个命令源码文件所产生的可执行文件与代码包重名的情况。比如,这里介绍的标准命令`go`和官方代码包`go`。现在我们要明确的告诉`godoc`命令要查看可执行文件go的文档,我们需要在名称前加入`cmd/`前缀: - hc@ubt:~$ godoc cmd/go +```bash +hc@ubt:~$ godoc cmd/go +``` -另外,如果我们想查看HTML格式的文档,就需要加入标记```-html```。当然,这样在命令行模式下的查看效果是很差的。但是,如果仔细查看的话,可以在其中找到一些相应源码的链接地址。 +另外,如果我们想查看HTML格式的文档,就需要加入标记`-html`。当然,这样在命令行模式下的查看效果是很差的。但是,如果仔细查看的话,可以在其中找到一些相应源码的链接地址。 -一般情况下,```godoc```命令会去Go语言根目录和环境变量GOPATH的值(一个或多个工作区)指向的工作区目录中查找代码包。不过,我们还可以通过加入标记```-goroot```来制定一个Go语言根目录。这个被指定的Go语言根目录仅被用于当次命令的执行。示例如下: +一般情况下,`godoc`命令会去Go语言根目录和环境变量GOPATH包含的工作区目录中查找代码包。我们可以通过加入标记`-goroot`来制定一个Go语言根目录。这个被指定的Go语言根目录仅被用于当次命令的执行。示例如下: - hc@ubt:~$ godoc -goroot="/usr/local/go" fmt +```bash +hc@ubt:~$ godoc -goroot="/usr/local/go" fmt +``` -现在让我们来看看另外一种模式。如果我们在执行命令时加上```-http```标记则会启用另一模式。这种模式被叫做Web服务器模式,它以Web页面的形式提供Go语言文档。 +现在让我们来看看另外一种模式。如果我们在执行命令时加上`-http`标记则会启用另一模式。这种模式被叫做Web服务器模式,它以Web页面的形式提供Go语言文档。 我们使用如下命令启动这个文档Web服务器: - hc@ubt:~/golang/goc2p$ godoc -http=:6060 +```bash +hc@ubt:~/golang/goc2p$ godoc -http=:6060 +``` -标记```-http```的值```:6060```表示启动的Web服务器使用本机的6060端口。之后,我们就可以通过在网络浏览器的地址栏中输入[http://localhost:6060](http://localhost:6060)来查看以网页方式展现的Go文档了。 +标记`-http`的值`:6060`表示启动的Web服务器使用本机的6060端口。之后,我们就可以通过在网络浏览器的地址栏中输入[http://localhost:6060](http://localhost:6060)来查看以网页方式展现的Go文档了。 -![本机的Go文档Web服务首页](images/0-1.jpg) +![本机的Go文档Web服务首页](images/0-1.png) _图0-1 本机的Go文档Web服务首页_ -这与[Go语言官方站点](http://golang.org)的Web服务页面如出一辙。这使得我们在不方便访问Go语言官方站点的情况下也可以查看Go语言文档。并且,更便利的是,通过本机的Go文档Web服务,我们还可以查看所有本机工作区下的代码的文档。比如,goc2p项目中的代码包```pkgtool```的页面如下图: +这与[Go语言官方站点](http://golang.org)的Web服务页面如出一辙。这使得我们在不方便访问Go语言官方站点的情况下也可以查看Go语言文档。并且,更便利的是,通过本机的Go文档Web服务,我们还可以查看所有本机工作区下的代码的文档。比如,goc2p项目中的代码包`pkgtool`的页面如下图: -![goc2p项目中的pkgtool包的Go文档页面](images/0-2.jpg) +![goc2p项目中的pkgtool包的Go文档页面](images/0-2.png) _图0-2 goc2p项目中的pkgtool包的Go文档页面_ 现在,我们在本机开启Go文档Web服务器,端口为9090。命令如下: - - hc@ubt:~$ godoc -http=:9090 -index +```bash +hc@ubt:~$ godoc -http=:9090 -index +``` -注意,要使用```-index```标记开启搜索索引,这个索引会在服务器启动时创建并维护。否则无论在Web页面还是命令行终端中提交查询都会返回错误“Search index disabled: no results available”。 +注意,要使用`-index`标记开启搜索索引。这个索引会在服务器启动时创建并维护。如果不加入此标记,那么无论在Web页面还是命令行终端中都是无法进行查询操作的。 -索引中提供了标示符和全文本搜索信息(通过正则表达式为可搜索性提供支持)。全文本搜索结果显示条目的最大数量可以通过标记```-maxresults```提供。标记```-maxresults```默认值是10000。如果不想提供如此多的结果条目,可以设置小一些的值。甚至,如果不想提供全文本搜索结果,可以将标记```-maxresults```的值设置为0,这样服务器就只会创建标识符索引,而根本不会创建全文本搜索索引了。标识符索引即为对程序实体(变量、常量、函数、结构体和接口)名称的索引。 +索引中提供了标识符和全文本搜索信息(通过正则表达式为可搜索性提供支持)。全文本搜索结果显示条目的最大数量可以通过标记`-maxresults`提供。标记`-maxresults`默认值是10000。如果不想提供如此多的结果条目,可以设置小一些的值。甚至,如果不想提供全文本搜索结果,可以将标记`-maxresults`的值设置为0,这样服务器就只会创建标识符索引,而根本不会创建全文本搜索索引了。标识符索引即为对程序实体名称的索引。 -正因为在使用了```-index```标记的情况下文档服务器会在启动时创建索引,所以在文档服务器启动之后还不能立即提供搜索服务,需要稍等片刻。在索引为被创建完毕之前,我们的搜索操作都会得到提示信息“Indexing in progress: result may be inaccurate”。 +正因为在使用了`-index`标记的情况下文档服务器会在启动时创建索引,所以在文档服务器启动之后还不能立即提供搜索服务,需要稍等片刻。在索引为被创建完毕之前,我们的搜索操作都会得到提示信息“Indexing in progress: result may be inaccurate”。 -如果我们在本机用```godoc```命令启动了Go文档Web服务器,且IP地址为192.168.1.4、端口为9090,那么我们就可以在另一个命令行终端甚至另一台能够与本机联通的计算机中通过如下命令进行查询了。查询命令如下: +如果我们在本机用`godoc`命令启动了Go文档Web服务器,且IP地址为192.168.1.4、端口为9090,那么我们就可以在另一个命令行终端甚至另一台能够与本机联通的计算机中通过如下命令进行查询了。查询命令如下: +```bash hc@ubt:~$ godoc -q -server="192.168.1.4:9090" Listener - +``` + 命令的最后为要查询的内容,可以是任何你想搜索的字符串,而不仅限于代码包、函数或者结构体的名称。 -标记```-q```开启了远程查询的功能。而标记```-server="192.168.1.4:9090"```则指明了远程文档服务器的IP地址和端口号。实际上,如果不指明远程查询服务器的地址,那么该命令会自行将地址“:6060”和“golang.org”作为远程查询服务器的地址。这两个地址即是默认的本机文档Web站点地址和官方的文档Web站点地址。所以执行如下命令我们也可以查询到标准库的信息: +标记`-q`开启了远程查询的功能。而标记`-server="192.168.1.4:9090"`则指明了远程文档服务器的IP地址和端口号。实际上,如果不指明远程查询服务器的地址,那么该命令会自行将地址“:6060”和“golang.org”作为远程查询服务器的地址。这两个地址即是默认的本机文档Web站点地址和官方的文档Web站点地址。所以执行如下命令我们也可以查询到标准库的信息: - hc@ubt:~$ godoc -q=true fmt +```bash +hc@ubt:~$ godoc -q fmt +``` -命令```godoc```还有很多可用的标记,但在通常情况下并不常用。读者如果有兴趣,可以在命令行环境下执行```godoc```进行查看。 +命令`godoc`还有很多可用的标记,但在通常情况下并不常用。读者如果有兴趣,可以在命令行环境下敲入`godoc`并查看其文档。 至于怎样才能写出优秀的代码包文档,我在《Go并发编程实战》的5.2节中做了详细说明。 - - - -## links - * [目录](catalog.md) - * 上一节: [go clean](0.4.md) - * 下一节: [go run](0.6.md) diff --git a/0.6.md b/0.6.md index a42f714..d1cdddd 100644 --- a/0.6.md +++ b/0.6.md @@ -1,97 +1,94 @@ -### 0.6 go run +# go run - -在本书第二章中,我们介绍过Go源码文件的分类。Go源码文件包括:命令源码文件、库源码文件和测试源码文件。其中,命令源码文件总应该属于```main```代码包,且在其中有无参数声明、无结果声明的main函数。单个命令源码文件可以被单独编译,也可以被单独安装(需要设置环境变量GOBIN)。当然,命令源码文件也可以被单独运行。我们想要运行命令源码文件就需要使用命令```go run```。 -```go run```命令可以编译并运行命令源码文件。由于它其中包含了编译动作,因此它也可以接受所有可用于```go build```命令的标记。除了标记之外,```go run```命令只接受Go源码文件作为参数,而不接受代码包。与```go build```命令和```go install```命令一样,```go run```命令也不允许多个命令源码文件作为参数,即使它们在同一个代码包中也是如此。而原因也是一致的,多个命令源码文件都有相同的main函数声明。 +在《Go并发编程实战》的第二章中,我介绍了Go源码文件的分类。Go源码文件包括:命令源码文件、库源码文件和测试源码文件。其中,命令源码文件总应该属于`main`代码包,且在其中有无参数声明、无结果声明的main函数。单个命令源码文件可以被单独编译,也可以被单独安装(可能需要设置环境变量GOBIN)。当然,命令源码文件也可以被单独运行。我们想要运行命令源码文件就需要使用命令`go run`。 -如果命令源码文件可以接受参数,那么在使用```go run```命令运行它的时候就可以把它的参数放在它的文件名后面,像这样: +`go run`命令可以编译并运行命令源码文件。由于它其中包含了编译动作,因此它也可以接受所有可用于`go build`命令的标记。除了标记之外,`go run`命令只接受Go源码文件作为参数,而不接受代码包。与`go build`命令和`go install`命令一样,`go run`命令也不允许多个命令源码文件作为参数,即使它们在同一个代码包中也是如此。而原因也是一致的,多个命令源码文件会都有main函数声明。 - hc@ubt:~/golang/goc2p/src/helper/ds$ go run showds.go -p ~/golang/goc2p +如果命令源码文件可以接受参数,那么在使用`go run`命令运行它的时候就可以把它的参数放在它的文件名后面,像这样: + +```bash +hc@ubt:~/golang/goc2p/src/helper/ds$ go run showds.go -p ~/golang/goc2p +``` -在上面的示例中,我们使用```go run```命令运行命令源码文件showds.go。这个命令源码文件可以接受一个名称为“p”的参数。我们用“-p”这种形式表示“p”是一个参数名而不是参数值。它与源码文件名之间需要用空格隔开。参数值会放在参数名的后面,两者成对出现。它们之间也要用空格隔开。如果有第二个参数,那么第二个参数的参数名与第一个参数的参数值之间也要有一个空格。以此类推。 +在上面的示例中,我们使用`go run`命令运行命令源码文件showds.go。这个命令源码文件可以接受一个名称为“p”的参数。我们用“-p”这种形式表示“p”是一个参数名而不是参数值。它与源码文件名之间需要用空格隔开。参数值会放在参数名的后面,两者成对出现。它们之间也要用空格隔开。如果有第二个参数,那么第二个参数的参数名与第一个参数的参数值之间也要有一个空格。以此类推。 -```go run```命令只能接受一个命令源码文件以及若干个库源码文件(需同属于```main包```)作为文件参数,且不能接受测试源码文件。它在执行时会检查源码文件的类型。如果参数中有多个或者没有命令源码文件,那么```go run```命令就只会打印错误提示信息并退出,而不会继续执行。 +`go run`命令只能接受一个命令源码文件以及若干个库源码文件(必须同属于`main`包)作为文件参数,且不能接受测试源码文件。它在执行时会检查源码文件的类型。如果参数中有多个或者没有命令源码文件,那么`go run`命令就只会打印错误提示信息并退出,而不会继续执行。 -在通过参数检查后,```go run```命令会将编译参数中的命令源码文件,并把编译后的可执行文件存放到临时工作目录中。 +在通过参数检查后,`go run`命令会将编译参数中的命令源码文件,并把编译后的可执行文件存放到临时工作目录中。 **编译和运行过程** -为了更直观的体现出```go run```命令中的操作步骤,我们在执行命令时加入标记```-n```,用于打印相关命令而不实际执行。现在让我们来模拟运行goc2p项目中的代码包helper/ds的命令源码文件showds.go。示例如下: +为了更直观的体现出`go run`命令中的操作步骤,我们在执行命令时加入标记`-n`,用于打印相关命令而不实际执行。现在让我们来模拟运行goc2p项目中的代码包helper/ds的命令源码文件showds.go。示例如下: - hc@ubt:~/golang/goc2p/src/helper/ds$ go run -n showds.go - - # - # command-line-arguments - # - - mkdir -p $WORK/command-line-arguments/_obj/ - mkdir -p $WORK/command-line-arguments/_obj/exe/ - cd /home/hc/golang/goc2p/src/helper/ds - /usr/local/go/pkg/tool/linux_386/8g -o $WORK/command-line-arguments/_obj - /_go_.8 -p command-line-arguments -complete -D _/home/freej/mybook/goc2p - /src/helper/ds -I $WORK ./showds.go - /usr/local/go/pkg/tool/linux_386/pack grcP $WORK $WORK - /command-line-arguments.a $WORK/command-line-arguments/_obj/_go_.8 - cd . - /usr/local/go/pkg/tool/linux_386/8l -o $WORK/command-line-arguments - /_obj/exe/showds -L $WORK $WORK/command-line-arguments.a - $WORK/command-line-arguments/_obj/exe/showds +```bash +hc@ubt:~/golang/goc2p/src/helper/ds$ go run -n showds.go + +# +# command-line-arguments +# + +mkdir -p $WORK/command-line-arguments/_obj/ +mkdir -p $WORK/command-line-arguments/_obj/exe/ +cd /home/hc/golang/goc2p/src/helper/ds +/usr/local/go1.5/pkg/tool/linux_amd64/compile -o $WORK/command-line-arguments.a -trimpath $WORK -p main -complete -buildid df49387da030ad0d3bebef3f046d4013f8cb08d3 -D _/home/hc/golang/goc2p/src/helper/ds -I $WORK -pack ./showds.go +cd . +/usr/local/go1.5/pkg/tool/linux_amd64/link -o $WORK/command-line-arguments/_obj/exe/showds -L $WORK -w -extld=clang -buildmode=exe -buildid=df49387da030ad0d3bebef3f046d4013f8cb08d3 $WORK/command-line-arguments.a +$WORK/command-line-arguments/_obj/exe/showds +``` 在上面的示例中并没有显示针对命令源码文件showds.go的依赖包进行编译和运行的相关打印信息。这是因为该源码文件的所有依赖包已经在之前被编译过了。 现在,我们来逐行解释这些被打印出来的信息。 -以前缀“#”开始的是注释信息。我们看到信息中有三行注释信息,并在中间行出现了内容“command-line-arguments”。我们在讲```go build```命令的时候说过,编译命令在分析参数的时候如果发现第一个参数是Go源码文件而不是代码包时,会在内部生成一个名为“command-line-arguments”的虚拟代码包。所以这里的注释信息就是要告诉我们下面的几行信息是关于虚拟代码包“command-line-arguments”的。 +以前缀“#”开始的是注释信息。我们看到信息中有三行注释信息,并在中间行出现了内容“command-line-arguments”。我们在讲`go build`命令的时候说过,编译命令在分析参数的时候如果发现第一个参数是Go源码文件而不是代码包时,会在内部生成一个名为“command-line-arguments”的虚拟代码包。所以这里的注释信息就是要告诉我们下面的几行信息是关于虚拟代码包“command-line-arguments”的。 打印信息中的“$WORK”表示临时工作目录的绝对路径。为了存放对虚拟代码包“command-line-arguments”的编译结果,命令在临时工作目录中创建了名为command-line-arguments的子目录,并在其下又创建了_obj子目录和_obj/exe子目录。 -然后,命令程序使用Go语言工具目录```8g```命令对命令源码文件showds.go进行了编译,并把结果文件存放到了$WORK/command-line-arguments/_obj目录下,名为_go_.8。我们在讲```go build```命令时提到过,```8g```命令是Go语言的官方编译器在x86(32bit)计算架构的计算机上所使用的编译程序。我们看到,编译结果文件的扩展名与底层编译命令名中的数字相对应。 +然后,命令程序使用Go语言工具目录`compile`命令对命令源码文件showds.go进行了编译,并把结果文件存放到了$WORK目录下,名为command-line-arguments.a。其中,`compile`是Go语言自带的编程工具。 -编译成功后,命令程序使用```pack```命令将编译文件打包并直接存放到临时工作目录中。而后,它再用连接命令```8l```生成最终的可执行文件,并存于$WORK/command-line-arguments/_obj/exe/目录中。打印信息中的最后一行表示,命令运行了生成的可执行文件。 +在编译成功之后,命令程序使用链接命令`link`生成最终的可执行文件,并将其存于$WORK/command-line-arguments/_obj/exe/目录中。打印信息中的最后一行表示,命令运行了生成的可执行文件。 通过对这些打印出来的命令的解读,我们了解了临时工作目录的用途以和内容。 -在上面的示例中,我们只是让```go run```命令打印出运行命令源码文件showds.go过程中需要执行的命令,而没有真正运行它。如果我们想真正运行命令源码文件showds.go并且想知道临时工作目录的位置,就需要去掉标记```-n```并且加上标记```-work```。当然,如果依然想看到过程中执行的命令,可以加上标记```-x```。如果读者已经看过之前我们对```go build```命令的介绍,就应该知道标记```-x```与标记```-n```一样会打印出过程执行的命令,但不同的这些命令会被真正的执行。调整这些标记之后的命令就像这样: +在上面的示例中,我们只是让`go run`命令打印出运行命令源码文件showds.go过程中需要执行的命令,而没有真正运行它。如果我们想真正运行命令源码文件showds.go并且想知道临时工作目录的位置,就需要去掉标记`-n`并且加上标记`-work`。当然,如果依然想看到过程中执行的命令,可以加上标记`-x`。如果读者已经看过之前我们对`go build`命令的介绍,就应该知道标记`-x`与标记`-n`一样会打印出过程执行的命令,但不同的这些命令会被真正的执行。调整这些标记之后的命令就像这样: - hc@ubt:~/golang/goc2p/src/helper/ds$ go run -x -work showds.go +```bash +hc@ubt:~/golang/goc2p/src/helper/ds$ go run -x -work showds.go +``` 当命令真正执行后,临时工作目录中就会出现实实在在的内容了,像这样: - /tmp/go-build204903183: - path/ - _obj/ - _go_.8 - path.a - command-line-arguments/ - _obj/ - exe/ - showds - _go_.8 - command-line-arguments.a +```bash +/tmp/go-build604555989: + command-line-arguments/ + _obj/ + exe/ + showds + command-line-arguments.a +``` -由于上述命令中包含了```-work```标记,所以我们可以从其输出中找到实际的工作目录(这里是/tmp/go-build204903183)。有意思的是,我们恰恰可以通过运行命令源码文件showds.go来查看这个临时工作目录的目录树: +由于上述命令中包含了`-work`标记,所以我们可以从其输出中找到实际的工作目录(这里是/tmp/go-build604555989)。有意思的是,我们恰恰可以通过运行命令源码文件showds.go来查看这个临时工作目录的目录树: - hc@ubt:~/golang/goc2p/src/helper/ds$ go run showds.go -p /tmp/go-build204903183 +```bash +hc@ubt:~/golang/goc2p/src/helper/ds$ go run showds.go -p /tmp/go-build604555989 +``` 读者可以自己试一试。 -我们在前面介绍过,命令源码文件如果可以接受参数,则可以在执行```go run```命令运行这个命令源码文件时把参数名和参数值成对的追加在后面。实际上,如果在命令后追加参数,那么在最后执行生成的可执行文件的时候也会追加一致的参数。例如,如果这样执行命令: +我们在前面介绍过,命令源码文件如果可以接受参数,则可以在执行`go run`命令运行这个命令源码文件时把参数名和参数值成对的追加在后面。实际上,如果在命令后追加参数,那么在最后执行生成的可执行文件的时候也会追加一致的参数。例如,如果这样执行命令: - hc@ubt:~/golang/goc2p/src/helper/ds$ go run -n showds.go -p ~/golang/goc2p +```bash +hc@ubt:~/golang/goc2p/src/helper/ds$ go run -n showds.go -p ~/golang/goc2p +``` -那么打印的最后一个命令就是: +那么带`-x`或`-n`标记的命令程序打印的最后一个命令就是: - $WORK/command-line-arguments/_obj/exe/showds -p /home/freej/golang/goc2p +```bash +$WORK/command-line-arguments/_obj/exe/showds -p /home/hc/golang/goc2p +``` -可见,```go run```命令会把追加到命令源码文件后面的参数原封不动的传给对应的可执行文件。 - -这就是一个命令源码文件从编译到运行的全过程。请记住,```go run```命令包含了两个动作:编译命令源码文件和运行对应的可执行文件。 - - +可见,`go run`命令会把追加到命令源码文件后面的参数原封不动的传给对应的可执行文件。 -## links - * [目录](catalog.md) - * 上一节: [godoc](0.5.md) - * 下一节: [go test](0.7.md) +以上简要展示了一个命令源码文件从编译到运行的全过程。请记住,`go run`命令包含了两个动作:编译命令源码文件和运行对应的可执行文件。 diff --git a/0.7.md b/0.7.md index ea13998..8ab07d1 100644 --- a/0.7.md +++ b/0.7.md @@ -1,91 +1,72 @@ -### 0.7 go test +# go test -```go test```命令用于对Go语言编写的程序进行测试。这种测试是以代码包为单位的。当然,这还需要测试源码文件的帮助。关于怎样编写并写好Go程序测试代码,我们会在本章的第二节加以详述。在这里,我们只讨论怎样使用命令启动测试。 +`go test`命令用于对Go语言编写的程序进行测试。这种测试是以代码包为单位的。当然,这还需要测试源码文件的帮助。关于怎样编写并写好Go程序测试代码,我们会在本章的第二节加以详述。在这里,我们只讨论怎样使用命令启动测试。 -```go test```命令会自动测试每一个指定的代码包。当然,前提是指定的代码包中存在测试源码文件。关于测试源码文件方面的知识,我们已经在第二章的第二节中介绍过。测试源码文件是名称以“_test.go”为后缀的、内含若干测试函数的源码文件。测试函数一般是以“Test”为名称前缀并有一个类型为“testing.T”的参数声明的函数. +`go test`命令会自动测试每一个指定的代码包。当然,前提是指定的代码包中存在测试源码文件。关于测试源码文件方面的知识,在我的图书《Go并发编程实战》中有详细介绍。测试源码文件是名称以“_test.go”为后缀的、内含若干测试函数的源码文件。测试函数一般是以“Test”为名称前缀并有一个类型为“testing.T”的参数声明的函数. 现在,我们来测试goc2p项目中的几个代码包。在使用```go test```命令时指定代码包的方式与其他命令无异——使用代码包导入路径。如果需要测试多个代码包,则需要在它们的导入路径之间加入空格以示分隔。示例如下: - hc@ubt:~$ go test basic cnet/ctcp pkgtool - ok basic 0.010s - ok cnet/ctcp 2.018s - ok pkgtool 0.009s +```bash +hc@ubt:~$ go test basic cnet/ctcp pkgtool +ok basic 0.012s +ok cnet/ctcp 2.014s +ok pkgtool 0.014s +``` -```go test```命令在执行完所有的代码包中的测试文件之后,会以代码包为单位打印出测试概要信息。在上面的示例中,对应三个代码包的三行信息的第一列都是“ok”。这说明它们都通过了测试。每行的第三列显示运行相应测试所用的时间,以秒为单位。我们还可以在代码包目录下运行不加任何参数的运行```go test```命令。其作用和结果与上面的示例是一样的。 +`go test`命令在执行完所有的代码包中的测试文件之后,会以代码包为单位打印出测试概要信息。在上面的示例中,对应三个代码包的三行信息的第一列都是“ok”。这说明它们都通过了测试。每行的第三列显示运行相应测试所用的时间,以秒为单位。我们还可以在代码包目录下运行不加任何参数的运行`go test`命令。其作用和结果与上面的示例是一样的。 -另外,我们还可以指定测试源码文件来进行测试。这样的话,```go test```命令只会执行指定文件中的测试,像这样: +另外,我们还可以指定测试源码文件来进行测试。这样的话,`go test`命令只会执行指定文件中的测试,像这样: +```bash hc@ubt:~/golang/goc2p/src/pkgtool$ go test envir_test.go - # command-line-arguments - ./envir_test.go:20: undefined: GetGoroot - ./envir_test.go:34: undefined: GetAllGopath - ./envir_test.go:74: undefined: GetSrcDirs - ./envir_test.go:76: undefined: GetAllGopath - ./envir_test.go:83: undefined: GetGoroot - FAIL command-line-arguments [build failed] - -我们看到,与指定源码文件进行编译或运行一样,命令程序会为指定的源码文件生成一个虚拟代码包——“command-line-arguments”。但是,测试并没有通过。但其原因并不是测试失败,而是编译失败。对于运行这次测试的命令程序来说,测试源码文件envir_test.go是属于代码包“command-line-arguments”的。并且,这个测试源码文件中使用了库源码文件envir.go中的函数。可以,它却没有显示导入这个库源码文件所属的代码包,这当然会引起编译错误。如果想解决这个问题,我们还需要在执行命令时加入这个测试源码文件所测试的那个源码文件。示例如下: - - hc@ubt:~/golang/goc2p/src/pkgtool$ go test envir_test.go envir.go - ok command-line-arguments 0.008s +# command-line-arguments +./envir_test.go:25: undefined: GetGoroot +./envir_test.go:40: undefined: GetAllGopath +./envir_test.go:81: undefined: GetSrcDirs +./envir_test.go:83: undefined: GetAllGopath +./envir_test.go:90: undefined: GetGoroot +FAIL command-line-arguments [build failed] +``` + +我们看到,与指定源码文件进行编译或运行一样,命令程序会为指定的源码文件生成一个虚拟代码包——“command-line-arguments”。但是,测试并没有通过。但其原因并不是测试失败,而是编译失败。对于运行这次测试的命令程序来说,测试源码文件envir_test.go是属于代码包“command-line-arguments”的。并且,这个测试源码文件中使用了库源码文件envir.go中的函数。但是,它却没有显示导入这个库源码文件所属的代码包。这显然会引起编译错误。如果想解决这个问题,我们还需要在执行命令时加入这个测试源码文件所测试的那个源码文件。示例如下: + +```bash +hc@ubt:~/golang/goc2p/src/pkgtool$ go test envir_test.go envir.go +ok command-line-arguments 0.010s +``` -现在,我们故意使代码包```pkgtool```中的某个测试失败。现在我们再来运行测试: +现在,我们故意使代码包`pkgtool`中的某个测试失败。现在我们再来运行测试: - hc@ubt:~$ go test basic cnet/ctcp pkgtool - ok basic 0.010s - ok cnet/ctcp 2.015s - --- FAIL: TestGetSrcDirs (0.00 seconds) - envir_test.go:85: Error: The src dir '/usr/local/go/src/pkg' is incorrect. - FAIL - FAIL pkgtool 0.009s +```bash +hc@ubt:~$ go test basic cnet/ctcp pkgtool +ok basic 0.010s +ok cnet/ctcp 2.015s +--- FAIL: TestGetSrcDirs (0.00 seconds) + envir_test.go:85: Error: The src dir '/usr/local/go/src/pkg' is incorrect. +FAIL +FAIL pkgtool 0.009s +``` -我们通过以上示例中的概要信息获知,测试源码文件中envir_test.go的测试函数```TestGetSrcDirs```中的测试失败了。在包含测试失败的测试源码文件名的那一行信息中,紧跟测试源码文件名的用冒号分隔的数字是错误信息所处的行号,在行号后面用冒号分隔的是错误信息。这个错误信息的内容是用户自行编写的。另外,概要信息的最后一行以“FAIL”为前缀。这表明针对代码包pkgtool的测试未通过。未通过的原因在前面的信息中已有描述。 +我们通过以上示例中的概要信息获知,测试源码文件中envir_test.go的测试函数`TestGetSrcDirs`中的测试失败了。在包含测试失败的测试源码文件名的那一行信息中,紧跟测试源码文件名的用冒号分隔的数字是错误信息所处的行号,在行号后面用冒号分隔的是错误信息。这个错误信息的内容是用户自行编写的。另外,概要信息的最后一行以“FAIL”为前缀。这表明针对代码包pkgtool的测试未通过。未通过的原因在前面的信息中已有描述。 一般情况下,我们会把测试源码文件与被测试的源码文件放在同一个代码包中。并且,这些源码文件中声明的包名也都是相同的。除此之外我们还有一种选择,那就是测试源码文件中声明的包名可以是所属包名再加“_test”后缀。我们把这种测试源码文件叫做包外测试源码文件。不过,包外测试源码文件存在一个弊端,那就是在它们的测试函数中无法测试被测源码文件中的包级私有的程序实体,比如包级私有的变量、函数和结构体类型。这是因为这两者的所属代码包是不相同的。所以,我们一般很少会编写包外测试源码文件。 **关于标记** -```go test```命令的标记处理部分是庞大且繁杂的,以至于使Go语言的开发者们不得不把这一部分的逻辑从```go test```命令程序主体中分离出来并建立单独的源码文件。因为```go test```命令中包含了编译动作,所以它可以接受可用于```go build```命令的所有标记。另外,它还有很多特有的标记。这些标记的用于控制命令本身的动作,有的用于控制和设置测试的过程和环境,还有的用于生成更详细的测试结果和统计信息。 +`go test`命令的标记处理部分是庞大且繁杂的,以至于使Go语言的开发者们不得不把这一部分的逻辑从`go test`命令程序主体中分离出来并建立单独的源码文件。因为`go test`命令中包含了编译动作,所以它可以接受可用于`go build`命令的所有标记。另外,它还有很多特有的标记。这些标记的用于控制命令本身的动作,有的用于控制和设置测试的过程和环境,还有的用于生成更详细的测试结果和统计信息。 -可用于```go test```命令的两个比较常用的标记是```-i```和标记```-c```。这两个就是用于控制```go test```命令本身的动作的标记。详见下表。 +可用于`go test`命令的几个比较常用的标记是`-c`、`-i`和`-o`。这两个就是用于控制`go test`命令本身的动作的标记。详见下表。 表0-6 ```go test```命令的标记说明 - - - - - - - - - - - - - -
- 标记名称 - - 标记描述 -
- -c - - 生成用于运行测试的可执行文件,但不执行它。 -
- -i - - 安装/重新安装运行测试所需的依赖包但不编译和运行测试代码。 -
- -上述这两个标记可以搭配使用。搭配使用的目的就是让```go test```命令既安装依赖包又编译测试代码,但不运行测试。也就是说,让命令程序跑一遍运行测试之前的所有流程。这可以测试一下测试过程。需要注意的是,在加入```-c```标记后,命令程序在编译测试代码并生成用于运行测试的一系列文件之后会把临时工作目录及其下的所有内容一并删除。如果想在命令执行结束后再去查看这些内容的话,我们还需要加入```-work```标记。 - -除此之外,```go test```命令还有很多功效各异的标记。但是由于这些标记的复杂性,我们需要结合测试源码文件进行详细的讲解。所以我们把这些内容放在了本章的第二节中。 - - - -## links - * [目录](catalog.md) - * 上一节: [go run](0.6.md) - * 下一节: [go list](0.8.md) \ No newline at end of file + +标记名称 | 标记描述 +------------ | ------------- +-c | 生成用于运行测试的可执行文件,但不执行它。这个可执行文件会被命名为“pkg.test”,其中的“pkg”即为被测试代码包的导入路径的最后一个元素的名称。 +-i | 安装/重新安装运行测试所需的依赖包,但不编译和运行测试代码。 +-o | 指定用于运行测试的可执行文件的名称。追加该标记不会影响测试代码的运行,除非同时追加了标记`-c`或`-i`。 + +上述这几个标记可以搭配使用。搭配使用的目的可以是让`go test`命令既安装依赖包又编译测试代码,但不运行测试。也就是说,让命令程序跑一遍运行测试之前的所有流程。这可以测试一下测试过程。注意,在加入`-c`标记后,命令程序会把用于运行测试的可执行文件存放到当前目录下。 + +除此之外,`go test`命令还有很多功效各异的标记。但是由于这些标记的复杂性,我们需要结合测试源码文件进行详细的讲解。所以我们在这里略过不讲。如果读者想了解相关详情,请参看《Go并发编程实战》的第5章。 \ No newline at end of file diff --git a/0.8.md b/0.8.md index 531dd81..1bd4a34 100644 --- a/0.8.md +++ b/0.8.md @@ -1,32 +1,38 @@ -### 0.8 go list +# go list -```go list```命令的作用是列出指定的代码包的信息。与其他命令相同,我们需要以代码包导入路径的方式给定代码包。被给定的代码包可以有多个。这些代码包对应的目录中必须直接保存有Go语言源码文件,其子目录中的文件不算在内。否则,代码包将被看做是不完整的。现在我们来试用一下: +`go list`命令的作用是列出指定的代码包的信息。与其他命令相同,我们需要以代码包导入路径的方式给定代码包。被给定的代码包可以有多个。这些代码包对应的目录中必须直接保存有Go语言源码文件,其子目录中的文件不算在内。否则,代码包将被看做是不完整的。现在我们来试用一下: - hc@ubt:~$ go list cnet/ctcp pkgtool - cnet/ctcp - pkgtool +```bash +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 +```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```时,即使参数中包含有不完整的代码包,命令也不会提示错误。示例如下: +这时,`go list`命令报告了一个错误——代码包`cnet`对应的目录下没有Go源码文件。但是命令还是把代码包pkgtool的导入路径打印出来了。然而,当我们在执行`go list`命令并加入标记`-e`时,即使参数中包含有不完整的代码包,命令也不会提示错误。示例如下: - hc@ubt:~$ go list -e cnet pkgtool - cnet - pkgtool +```bash +hc@ubt:~$ go list -e cnet pkgtool +cnet +pkgtool +``` -标记```-e```的作用是以容错模式加载和分析指定的代码包。在这种情况下,命令程序如果在加载或分析的过程中遇到错误只会在内部记录一下,而不会直接把错误信息打印出来。我们为了看到错误信息可以使用```-json```标记。这个标记的作用是把代码包的结构体实例用JSON的样式打印出来。 +标记`-e`的作用是以容错模式加载和分析指定的代码包。在这种情况下,命令程序如果在加载或分析的过程中遇到错误只会在内部记录一下,而不会直接把错误信息打印出来。我们为了看到错误信息可以使用`-json`标记。这个标记的作用是把代码包的结构体实例用JSON的样式打印出来。 -这里解释一下,JSON的全称是Javascript Object Notation。它一种轻量级的承载数据的格式。JSON的优势在于语法简单、短小精悍,且非常易于处理。JSON还是一种纯文本格式,独立于编程语言。正因为如此,得到了绝大多数编程语言和浏览器的支持,应用非常广泛。Go语言当然也不例外,在它的标准库中有专门用于处理和转换JSON格式的数据的代码包```encoding/json```。关于JSON格式的具体内容,读者可以去它的[官方网站](http://www.json.org)查看说明。 +这里解释一下,JSON的全称是Javascript Object Notation。它一种轻量级的承载数据的格式。JSON的优势在于语法简单、短小精悍,且非常易于处理。JSON还是一种纯文本格式,独立于编程语言。正因为如此,得到了绝大多数编程语言和浏览器的支持,应用非常广泛。Go语言当然也不例外,在它的标准库中有专门用于处理和转换JSON格式的数据的代码包`encoding/json`。关于JSON格式的具体内容,读者可以去它的[官方网站](http://www.json.org)查看说明。 -在了解了这些基本概念之后,我们来试用一下```-json```标记。示例如下: +在了解了这些基本概念之后,我们来试用一下`-json`标记。示例如下: - hc@ubt:~$ go list -e -json cnet +```bash +hc@ubt:~$ go list -e -json cnet { "Dir": "/home/hc/golang/goc2p/src/cnet", "ImportPath": "cnet", @@ -41,531 +47,183 @@ "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格式的代码包信息中,对于结构体中的字段的显示是不完整的。因为命令程序认为我们指定`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命令程序中的代码包结构体都有哪些公开的字段。如下表。 +现在我们使用带`-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) - - 代码包的导入路径。 -
- Name - - 字符串(string) - - 代码包的名称。 -
- Doc - - 字符串(string) - - 代码包的文档字符串。 -
- Target - - 字符串(string) - - 代码包的安装路径。 -
- Goroot - - 布尔(bool) - - 代码包是否在Go安装目录下。 -
- Standard - - 布尔(bool) - - 代码包是否属于标准库的一部分。 -
- Stale - - 布尔(bool) - - 代码包能否被```go install```命令安装。 -
- Root - - 字符串(string) - - 代码包所属的工作区或Go安装目录的路径。 -
+ +字段名称 | 字段类型 | 字段描述 +------------- | --------------- | --------------- +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”后缀的文件的数组。 -
- HFiles - - 字符串切片([]string) - - 名称中有“.h”后缀的文件的数组。 -
- SFiles - - 字符串切片([]string) - - 名称中有“.s”后缀的文件的数组。 -
- SysoFiles - - 字符串切片([]string) - - 名称中有“.syso”后缀的文件的数组。这些文件需要被加入到归档文件中。 -
- SwigFiles - - 字符串切片([]string) - - 名称中有“.swig”后缀的文件的数组。 -
- SwigCXXFiles - - 字符串切片([]string) - - 名称中有“.swigcxx”后缀的文件的数组。 -
+ +字段名称 | 字段类型 | 字段描述 +-------------- | -------------------- | --------------- +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。 -
- CgoLDFLAGS - - 字符串切片([]string) - - 需要传递给链接器的标记的数组。针对于Cgo。 -
- CgoPkgConfig - - 字符串切片([]string) - - pkg-config的名称的数组。针对于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) - - 所有的依赖包(包括间接依赖)的导入路径的数组。 -
+ +字段名称 | 字段类型 | 字段描述 +-------------- | -------------------- | --------------- +Imports | 字符串切片([]string) | 该代码包中的源码文件显式导入的依赖包的导入路径的列表。 +Deps | 字符串切片([]string) | 所有的依赖包(包括间接依赖)的导入路径的列表。 表0-11 代码包结构体中与错误信息有关的字段 - - - - - - - - - - - - - - - - - - - - - -
- 字段名称 - - 字段类型 - - 字段描述 -
- Incomplete - - 布尔(bool) - - 代码包是否是完整的,也即在载入或分析代码包及其依赖包时是否有错误发生。 -
- Error - - *PackageError类型 - - 载入或分析代码包时发生的错误。 -
- Error - - *PackageError类型的数组([]*PackageError) - - 载入或分析代码包的依赖包时发生的错误。 -
+ +字段名称 | 字段类型 | 字段描述 +-------------- | -------------------- | --------------- +Incomplete | 布尔(bool) | 代码包是否是完整的,也即在载入或分析代码包及其依赖包时是否有错误发生。 +Error | \*PackageError类型 | 载入或分析代码包时发生的错误。 +DepsErrors | []\*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```命令很有用。它可以为我们提供指定代码包的更深层次的信息。这些信息往往是我们无法从源码文件中直观看到的。 - - - -## links - * [目录](catalog.md) - * 上一节: [go test](0.7.md) - * 下一节: [go fmt与gofmt](0.9.md) \ No newline at end of file + +字段名称 | 字段类型 | 字段描述 +-------------- | -------------------- | --------------- +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`命令很有用。它可以为我们提供指定代码包的更深层次的信息。这些信息往往是我们无法从源码文件中直观看到的。 \ No newline at end of file diff --git a/0.9.md b/0.9.md index 5761f52..ddbf8c8 100644 --- a/0.9.md +++ b/0.9.md @@ -1,140 +1,62 @@ -### 0.9 go fmt与gofmt +# go fmt与gofmt -```go fmt```命令会按照Go语言代码规范格式化指定代码包中的所有Go语言源码文件的代码,所有Go语言源码文件即包括命令源码文件、库源码文件和测试源码文件。注意,当代码包还有子代码包时,子代码包中的Go语言源码文件是不包含在内的。也就是说,```go fmt```命令只会格式化被直接保存在指定代码包对应目录下的Go语言源码文件。 - -和```go doc```命令与```godoc```命令的关系一样,```go fmt```命令也是```gofmt```命令的简单封装。```go fmt```命令本身可以接受两个标记。标记```-n```仅仅打印出内部使用的```gofmt```命令及其标记和参数而不真正执行它。 标记```-x```则既打印又执行这个命令。在```go fmt```命令程序内部,会在其调用的```gofmt```命令加上标记```-l```和```-w```,并以指定代码包中的所有Go语言源码文件的路径作为参数,就像这样: - - hc@ubt:~$ go fmt -n pkgtool - gofmt -l -w golang/goc2p/src/pkgtool/envir.go golang/goc2p/src pkgtoolenvir_test.go golang/goc2p/src/pkgtool/fpath.go golang/goc2p/src/pkgtool ipath.go golang/goc2p/src/pkgtool/pnode.go golang/goc2p/src/pkgtool/util.go golang/goc2p/src/pkgtool/util_test.go - -注意,作为```gofmt```命令参数的Go语言源码文件的路径是相对的,而不是绝对的。不过这只是为了让参数看起来更短一些而已。所以,当我们直接执行```gofmt```命令的时候,使用源码文件的绝对路径作为参数也是没有问题的。实际上,任何Go源码文件或包含有Go语言源码文件的目录的相对路径或绝对路径都可以作为```gofmt```命令的参数。当使用包含有Go语言源码文件的目录的绝对路径或相对路径作为参数时,```gofmt```命令会把被直接保存在这个目录下的Go语言源码文件作为目标源码文件。 - -```go fmt```命令程序内部在执行```gofmt```命令时加入的标记是固定的。如果我们想使用与之不同的标记集合就必须直接使用```gofmt```命令了。现在我们来看一下```gofmt```命令可接受的所有标记。如下表。 - -_表0-13 ```gofmt```命令的标记说明_ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 标记名称 - - 标记描述 -
- -l - - 仅把那些不符合格式化规范的、需要被命令程序改写的源码文件的绝对路径打印到标准输出。而不是把改写后的全部内容都打印到标准输出。 -
- -w - - 把改写后的内容直接写入到文件中,而不是作为结果打印到标准输出。 -
- -r - - 添加形如“a[b:len(a)] -> a[b:]”的重写规则。如果我们需要自定义某些额外的格式化规则,就需要用到它。 -
- -s - - 简化文件中的代码。 -
- -d - - 只把改写前后的内容的对比信息作为结果打印到标准输出。而不是把改写后的全部内容都打印到标准输出。命令程序将使用diff命令对内容进行比对。在Windows操作系统下可能没有diff命令,需要另行安装。 -
- -e - - 打印所有的语法错误到标准输出。如果不使用此标记,则只会打印每行的第1个错误且只打印前10个错误。 -
- -comments - - 是否保留源码文件中的注释。在默认情况下,此标记会被隐式的使用,并且值为true。 -
- -tabwidth - - 此标记用于设置代码中缩进所使用的空格数量,默认值为8。要使此标记生效,需要使用“-tabs”标记并把值设置为false。 -
- -tabs - - 是否使用tab('\t')来代替空格表示缩进。在默认情况下,此标记会被隐式的使用,并且值为true。 -
- -cpuprofile - - 是否开启CPU使用情况记录,并将记录内容保存在此标记值所指的文件中。 -
- -看过上表中的信息,我们就很容易理解```go fmt```命令的行为了。因为它在内部执行了```gofmt```命令,并加入了标记```-l```和```-w```。这会使命令程序打印需要改写的文件的绝对路径到标准输出,并且直接把改写的内容写入到文件中。在默认情况下,```gofmt```命令会把改写后内容直接打印到标准输出上。 +`go fmt`命令会按照Go语言代码规范格式化指定代码包中的所有Go语言源码文件的代码,所有Go语言源码文件即包括命令源码文件、库源码文件和测试源码文件。注意,当代码包还有子代码包时,子代码包中的Go语言源码文件是不包含在内的。也就是说,`go fmt`命令只会格式化被直接保存在指定代码包对应目录下的Go语言源码文件。 + +与`go doc`命令和`godoc`命令的关系类似,`go fmt`命令是`gofmt`命令的简单封装。`go fmt`命令本身可以接受两个标记。标记`-n`可以让命令程序仅打印出内部使用的`gofmt`命令及其标记和参数而不真正执行它。标记`-x`则会使命令程序既打印又执行这个命令。在`go fmt`命令程序内部,会在其调用的`gofmt`命令后面加上标记`-l`和`-w`,并以指定代码包中的所有Go语言源码文件的路径作为参数,就像这样: + +```bash +hc@ubt:~$ go fmt -n pkgtool +gofmt -l -w golang/goc2p/src/pkgtool/envir.go golang/goc2p/src pkgtoolenvir_test.go golang/goc2p/src/pkgtool/fpath.go golang/goc2p/src/pkgtool ipath.go golang/goc2p/src/pkgtool/pnode.go golang/goc2p/src/pkgtool/util.go golang/goc2p/src/pkgtool/util_test.go +``` + +注意,作为`gofmt`命令参数的Go语言源码文件的路径是相对的,而不是绝对的。不过这只是为了让参数看起来更短一些而已。所以,当我们直接执行`gofmt`命令的时候,使用源码文件的绝对路径作为参数也是没有问题的。实际上,任何Go源码文件或包含有Go语言源码文件的目录的相对路径或绝对路径都可以作为`gofmt`命令的参数。当使用包含有Go语言源码文件的目录的绝对路径或相对路径作为参数时,`gofmt`命令会把在这个目录下的Go语言源码文件作为目标源码文件。 + +`go fmt`命令程序内部在执行`gofmt`命令时加入的标记是固定的。如果我们想使用与之不同的标记集合就必须直接使用`gofmt`命令了。现在我们来看一下`gofmt`命令可接受的所有标记。如下表。 + +_表0-13 `gofmt`命令的标记说明_ + +标记名称 | 标记描述 +----------- | --------------- +-cpuprofile | 把CPU概要写入指定文件。文件的路径应该作为此标记的值。 +-d | 显示格式化前后的不同(如果有的话),而不是直接格式化那些代码。 +-e | 报告目标源码文件中的所有错误。默认情况下,仅会显示前10个错误。 +-l | 仅把那些不符合格式化规范的、需要被命令程序改写的源码文件的绝对路径打印到标准输出。而不是把改写后的全部内容都打印到标准输出。 +-r | 添加形如“a[b:len(a)] -> a[b:]”的重写规则。如果我们需要自定义某些额外的格式化规则,就需要用到它。规则字符串应该作为此标记的值。 +-s | 简化文件中的代码。 +-w | 把改写后的内容直接写入到文件中,而不是作为结果打印到标准输出。 + +看过上表中的信息,我们就很容易理解`go fmt`命令的行为了。因为它在内部执行了`gofmt`命令,并加入了标记`-l`和`-w`。这会使命令程序打印需要改写的文件的绝对路径到标准输出,并且直接把格式化后的内容写入到原始文件中。在默认情况下,`gofmt`命令会把格式化后的内容直接打印到标准输出上。 实际上,命令程序会把目标源码文件中的内容解析成抽象语法树。当在解析过程中发现语法错误时,命令程序就会显示错误提示信息并退出。在默认情况下,目标源码文件中的语法错误不会全部被显示出来。我们可以加入标记```-e```以使命令程序打印出全部错误到标准输出。 **自定义改写操作** -在默认情况下,```gofmt```命令对Go语言源码文件的改写操作包括如下几个方面: +在默认情况下,`gofmt`命令对Go语言源码文件的改写操作包括如下几个方面: + 以字典序排序依赖包导入语句块中代码包导入路径的先后顺序。 -+ 标准化各个语言或语句块之间的缩进、空格和换行。比如,把所有的```\r\n```转换成```\n```。 ++ 标准化各个语言或语句块之间的缩进、空格和换行。比如,把所有的`\r\n`转换成`\n`。 + 对代码语法的小修正。比如,消除用于判断变量类型的switch语句块中多余的圆括号。 -如果想自定义额外的改写操作,需要使用```-r```标记。```-r```标记的值中必须包含“->”,比如```a[b:len(a)] -> a[b:]```。“->”的左边应该是需要被替代的表达式的示例,而右边则应该是用来替代“->”左边表达式的表达式的示例。 +如果想自定义额外的改写操作,需要使用`-r`标记。`-r`标记的值中必须包含“->”,比如`a[b:len(a)] -> a[b:]`。“->”的左边应该是需要被替代的表达式的示例,而右边则应该是用来替代“->”左边表达式的表达式的示例。 -如果我们使用标记```-r```,那么命令程序是在解析源码文件之前,会将此标记值中的被替换表达式和替换表达式分别解析为抽象语法树的表达式节点。如果解析不成功,也就意味着无法进行后续的替换操作,命令程序会在打印错误提示信息后退出。如果解析成功,那么命令程序会在解析源码文件成功之后进行表达式替换操作。命令程序会寻找该源码文件的抽象语法树中与被替换表达式相匹配的节点,并用替换表达式替换之。```gofmt```命令已支持但不限于如下自定义替换操作: +如果我们使用标记`-r`,那么命令程序在解析源码文件之前会将此标记值中的被替换表达式和替换表达式分别解析为抽象语法树的表达式节点。如果解析不成功,也就意味着无法进行后续的替换操作,命令程序会在打印错误提示信息后退出。如果解析成功,那么命令程序会在解析源码文件成功之后进行表达式替换操作。命令程序会寻找该源码文件的抽象语法树中与被替换表达式相匹配的节点,并用替换表达式替换之。`gofmt`命令已支持但不限于如下自定义替换操作: -+ 程序实体名称的替换。程序实体包括变量、常量、函数、结构体和接口。比如:```-r=array1->array2```和```-r=FuncA->FuncB```。 ++ 程序实体名称的替换。程序实体包括变量、常量、函数、结构体和接口。比如:`-r=array1->array2`和`-r=FuncA->FuncB`。 -+ 程序实体类型的替换,其中也包含函数的参数和结果的类型的替换。比如:```-r=string->bool```和```-r=interface{}->int```。 ++ 程序实体类型的替换,其中也包含函数的参数和结果的类型的替换。比如:`-r=string->bool`和`-r=interface{}->int`。 -+ 多余圆括号的清除。比如:我们这样设置标记```-r=(x)->x```会使目标代码中的```a = (-x.s)```被改写为```a = -x.s```,也会使代码中的```((b = -x.f()))```被改写为```b = -x.f()```,还会使```c = -(x).f```改写为````c = -x.f```,但是不会去掉```d = (&x).s```和```e = (-x).f()```中的圆括号。也就是说,命令程序会在不改变语义和不产生语法歧义的前提下清除代码中多余的圆括号。 ++ 多余圆括号的清除。比如:我们这样设置标记`-r=(x)->x`会使目标代码中的`a = (-x.s)`被改写为`a = -x.s`,也会使代码中的`((b = -x.f()))`被改写为`b = -x.f()`,还会使`c = -(x).f`改写为`c = -x.f`,但是不会去掉`d = (&x).s`和`e = (-x).f()`中的圆括号。也就是说,命令程序会在不改变语义和不产生语法歧义的前提下清除代码中多余的圆括号。 -+ 数值操作的替换。比如:我们这样设置标记```-r=x+x->x*2```会使代目标码中的所有的```x + x```被替换为```x * 2```。并且,如果需要被替换的表达式中包含注释的话,则在替换操作的过程中会去掉这些注释。比如,在同样的标记设置的情况下```x /* It's comment */ + x```仍然会被替换为```x * 2```。 ++ 数值操作的替换。比如:我们这样设置标记`-r=x+x->x*2`会使代目标码中的所有的`x + x`被替换为`x * 2`。并且,如果需要被替换的表达式中包含注释的话,则在替换操作的过程中会去掉这些注释。比如,在同样的标记设置的情况下`x /* It's comment */ + x`仍然会被替换为`x * 2`。 -+ 基于参数列表的函数调用替换。比如:如果我们这样设置标记```-r='funcA(a)->FuncA(a, c)'```,则目标代码中调用函数```funcA```并以一个变量作为参数的语句都会被替换为调用函数```FuncA```并以变量```a```和变量```c```作为参数的语句。注意,被替换表达式中作为参数的```a```只是表明函数```funcA```有一个参数,而并不关心这个参数的名称是什么。也就是说,在同样的标记设置的情况下,目标代码中的```funcA(b)```或```funcA(x)```都会被替换为```FuncA(a, c)```。再或者,如果我们这样设置标记```-r='funB(x...)->FunC(x)'```,则目标代码中的```funB(x...)```或```funB(y...)```或其它类似的调用函数都会被替换为```FunC(x)```。其中,当类型为数组/切片的参数后跟三个英文半角句号“...”时,则表明需要把这个参数中的每一个元素都作为单独的参数传入到函数中。因此,这种替换方式可以用来在函数名称和/或参数列表改变之后,批量的跟进修正调用该函数的代码。 ++ 基于参数列表的函数调用替换。比如:如果我们这样设置标记`-r='funcA(a)->FuncA(a, c)'`,则目标代码中调用函数`funcA`并以一个变量作为参数的语句都会被替换为调用函数`FuncA`并以变量`a`和变量`c`作为参数的语句。注意,被替换表达式中作为参数的`a`只是表明函数`funcA`有一个参数,而并不关心这个参数的名称是什么。也就是说,在同样的标记设置的情况下,目标代码中的`funcA(b)`或`funcA(x)`都会被替换为`FuncA(a, c)`。再或者,如果我们这样设置标记`-r='funB(x...)->FunC(x)'`,则目标代码中的`funB(x...)`或`funB(y...)`或其它类似的调用函数都会被替换为`FunC(x)`。其中,当类型为数组/切片的参数后跟三个英文半角句号“...”时,则表明需要把这个参数中的每一个元素都作为单独的参数传入到函数中。因此,这种替换方式可以用来在函数名称和/或参数列表改变之后,批量的跟进修正调用该函数的代码。 **代码简化操作** -当我们在执行```gofmt```命令时加入了标记```-s```,命令程序会在目标源码文件中寻找可以简化的代码并简化它。简化操作包括: +当我们在执行`gofmt`命令时加入了标记`-s`,命令程序会在目标源码文件中寻找可以简化的代码并简化它。简化操作包括: + 消除在数组/切片初始化中的不必要的类型声明。 @@ -144,149 +66,32 @@ _表0-13 ```gofmt```命令的标记说明_ + 消除迭代时的非必要临时变量赋值操作。 -这些操作基本上都是出于尽量使用Go语言的语法糖已达到减少代码量的目的。我们在编写Go语言代码的时候应该直接使用这些语法糖而不应该依赖使用```gofmt```命令来简化。这里所说的Go语言的语法糖,我们在第3章中已经有所介绍。 - -我们在本小节中详细介绍了```go fmt```命令和```gofmt```命令。下面我们再汇总一下这两个命令可以为我们做的事情。如下表。 - -_表0-14 ```go fmt```命令和```gofmt```命令的功能_ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 功能 - - go fmt命令 - - gofmt命令 -
- 格式化代码。 - - √ - - √ -
- 列出不规范的源码文件。 - - √ - - √ -
- 自动改写源码文件。 - - √ - - √ -
- 显示对比信息。 - - × - - √ -
- 提示全部编译错误。 - - × - - √ -
- 清除注释。 - - × - - √ -
- 自定义缩进。 - - × - - √ -
- 简化代码。 - - × - - √ -
- 自定义替换/重构辅助。 - - × - - √ -
- CPU使用情况记录。 - - × - - √ -
- -最后,值得一提的是,当我们执行```gofmt```命令且没有加任何参数的时候,该命令将会进入到交互模式。在这种模式下,我们可以直接在命令行界面中输入源码,并以Ctrl-d结束。在Linux操作系统下,Ctrl-d代表EOF(End Of File,中文译为文件结束符)。需要注意的是,如果在一行的中间按下Ctrl-d,则表示输出“标准输入”的缓存区,所以这时必须连续按两次Ctrl-d。另外,在Windows操作系统下,Ctrl-z代表EOF,所以需要以Ctrl-z结束。在这之后,```gofmt```命令会像从源码文件中读取源码那样从命令行界面(也称为标准输入)读取源码,并在格式化后将结果打印到命令行界面(也称为标准输出)中。示例如下: - - hc@ubt:~$ gofmt -r='fmt.Println(a)->fmt.Printf("%s\n", a)' - if a=="print" {fmt.Println(a)} - warning: rewrite ignored for incomplete programs - if a == "print" { - fmt.Println(a) - } - -由上述示例可知,我们可以使用```gofmt```命令的交互模式格式化任意的代码片段。虽然会显示一行警告信息,但是格式化后的结果仍然会被打印出来。并且,在交互模式下,当我们输入的代码片段不符合Go语言的语法规则时,命令程序也会打印出错误提示信息。在其它方面,命令程序在交互模式与普通模式下的行为也是基本一致的。 - - - -## links - * [目录](catalog.md) - * 上一节: [go list](0.8.md) - * 下一节: [go fix与go tool fix](0.10.md) +这些操作基本上都是出于尽量使用Go语言的语法糖已达到减少代码量的目的。我们在编写Go语言代码的时候应该直接使用这些语法糖而不应该依赖使用`gofmt`命令来简化。这里所说的Go语言的语法糖,我们在第3章中已经有所介绍。 + +我们在本小节中详细介绍了`go fmt`命令和`gofmt`命令。下面我们再汇总一下这两个命令可以为我们做的事情。如下表。 + +_表0-14 `go fmt`命令和`gofmt`命令的功能_ + +功能 | `go fmt`命令 | `gofmt`命令 +-------------------- | ----------- | ----------- +格式化代码 | √ | √ +列出不规范的源码文件 | √ | √ +自动改写源码文件 | √ | √ +显示对比信息 | × | √ +提示全部错误 | × | √ +简化代码 | × | √ +自定义替换/重构辅助 | × | √ +CPU概要记录 | × | √ + +最后,值得一提的是,当我们执行`gofmt`命令且没有加任何参数的时候,该命令将会进入到交互模式。在这种模式下,我们可以直接在命令行界面中输入源码,并以Ctrl-d结束。在Linux操作系统下,Ctrl-d代表EOF(End Of File,中文译为文件结束符)。需要注意的是,如果在一行的中间按下Ctrl-d,则表示输出“标准输入”的缓存区,所以这时必须连续按两次Ctrl-d。另外,在Windows操作系统下,Ctrl-z代表EOF,所以需要以Ctrl-z结束。在这之后,`gofmt`命令会像从源码文件中读取源码那样从命令行界面(也称为标准输入)读取源码,并在格式化后将结果打印到命令行界面(也称为标准输出)中。示例如下: + +```bash +hc@ubt:~$ gofmt -r='fmt.Println(a)->fmt.Printf("%s\n", a)' +if a=="print" {fmt.Println(a)} <----- 在此行的末尾键入回车和Ctrl-d。 +warning: rewrite ignored for incomplete programs <----- 此行及以下就是命令输出的内容。 +if a == "print" { + fmt.Println(a) +} +``` + +由上述示例可知,我们可以使用`gofmt`命令的交互模式格式化任意的代码片段。虽然会显示一行警告信息,但是格式化后的结果仍然会被打印出来。并且,在交互模式下,当我们输入的代码片段不符合Go语言的语法规则时,命令程序也会打印出错误提示信息。在其它方面,命令程序在交互模式与普通模式下的行为也是基本一致的。 diff --git a/LICENSE b/LICENSE index 00459bc..9cecc1d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,28 +1,674 @@ -Copyright (c) 2014, Robert Hao -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of the {organization} nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md index 19d3a9f..2578466 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,31 @@ -go_command_tutorial -=================== +# GO 命令教程 -Golang command tutorial by Chinese. +## 作者介绍 -Base on Go 1.3.x +**郝林**,从事互联网软件研发和管理工作已有15年,在银行、电信、社交网络、互联网金融、电子商务、大数据等领域都工作过。我对Go语言和Julia语言都情有独钟,并且目前正在独立从事编程教育研究、专业内容输出、在线社群运营等工作。我制作和发布过一些很受欢迎的免费教程、技术图书和付费专栏,其中就包括本教程。另外还有(按时间排序):慕课网的免费教程[《Go语言第一课》](https://www.imooc.com/learn/345)、人邮图灵的原创技术图书[《Go并发编程实战》](https://www.ituring.com.cn/book/1950)、极客时间的付费专栏[《Go语言核心36讲》](https://time.geekbang.org/column/intro/112)等。 -本教程的由来 -=================== +## 本教程的由来 -这份Go命令教程原先是我著的图书《Go并发编程实战》(此书会由人民邮电出版社的图灵公司在2014年11月份出版)中的一部分。这部分内容与并发编程的关系不大,故被砍掉。但是它是有价值的,也算是我对Go语言官方提供的标准命令的一个学习笔记。所以,我觉得应该把它做成免费资源分享给大家。经出版社的认可,我将这份教程放在这里供广大Go语言爱好者阅读。 +这份Go命令教程原先是我撰写的图书[《Go并发编程实战》](http://www.ituring.com.cn/book/1525)中的一部分。这部分内容与并发编程的关系不大,故被砍掉。但是它是有价值的,也算是我对Go语言官方提供的标准命令的一个学习笔记。所以,我觉得应该把它做成免费资源分享给大家。经出版社的认可,我将这份教程放在这里供广大Go语言爱好者阅读。 +**信息更新**: +- 2017-04-01:[《Go并发编程实战》第2版](http://www.ituring.com.cn/book/1950)已经出版了。 -相关源码 -=================== +## 相关源 -本教程中会提及一个名为```goc2p```的项目。该项目实际上是《Go并发编程实战》随书附带的示例项目。这本书中讲到的所有源码都在```goc2p```项目中。我最迟会在《Go并发编程实战》出版之时将[```goc2p```](https://github.com/hyper-carrot/goc2p)项目放出。 +本教程在Github上的地址在[这里](https://github.com/hyper0x/go_command_tutorial)。如果你喜欢本教程,请不吝Star。如果你想贡献一份力量,欢迎提交Pull Request(提示:请小步pr,避免大刀阔斧)。 +针对Go 1.3和1.4版本的教程已被放入分支[go1.3](https://github.com/hyper0x/go_command_tutorial/tree/go1.3)。而主分支此后一段时间内会致力于针对Go 1.5进行更新。 -关于协议 -=================== +本教程中会提及一个名为`goc2p`的项目。该项目实际上是《Go并发编程实战》随书附带的示例项目。这本书中讲到的所有源码都在`goc2p`项目中。我已在《Go并发编程实战》出版之时将[`goc2p`](https://github.com/hyper0x/goc2p)项目放出。另外,《Go并发编程实战》第2版的示例项目在[这里](https://github.com/gopcp/example.v2),可供参考。 -我希望这个项目中的内容永远是免费的。也就是说,任何组织和个人不应该出于商业目的而摘取其中的内容。更详细的条款请阅读本项目中的[LICENSE](LICENSE)文件。 +## 关于协议 +我希望这个项目中的内容永远是免费的。也就是说,任何组织和个人不应该出于商业目的而摘取其中的内容。更详细的条款请阅读本项目中的[LICENSE](https://github.com/hyper0x/go_command_tutorial/blob/master/LICENSE)文件。 -开始阅读 -=================== +## 版本信息 +书中内容及演示代码基于以下版本: -请点击[目录](catalog.md)并开始阅读。如发现纰漏欢迎提issue,欢迎Pull Request。 +| 系统 | 版本信息 +|---------|------ +|Golang |1.3(will be abandoned) + 1.5(in progress) diff --git a/SUMMARY.md b/SUMMARY.md index 4cdf562..42ff358 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -1,17 +1,14 @@ -# Summary - -* [标准命令详解](0.0.md) - * [go build](0.1.md) - * [go install](0.2.md) - * [go get](0.3.md) - * [go clean](0.4.md) - * [godoc](0.5.md) - * [go run](0.6.md) - * [go test](0.7.md) - * [go list](0.8.md) - * [go fmt与go tool fix](0.10.md) - * [go vet与go tool vet](0.11.md) - * [go tool pprof](0.12.md) - * [go tool cgo](0.13.md) - * [go env](0.14.md) - +- [标准命令详解](0.0.md) + - [go build](0.1.md) + - [go install](0.2.md) + - [go get](0.3.md) + - [go clean](0.4.md) + - [go doc与godoc](0.5.md) + - [go run](0.6.md) + - [go test](0.7.md) + - [go list](0.8.md) + - [go fix与go tool fix](0.10.md) + - [go vet与go tool vet](0.11.md) + - [go tool pprof](0.12.md) + - [go tool cgo](0.13.md) + - [go env](0.14.md) diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..c50ff38 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-merlot \ No newline at end of file diff --git a/_sidebar.md b/_sidebar.md new file mode 100644 index 0000000..172a9e4 --- /dev/null +++ b/_sidebar.md @@ -0,0 +1,30 @@ +* [标准命令详解](0.0) + +* [go build](0.1) + +* [go install](0.2) + +* [go get](0.3) + +* [go clean](0.4) + +* [go doc 与 godoc](0.5) + +* [go run](0.6) + +* [go test](0.7) + +* [go list](0.8) + +* [go fmt 与 gofmt](0.9) + +* [go fix 与 go tool fix](0.10) + +* [go vet 与 go tool vet](0.11) + +* [go tool pprof](0.12) + +* [go tool cgo](0.13) + +* [go env](0.14) + diff --git a/catalog.md b/catalog.md index 981f75e..b5f4294 100644 --- a/catalog.md +++ b/catalog.md @@ -5,7 +5,7 @@ - 0.2 [go install](0.2.md) - 0.3 [go get](0.3.md) - 0.4 [go clean](0.4.md) - - 0.5 [godoc](0.5.md) + - 0.5 [go doc与godoc](0.5.md) - 0.6 [go run](0.6.md) - 0.7 [go test](0.7.md) - 0.8 [go list](0.8.md) diff --git a/config.json b/config.json index 770b6bd..be56450 100644 --- a/config.json +++ b/config.json @@ -1,9 +1,7 @@ { - "name": "Go命令教程", - "introduction": "带你玩儿转Go语言的命令和工具。", - "title": "go command tutorial", - "keywords": "golang", - "path": { - "toc": "catalog.md" - } + "name": "GO 命令教程", + "introduction":"这份Go命令教程原先是我著的图书《Go并发编程实战》中的一部分。这部分内容与并发编程的关系不大,故被砍掉。但是它是有价值的,也算是我对Go语言官方提供的标准命令的一个学习笔记。所以,我觉得应该把它做成免费资源分享给大家。", + "path": { + "toc": "SUMMARY.md" + } } diff --git a/cover/background.jpg b/cover/background.jpg new file mode 100755 index 0000000..7726503 Binary files /dev/null and b/cover/background.jpg differ diff --git a/cover/logo.png b/cover/logo.png new file mode 100644 index 0000000..8675af7 Binary files /dev/null and b/cover/logo.png differ diff --git a/images/0-1.jpg b/images/0-1.jpg deleted file mode 100644 index 527728d..0000000 Binary files a/images/0-1.jpg and /dev/null differ diff --git a/images/0-1.png b/images/0-1.png new file mode 100644 index 0000000..1665b99 Binary files /dev/null and b/images/0-1.png differ diff --git a/images/0-2.jpg b/images/0-2.jpg deleted file mode 100644 index 159a082..0000000 Binary files a/images/0-2.jpg and /dev/null differ diff --git a/images/0-2.png b/images/0-2.png new file mode 100644 index 0000000..4540be4 Binary files /dev/null and b/images/0-2.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..75aa7ce --- /dev/null +++ b/index.html @@ -0,0 +1,76 @@ + + + + + Go 命令教程 + + + + + + + + + + +
+ + + + + + + + + + + + + + + +