清理文件

This commit is contained in:
chai2010
2022-08-04 15:15:13 +08:00
parent 06a1bdf735
commit 3a20d238d9
83 changed files with 25 additions and 63507 deletions

View File

@@ -1,50 +0,0 @@
### 10.7.1. 工作区结构
对于大多数的Go语言用户只需要配置一个名叫GOPATH的环境变量用来指定当前工作目录即可。当需要切换到不同工作区的时候只要更新GOPATH就可以了。例如我们在编写本书时将GOPATH设置为`$HOME/gobook`
```
$ export GOPATH=$HOME/gobook
$ go get gopl.io/...
```
当你用前面介绍的命令下载本书全部的例子源码之后,你的当前工作区的目录结构应该是这样的:
```
GOPATH/
src/
gopl.io/
.git/
ch1/
helloworld/
main.go
dup/
main.go
...
golang.org/x/net/
.git/
html/
parse.go
node.go
...
bin/
helloworld
dup
pkg/
darwin_amd64/
...
```
GOPATH对应的工作区目录有三个子目录。其中src子目录用于存储源代码。每个包被保存在与$GOPATH/src的相对路径为包导入路径的子目录中例如gopl.io/ch1/helloworld相对应的路径目录。我们看到一个GOPATH工作区的src目录中可能有多个独立的版本控制系统例如gopl.io和golang.org分别对应不同的Git仓库。其中pkg子目录用于保存编译后的包的目标文件bin子目录用于保存编译后的可执行程序例如helloworld可执行程序。
第二个环境变量GOROOT用来指定Go的安装目录还有它自带的标准库包的位置。GOROOT的目录结构和GOPATH类似因此存放fmt包的源代码对应目录应该为$GOROOT/src/fmt。用户一般不需要设置GOROOT默认情况下Go语言安装工具会将其设置为安装的目录路径。
其中`go env`命令用于查看Go语言工具涉及的所有环境变量的值包括未设置环境变量的默认值。GOOS环境变量用于指定目标操作系统例如android、linux、darwin或windowsGOARCH环境变量用于指定处理器的类型例如amd64、386或arm等。虽然GOPATH环境变量是唯一必须要设置的但是其它环境变量也会偶尔用到。
```
$ go env
GOPATH="/home/gopher/gobook"
GOROOT="/usr/local/go"
GOARCH="amd64"
GOOS="darwin"
...
```

View File

@@ -1,42 +0,0 @@
### 10.7.2. 下载包
使用Go语言工具箱的go命令不仅可以根据包导入路径找到本地工作区的包甚至可以从互联网上找到和更新包。
使用命令`go get`可以下载一个单一的包或者用`...`下载整个子目录里面的每个包。Go语言工具箱的go命令同时计算并下载所依赖的每个包这也是前一个例子中golang.org/x/net/html自动出现在本地工作区目录的原因。
一旦`go get`命令下载了包然后就是安装包或包对应的可执行的程序。我们将在下一节再关注它的细节现在只是展示整个下载过程是如何的简单。第一个命令是获取golint工具它用于检测Go源代码的编程风格是否有问题。第二个命令是用golint命令对2.6.2节的gopl.io/ch2/popcount包代码进行编码风格检查。它友好地报告了忘记了包的文档
```
$ go get github.com/golang/lint/golint
$ $GOPATH/bin/golint gopl.io/ch2/popcount
src/gopl.io/ch2/popcount/main.go:1:1:
package comment should be of the form "Package popcount ..."
```
`go get`命令支持当前流行的托管网站GitHub、Bitbucket和Launchpad可以直接向它们的版本控制系统请求代码。对于其它的网站你可能需要指定版本控制系统的具体路径和协议例如 Git或Mercurial。运行`go help importpath`获取相关的信息。
`go get`命令获取的代码是真实的本地存储仓库而不仅仅只是复制源文件因此你依然可以使用版本管理工具比较本地代码的变更或者切换到其它的版本。例如golang.org/x/net包目录对应一个Git仓库
```
$ cd $GOPATH/src/golang.org/x/net
$ git remote -v
origin https://go.googlesource.com/net (fetch)
origin https://go.googlesource.com/net (push)
```
需要注意的是导入路径含有的网站域名和本地Git仓库对应远程服务地址并不相同真实的Git地址是go.googlesource.com。这其实是Go语言工具的一个特性可以让包用一个自定义的导入路径但是真实的代码却是由更通用的服务提供例如googlesource.com或github.com。因为页面 https://golang.org/x/net/html 包含了如下的元数据它告诉Go语言的工具当前包真实的Git仓库托管地址
```
$ go build gopl.io/ch1/fetch
$ ./fetch https://golang.org/x/net/html | grep go-import
<meta name="go-import"
content="golang.org/x/net git https://go.googlesource.com/net">
```
如果指定`-u`命令行标志参数,`go get`命令将确保所有的包和依赖的包的版本都是最新的,然后重新编译和安装它们。如果不包含该标志参数的话,而且如果包已经在本地存在,那么代码将不会被自动更新。
`go get -u`命令只是简单地保证每个包是最新版本如果是第一次下载包则是比较方便的但是对于发布程序则可能是不合适的因为本地程序可能需要对依赖的包做精确的版本依赖管理。通常的解决方案是使用vendor的目录用于存储依赖包的固定版本的源代码对本地依赖的包的版本更新也是谨慎和持续可控的。在Go1.5之前一般需要修改包的导入路径所以复制后golang.org/x/net/html导入路径可能会变为gopl.io/vendor/golang.org/x/net/html。最新的Go语言命令已经支持vendor特性但限于篇幅这里并不讨论vendor的具体细节。不过可以通过`go help gopath`命令查看Vendor的帮助文档。
(译注墙内用户在上面这些命令的基础上还需要学习用翻墙来go get。)
**练习 10.3:** 从 http://gopl.io/ch1/helloworld?go-get=1 获取内容,查看本书的代码的真实托管的网址(`go get`请求HTML页面时包含了`go-get`参数,以区别普通的浏览器请求)。

View File

@@ -1,107 +0,0 @@
### 10.7.3. 构建包
`go build`命令编译命令行参数指定的每个包。如果包是一个库则忽略输出结果这可以用于检测包是可以正确编译的。如果包的名字是main`go build`将调用链接器在当前目录创建一个可执行程序;以导入路径的最后一段作为可执行程序的名字。
由于每个目录只包含一个包因此每个对应可执行程序或者叫Unix术语中的命令的包会要求放到一个独立的目录中。这些目录有时候会放在名叫cmd目录的子目录下面例如用于提供Go文档服务的golang.org/x/tools/cmd/godoc命令就是放在cmd子目录§10.7.4)。
每个包可以由它们的导入路径指定,就像前面看到的那样,或者用一个相对目录的路径名指定,相对路径必须以`.``..`开头。如果没有指定参数,那么默认指定为当前目录对应的包。下面的命令用于构建同一个包,虽然它们的写法各不相同:
```
$ cd $GOPATH/src/gopl.io/ch1/helloworld
$ go build
```
或者:
```
$ cd anywhere
$ go build gopl.io/ch1/helloworld
```
或者:
```
$ cd $GOPATH
$ go build ./src/gopl.io/ch1/helloworld
```
但不能这样:
```
$ cd $GOPATH
$ go build src/gopl.io/ch1/helloworld
Error: cannot find package "src/gopl.io/ch1/helloworld".
```
也可以指定包的源文件列表这一般只用于构建一些小程序或做一些临时性的实验。如果是main包将会以第一个Go源文件的基础文件名作为最终的可执行程序的名字。
```
$ cat quoteargs.go
package main
import (
"fmt"
"os"
)
func main() {
fmt.Printf("%q\n", os.Args[1:])
}
$ go build quoteargs.go
$ ./quoteargs one "two three" four\ five
["one" "two three" "four five"]
```
特别是对于这类一次性运行的程序,我们希望尽快的构建并运行它。`go run`命令实际上是结合了构建和运行的两个步骤:
```
$ go run quoteargs.go one "two three" four\ five
["one" "two three" "four five"]
```
(译注其实也可以偷懒直接go run `*.go`)
第一行的参数列表中,第一个不是以`.go`结尾的将作为可执行程序的参数运行。
默认情况下,`go build`命令构建指定的包和它依赖的包,然后丢弃除了最后的可执行文件之外所有的中间编译结果。依赖分析和编译过程虽然都是很快的,但是随着项目增加到几十个包和成千上万行代码,依赖关系分析和编译时间的消耗将变的可观,有时候可能需要几秒种,即使这些依赖项没有改变。
`go install`命令和`go build`命令很相似,但是它会保存每个包的编译成果,而不是将它们都丢弃。被编译的包会被保存到$GOPATH/pkg目录下目录路径和 src目录路径对应可执行程序被保存到$GOPATH/bin目录。很多用户会将$GOPATH/bin添加到可执行程序的搜索列表中。还有`go install`命令和`go build`命令都不会重新编译没有发生变化的包,这可以使后续构建更快捷。为了方便编译依赖的包,`go build -i`命令将安装每个目标所依赖的包。
因为编译对应不同的操作系统平台和CPU架构`go install`命令会将编译结果安装到GOOS和GOARCH对应的目录。例如在Mac系统golang.org/x/net/html包将被安装到$GOPATH/pkg/darwin_amd64目录下的golang.org/x/net/html.a文件。
针对不同操作系统或CPU的交叉构建也是很简单的。只需要设置好目标对应的GOOS和GOARCH然后运行构建命令即可。下面交叉编译的程序将输出它在编译时的操作系统和CPU类型
<u><i>gopl.io/ch10/cross</i></u>
```Go
func main() {
fmt.Println(runtime.GOOS, runtime.GOARCH)
}
```
下面以64位和32位环境分别编译和执行
```
$ go build gopl.io/ch10/cross
$ ./cross
darwin amd64
$ GOARCH=386 go build gopl.io/ch10/cross
$ ./cross
darwin 386
```
有些包可能需要针对不同平台和处理器类型使用不同版本的代码文件以便于处理底层的可移植性问题或为一些特定代码提供优化。如果一个文件名包含了一个操作系统或处理器类型名字例如net_linux.go或asm_amd64.sGo语言的构建工具将只在对应的平台编译这些文件。还有一个特别的构建注释参数可以提供更多的构建过程控制。例如文件中可能包含下面的注释
```Go
// +build linux darwin
```
在包声明和包注释的前面,该构建注释参数告诉`go build`只在编译程序对应的目标操作系统是Linux或Mac OS X时才编译这个文件。下面的构建注释则表示不编译这个文件
```Go
// +build ignore
```
更多细节可以参考go/build包的构建约束部分的文档。
```
$ go doc go/build
```

View File

@@ -1,76 +0,0 @@
### 10.7.4. 包文档
Go语言的编码风格鼓励为每个包提供良好的文档。包中每个导出的成员和包声明前都应该包含目的和用法说明的注释。
Go语言中的文档注释一般是完整的句子第一行通常是摘要说明以被注释者的名字开头。注释中函数的参数或其它的标识符并不需要额外的引号或其它标记注明。例如下面是fmt.Fprintf的文档注释。
```Go
// Fprintf formats according to a format specifier and writes to w.
// It returns the number of bytes written and any write error encountered.
func Fprintf(w io.Writer, format string, a ...interface{}) (int, error)
```
Fprintf函数格式化的细节在fmt包文档中描述。如果注释后紧跟着包声明语句那注释对应整个包的文档。包文档对应的注释只能有一个译注其实可以有多个它们会组合成一个包文档注释包注释可以出现在任何一个源文件中。如果包的注释内容比较长一般会放到一个独立的源文件中fmt包注释就有300行之多。这个专门用于保存包文档的源文件通常叫doc.go。
好的文档并不需要面面俱到文档本身应该是简洁但不可忽略的。事实上Go语言的风格更喜欢简洁的文档并且文档也是需要像代码一样维护的。对于一组声明语句可以用一个精炼的句子描述如果是显而易见的功能则并不需要注释。
在本书中,只要空间允许,我们之前很多包声明都包含了注释文档,但你可以从标准库中发现很多更好的例子。有两个工具可以帮到你。
首先是`go doc`命令,该命令打印其后所指定的实体的声明与文档注释,该实体可能是一个包:
```
$ go doc time
package time // import "time"
Package time provides functionality for measuring and displaying time.
const Nanosecond Duration = 1 ...
func After(d Duration) <-chan Time
func Sleep(d Duration)
func Since(t Time) Duration
func Now() Time
type Duration int64
type Time struct { ... }
...many more...
```
或者是某个具体的包成员:
```
$ go doc time.Since
func Since(t Time) Duration
Since returns the time elapsed since t.
It is shorthand for time.Now().Sub(t).
```
或者是一个方法:
```
$ go doc time.Duration.Seconds
func (d Duration) Seconds() float64
Seconds returns the duration as a floating-point number of seconds.
```
该命令并不需要输入完整的包导入路径或正确的大小写。下面的命令将打印encoding/json包的`(*json.Decoder).Decode`方法的文档:
```
$ go doc json.decode
func (dec *Decoder) Decode(v interface{}) error
Decode reads the next JSON-encoded value from its input and stores
it in the value pointed to by v.
```
第二个工具名字也叫godoc它提供可以相互交叉引用的HTML页面但是包含和`go doc`命令相同以及更多的信息。图10.1演示了time包的文档11.6节将看到godoc演示可以交互的示例程序。godoc的在线服务 https://godoc.org ,包含了成千上万的开源包的检索工具。
![](../images/ch10-01.png)
你也可以在自己的工作区目录运行godoc服务。运行下面的命令然后在浏览器查看 http://localhost:8000/pkg 页面:
```
$ godoc -http :8000
```
其中`-analysis=type``-analysis=pointer`命令行标志参数用于打开文档和代码中关于静态分析的结果。

View File

@@ -1,14 +0,0 @@
### 10.7.5. 内部包
在Go语言程序中包是最重要的封装机制。没有导出的标识符只在同一个包内部可以访问而导出的标识符则是面向全宇宙都是可见的。
有时候,一个中间的状态可能也是有用的,标识符对于一小部分信任的包是可见的,但并不是对所有调用者都可见。例如,当我们计划将一个大的包拆分为很多小的更容易维护的子包,但是我们并不想将内部的子包结构也完全暴露出去。同时,我们可能还希望在内部子包之间共享一些通用的处理包,或者我们只是想实验一个新包的还并不稳定的接口,暂时只暴露给一些受限制的用户使用。
为了满足这些需求Go语言的构建工具对包含internal名字的路径段的包导入路径做了特殊处理。这种包叫internal包一个internal包只能被和internal目录有同一个父目录的包所导入。例如net/http/internal/chunked内部包只能被net/http/httputil或net/http包导入但是不能被net/url包导入。不过net/url包却可以导入net/http/httputil包。
```
net/http
net/http/internal/chunked
net/http/httputil
net/url
```

View File

@@ -1,112 +0,0 @@
### 10.7.6. 查询包
`go list`命令可以查询可用包的信息。其最简单的形式,可以测试包是否在工作区并打印它的导入路径:
```
$ go list github.com/go-sql-driver/mysql
github.com/go-sql-driver/mysql
```
`go list`命令的参数还可以用`"..."`表示匹配任意的包的导入路径。我们可以用它来列出工作区中的所有包:
```
$ go list ...
archive/tar
archive/zip
bufio
bytes
cmd/addr2line
cmd/api
...many more...
```
或者是特定子目录下的所有包:
```
$ go list gopl.io/ch3/...
gopl.io/ch3/basename1
gopl.io/ch3/basename2
gopl.io/ch3/comma
gopl.io/ch3/mandelbrot
gopl.io/ch3/netflag
gopl.io/ch3/printints
gopl.io/ch3/surface
```
或者是和某个主题相关的所有包:
```
$ go list ...xml...
encoding/xml
gopl.io/ch7/xmlselect
```
`go list`命令还可以获取每个包完整的元信息,而不仅仅只是导入路径,这些元信息可以以不同格式提供给用户。其中`-json`命令行参数表示用JSON格式打印每个包的元信息。
```
$ go list -json hash
{
"Dir": "/home/gopher/go/src/hash",
"ImportPath": "hash",
"Name": "hash",
"Doc": "Package hash provides interfaces for hash functions.",
"Target": "/home/gopher/go/pkg/darwin_amd64/hash.a",
"Goroot": true,
"Standard": true,
"Root": "/home/gopher/go",
"GoFiles": [
"hash.go"
],
"Imports": [
"io"
],
"Deps": [
"errors",
"io",
"runtime",
"sync",
"sync/atomic",
"unsafe"
]
}
```
命令行参数`-f`则允许用户使用text/template包§4.6的模板语言定义输出文本的格式。下面的命令将打印strconv包的依赖的包然后用join模板函数将结果链接为一行连接时每个结果之间用一个空格分隔
```
$ go list -f '{{join .Deps " "}}' strconv
errors math runtime unicode/utf8 unsafe
```
译注上面的命令在Windows的命令行运行会遇到`template: main:1: unclosed action`的错误。产生这个错误的原因是因为命令行对命令中的`" "`参数进行了转义处理。可以按照下面的方法解决转义字符串的问题:
```
$ go list -f "{{join .Deps \" \"}}" strconv
```
下面的命令打印compress子目录下所有包的导入包列表
```
$ go list -f '{{.ImportPath}} -> {{join .Imports " "}}' compress/...
compress/bzip2 -> bufio io sort
compress/flate -> bufio fmt io math sort strconv
compress/gzip -> bufio compress/flate errors fmt hash hash/crc32 io time
compress/lzw -> bufio errors fmt io
compress/zlib -> bufio compress/flate errors fmt hash hash/adler32 io
```
{% endraw %}
译注Windows下有同样有问题要避免转义字符串的干扰
```
$ go list -f "{{.ImportPath}} -> {{join .Imports \" \"}}" compress/...
```
{% endraw %}
`go list`命令对于一次性的交互式查询或自动化构建或测试脚本都很有帮助。我们将在11.2.4节中再次使用它。每个子命令的更多信息,包括可设置的字段和意义,可以用`go help list`命令查看。
在本章我们解释了Go语言工具中除了测试命令之外的所有重要的子命令。在下一章我们将看到如何用`go test`命令去运行Go语言程序中的测试代码。
**练习 10.4** 创建一个工具,根据命令行指定的参数,报告工作区所有依赖包指定的其它包集合。提示:你需要运行`go list`命令两次一次用于初始化包一次用于所有包。你可能需要用encoding/json§4.5包来分析输出的JSON格式的信息。

View File

@@ -402,7 +402,7 @@ $ go list -json hash
$ go list -f '{{join .Deps " "}}' strconv
errors math runtime unicode/utf8 unsafe
```
{% endraw %}
译注上面的命令在Windows的命令行运行会遇到`template: main:1: unclosed action`的错误。产生这个错误的原因是因为命令行对命令中的`" "`参数进行了转义处理。可以按照下面的方法解决转义字符串的问题:
@@ -410,7 +410,7 @@ errors math runtime unicode/utf8 unsafe
```
$ go list -f "{{join .Deps \" \"}}" strconv
```
{% endraw %}
下面的命令打印compress子目录下所有包的导入包列表
@@ -423,7 +423,7 @@ compress/gzip -> bufio compress/flate errors fmt hash hash/crc32 io time
compress/lzw -> bufio errors fmt io
compress/zlib -> bufio compress/flate errors fmt hash hash/adler32 io
```
{% endraw %}
译注Windows下有同样有问题要避免转义字符串的干扰
@@ -431,7 +431,7 @@ compress/zlib -> bufio compress/flate errors fmt hash hash/adler32 io
```
$ go list -f "{{.ImportPath}} -> {{join .Imports \" \"}}" compress/...
```
{% endraw %}
`go list`命令对于一次性的交互式查询或自动化构建或测试脚本都很有帮助。我们将在11.2.4节中再次使用它。每个子命令的更多信息,包括可设置的字段和意义,可以用`go help list`命令查看。