第10章 包和工具

现在随便一个小程序可能就包含10000个函数,但是我们不可能一个个去构建,大部分还是来自于他人,这些函数通过类似包和模块的方式被重用

go语言的包超过100个,可以在终端中使用go list std |wc -l去查看,开源包可以通过http://godoc.org来检索

go带了一个工具包里面有各种简化工作区和包管理的小工具

10.7 工具

现在我们再来看看go语言工具箱中的具体功能,包括如何下载、格式化、构建、测试和安装Go语言编写的程序

Go语言的工具箱集合了一系列功能的命令集,它是一个包管理器,可以查询包、计算包的依赖关系、从远程版本控制系统下载包;它也是一个构建系统,计算文件的依赖关系,然后调用编译器、汇编器和连接器构建程序;另外它也是一个单元测试和基准测试的驱动程序,我们将在下一章讨论这个问题

我们运行一下go命令,看看工具

 % go
Go is a tool for managing Go source code.Usage:go <command> [arguments]The commands are:bug         start a bug reportbuild       compile packages and dependenciesclean       remove object files and cached filesdoc         show documentation for package or symbolenv         print Go environment informationfix         update packages to use new APIsfmt         gofmt (reformat) package sourcesgenerate    generate Go files by processing sourceget         add dependencies to current module and install theminstall     compile and install packages and dependencieslist        list packages or modulesmod         module maintenancerun         compile and run Go programtest        test packagestool        run specified go toolversion     print Go versionvet         report likely mistakes in packagesUse "go help <command>" for more information about a command.Additional help topics:buildconstraint build constraintsbuildmode       build modesc               calling between Go and Ccache           build and test cachingenvironment     environment variablesfiletype        file typesgo.mod          the go.mod filegopath          GOPATH environment variablegopath-get      legacy GOPATH go getgoproxy         module proxy protocolimportpath      import path syntaxmodules         modules, module versions, and moremodule-get      module-aware go getmodule-auth     module authentication using go.sumpackages        package lists and patternsprivate         configuration for downloading non-public codetestflag        testing flagstestfunc        testing functionsvcs             controlling version control with GOVCSUse "go help <topic>" for more information about that topic

为了达到零配置的设计目标,Go语言的工具箱很多地方都依赖各种约定。例如,根据给定的源文件名称,Go语言的工具可以找到源文件对应的包,因为每个目录只包含了单一的包,并且包的导入路径和工作区的目录结构是对应的,给定一个包的导入路径,Go语言的工具可以找到与之对应的储存着实体文件的目录。它还可以跟俊导入路径找到存储代码仓库的远程服务器URL

10.7.1 工作区结构

对于大多数的Go用户来说只需要配置GOPATH的环境 变量就可指定当前的工作目录

$ export GOPATH=$HOME/gobook
$ go get gopl.io/...

按照上述逻辑下载源码应该是如下的目录结构

GOPATH/src/gopl.io/.git/ch1/helloworld/main.godup/main.go...golang.org/x/net/.git/html/parse.gonode.go...bin/helloworldduppkg/darwin_amd64/...

GOPATH对应的工作区域有三个子目录:src/bin/pkg。src存放源代码,bin存放编译后可执行的程序,pkg用于保存编译后的包的目标文件

第二个环境变量GOROOT用来指定Go的安装目录,还有它自带的标准库包的位置

其中go env命令用于查看go语言工具涉及所有环境变量的值,包括未设置环境变量的默认值

$ go env
GOPATH="/home/gopher/gobook"
GOROOT="/usr/local/go"
GOARCH="amd64"
GOOS="darwin"
...

10.7.2 下载包

使用Go语言工具箱的Go命令,不仅可以根据包导入路径找到本地工作区的包,甚至可以从互联网上找到并且更新包

使用 go get 可以下载一个单一的包或者用…下载整个子目录里面的每个包,go命令会同时计算并下载所有依赖的包

一旦包被下载,接着就是安装包或包对应的可执行程序

下载过程:第一个命令是获取golint工具,它用于检测Go源代码的编程风格是否有问题,第二个命令是用golint命令对包代码进行编码风格检查,它友好的报告了忘记了包的文档

$ 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仓库对应远程服务地址并不相同,真实的地址是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命令将确保包和依赖的包都是最新版本,如果指定,存在于本地的包则不会更新到最新版本

但是对于发布程序,本地程序可能需要对依赖的包进行精细化的管理。一般解决方法是使用vendor的目录用于存储依赖包的固定版本的源代码,对本地依赖的包的版本更新也是谨慎和持续可控的

10.7.3 构建包

go build命令编译命令行参数指定的每一个包

如果包是一个库,则忽略输出结果,这可以检测包是否是正确编译。如果包的名字是main,go build将调用链接器在当前目录创建一个可执行程序;以导入路径的最后一段作为可执行程序的名字

由于每个目录只包含一个包,因此每个对应可执行程序或者叫Unix术语中的命令的包,会要求放到一个独立的目录中。这些目录有时候会放在名叫cmd目录的子目录下面,例如用于提供Go文档服务的golang.org/x/tools/cmd/godoc命令就是放在cmd子目录

每个包可以由它们的导入路径指定,就像前面提到的那样,或者用一个相对目录的路径名指定,相对路径必须以.或…开头,如果没有指定参数,那么默认指定为当前目录对应的包。下面的命令用于构建同一个包,虽然它们写法各不相同

$ 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 mainimport ("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 build用于构建指定的包和依赖的包,但会丢弃除可执行文件之外的所有中间编译结果

go install 则会保存中间编译结果

编译对应不同的操作系统平台和CPU架构,go install 命令会将编译结果安装到GOOS和GOARCH对应的目录

针对不同的操作系统或CPU交叉构建也很简单,只需设置好目标对应的GOOS和GOARCH,然后运行构建命令即可,下面交叉编译的程序将输出它在编译时的操作系统和CPU类型

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

更多的细节可以查看文档

go doc go/build

10.7.4 包文档

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)

当包的注释比较多则可以放到另一个文件中

使用go doc 包名/成员名/方法名可以查看,如下

go doc time
go doc time.Since
go doc time.Duration.Seconds

这个命令并不需要输入完整导入路径或者正确的大小写

go doc json.decode
package json // import "encoding/json"func (dec *Decoder) Decode(v interface{}) errorDecode reads the next JSON-encoded value from its input and stores it in thevalue pointed to by v.See the documentation for Unmarshal for details about the conversion of JSONinto a Go value.

第二个工具godoc 可以提供相互交叉引用的HTML页面,但是包含和go doc命令相似以及更多的信息。godoc的在线服务 https://godoc.org ,包含了成千上万的开源包的检索工具

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

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

Windows下有同样有问题,要避免转义字符串的干扰:

$ go list -f "{{.ImportPath}} -> {{join .Imports \" \"}}" compress/...

go list命令对于一次性的交互式查询或自动化构建或测试脚本都很有帮助。我们将在11.2.4节中再次使用它。每个子命令的更多信息,包括可设置的字段和意义,可以用go help list命令查看。

在本章,我们解释了Go语言工具中除了测试命令之外的所有重要的子命令。

在下一章,我们将看到如何用go test命令去运行Go语言程序中的测试代码

Go语言圣经 - 第10章 包和工具 - 10.7 工具相关推荐

  1. 《Go语言圣经》第一章 - 读书笔记

    <Go语言圣经>第一章 - 读书笔记 第一章 Go语言入门 01 Hello World 02 命令行参数 练习 练习1.1 练习1.2: 练习1.3: 03 查找重复的行 例子运行 du ...

  2. Go 语言圣经 第十章 包和工具

    /** 第十章 包和工具 Go语言开源包,它们可以通过 http://godoc.org 检索 */ 10.1 包简介 知识点 1.包系统设计的目的是为了简化大型程序的设计和维护工作 2.每个包一般都 ...

  3. Go语言圣经 - 第5章 函数 - 5.10 Recover捕获异常

    第5章 函数 函数可以让我们将一个语句序列打包成一个单元,然后可以从程序中其他地方多次调用,函数的机制可以让我们把一个大的工作分解成小任务.前面我们已经接触过函数,本章我们将讨论函数的更多特性 5.1 ...

  4. Go语言圣经 - 第3章 基础数据类型

    第3章 基础数据类型 Go语言将数据类型分为了四类:基础类型.符合类型.引用类型和接口类型.基础类型:数字.字符串和布尔型:复合数据类型:数组和结构体:引用类型:指针.切片.字典.函数.通道,虽然数据 ...

  5. Go语言圣经 - 第11章 测试 - 11.4 - 11.6

    第11章 测试 软件测试是一个巨大的领域,但是Go语言的测试技术是相对比较低级的,它依赖一个Go test测试命令和一组按照约定方式编写的测试函数,测试命令可以运行这些函数 在实践中,编写测试代码和编 ...

  6. Go语言圣经 - 第7章 接口 - 7.9 表达式求值

    第7章 接口 接口类型是对其它类型行为的抽象和概括.接口类型不会和特定的实现细节绑定在一起,这种抽象的方式能让我们的函数更加的灵活和更具有适应能力 Go语言的接口比较特殊,因为它是满足隐式实现的.也就 ...

  7. Go语言圣经 - 第11章 测试 - 11.1 go test 11.2 测试函数

    第11章 测试 软件测试是一个巨大的领域,但是Go语言的测试技术是相对比较低级的,它依赖一个Go test测试命令和一组按照约定方式编写的测试函数,测试命令可以运行这些函数 在实践中,编写测试代码和编 ...

  8. Go语言圣经 - 第1章 入门 - 1.3 1.4 查找重复的行 GIF

    第1章 入门 1.3 查找重复的行 在1.2节,我们简单的了解了一些Go语言的基本语法,接下来我们再来看一个例子进一步学习 对文件做拷贝.打印.搜索和排序.统计或类似事情的程序都有一个差不多的程序结构 ...

  9. Go语言圣经 - 第11章 测试 - 11.3 测试覆盖率

    第11章 测试 软件测试是一个巨大的领域,但是Go语言的测试技术是相对比较低级的,它依赖一个Go test测试命令和一组按照约定方式编写的测试函数,测试命令可以运行这些函数 在实践中,编写测试代码和编 ...

最新文章

  1. Django 验证码4.4
  2. php后台开发(二)Laravel框架
  3. 6.2 sql安全性
  4. 华为内部存储转sd卡_高调谈洗牌 2019年中国存储市场下半场的关键词是“低调”吗?...
  5. 11.19 rpm:RPM包管理器
  6. 获取所有某格式文件到文件
  7. 让程序间隔执行并可以停止
  8. [译] 搜索结果页的最佳实践
  9. 黑马vue实战项目-(三)权限管理功能开发
  10. 百战程序员数据结构 课件_结构之战
  11. Matlab遗传算法实例
  12. PackageManager的基本使用
  13. Faster-RCNN理论
  14. 部队计算机专业培训申请书,《计算机专业奖学金申请书》.docx
  15. Win10要是个人,也算是鬼门关走过一遭了
  16. 计算机专业报瑞士酒店管理,我适合去瑞士读酒店管理吗?
  17. Oracle table move tablespace
  18. Doc2Vec模型的介绍与gensim中Doc2Vec的使用
  19. DNS信息查询综合实验
  20. LintCode_138——子数组和为零

热门文章

  1. springboot大纲
  2. Cocos creator Graphics绘制线条却出现的是填充图案
  3. 现代电子计算机元件的发展从电子管,现代计算机技术的发展方向与趋势探索
  4. Python编程输出三角形的边长及面积
  5. esp8266串口 报错ets Jan 8 2013,rst cause:2, boot mode:(3,7)
  6. 赤脚小子 香港经典武侠片
  7. 极链科技联合阿里云发布视联网平台解决方案
  8. 数据工程师面试常见题目汇总
  9. MatLab矩阵的创建、寻访和运算
  10. 模拟退火算法系列之(二):一个实例