Go语言

15. Go语言命令行工具

Go语言的工具箱提供了很多常用工具。它可以被看作是特殊包管理器,用于包的查询、计算包的依赖关系、从远程版本控制系统下载包等任务。它也是一个构建系统,计算文件的依赖关系,然后调用编译器、汇编器和连接器构建程序。它还是一个单元测试、基准测试和性能测试的驱动程序,可让我们非常方便地进行各类代码测试,快速查找各类问题。

go help

The commands are:
	build 编译打包依赖
	clean 清理链接和缓存文件
	doc 展示包的文档
 	env 打印Go环境变量信息
 	bug 启动Go语言调试
 	fix 更新文档语法
 	fmt 格式化文档结构
 	generate 通过扫描Go源码中的特殊注释来识别要运行的常规命令
 	get 下载安装包和依赖项
 	install 编译安装包和依赖项
	list 查询包
 	run 编译运行Go程序
 	test 测试包
 	tool 运行Go的其他工具
 	version 打印版本号
 	vet 静态错误检查工具
...

15.1 编译相关指令

相较于Java和C++的编译速度,Go语言的快速编译是一个主要的效率优势。

15.1.1 build

go build指令用于编译我们的源码文件、代码包以及它们的依赖包。

在这里插入图片描述

在这里插入图片描述

C:\Users\admin\go\src\main>go build -o main.exe lib.go main.go
C:\Users\admin\go\src\main>go build main

Go语言有个不足的地方是,简单的几行代码使用go build命令编译出来大小会达到2~3 MB。

go build命令后面加上编译参数-ldflags "-s -w"就可以减小体积。其中-s的作用是去掉符号信息,在程序panic时的stack trace就没有任何文件名/行号信息了;-w是去掉DWARF调试信息,而后得到的程序就不能用gdb调试了。

也可以使用第三方工具对Go程序进行压缩处理 ,如UPX压缩。

go build还支持如下的附加参数。

-v 编译时显示包名
-a 强制重新构建
-n 打印输出编译时所用到的命令,但不真正执行
-x 打印输出编译时所用到的命令
-race 开启竞态检测

Go语言的源码文件分为三大类:命令源码文件、库源码文件和测试源码文件。命令源码文件总是作为可执行的程序的入口,库源码文件一般用于集中放置各种待使用的程序实体,而测试源码文件主要用于对前两种源码文件中的程序实体的功能和性能进行测试。

15.1.2 run

我们在调试代码时通常会使用go run命令。该命令会编译执行Go语言源码,不会在当前目录生成可执行文件,而是生成在临时目录下。

go run的对象只能是单个或多个.go文件(必须同属于main包),且不能为测试文件。

// 编译运行file1.go文件
go run file1.go
// 编译运行file1.go和file2.go文件
go run file1.go file2.go
// 编译运行当前目录下的所有文件
go run *.go

无法针对包运行go run指令,只能使用go build编译整个包,再运行编译后的可执行文件。如果go run指定源文件中引用了其他文件的方法或定义的变量,编译器会抛出undefined的错误。

package main

import (
   "fmt"
   "os"
)

func main() {
   fmt.Println("os.Args", os.Args)
}

在这里插入图片描述

可执行文件编译生成到C:\Users\admin\AppData\Local\Temp目录中。

使用go run命令时,还可以使用-work参数来显示当前的编译目录。

go run这个命令具体干了些什么事情呢?可以使用-n参数输出编译过程时所用到的命令。

→ 创建了两个临时文件夹:b001和exe。编译器先是执行了compile命令,然后是link,生成了归档文件.a和最终可执行文件,最终的可执行文件放在exe文件夹里面。命令的最后一步就是执行了可执行文件。

15.1.3 install

go install命令的作用是编译后安装,该命令依赖于GOPATH,因此不能在独立的目录中使用该命令。

与go build不同的是,go install是将编译的中间件放到pkg目录下,将编译出来的可执行文件放到bin目录下。

在这里插入图片描述

go install可以使用大部分go build的附加参数,由于go install的可执行文件的输出目录始终是GOPATH下的bin目录,因此不能使用-o参数指定输出的可执行文件名。go install依旧可以使用-n参数来查看运行过程中该命令到底做了哪些事情。

15.1.4 交叉编译

交叉编译就是在一个平台上生成另一个平台上的可执行代码。同一个体系结构可以运行不同的操作系统。

Go语言的交叉编译非常方便,只需在编译前设置一下Go的环境变量CGO_ENABLED、GOOS和GOARCH即可。

其中GOOS为目标操作系统,GOARCH是目标操作系统的架构,CGO_ENABLED表示是否使用C语言版本的GO编译器,参数配置为0的时候就关闭C语言版本的编译器了。

自从Golang 1.5以后,Go语言进行了自举,也就是Go语言编译器使用Go语言编写,而不再使用C语言。

  1. Windows下编译Linux

    在这里插入图片描述

  2. Mac下编译Linux、Windows

在这里插入图片描述

  1. Linux下编译Mac、Windows

在这里插入图片描述

各系统版本与对应的GOOS和GOARCH(系统架构)参数列表

系统GOOSGOARCH
Windows 32 位windows386
Windows 64 位windowsamd64
OS X 32 位darwin386
OS X 64 位darwinamd64
Linux 32 位linux386
Linux 64 位linuxamd64

15.2 代码获取 get

go get命令用于从远程仓库中下载安装远程代码包,这是我们常用且非常重要的指令。

go get github.com/gin-gonic/gin

首先是网站路径(如github.com),其次是包的创建者,最后是包名。

go get命令会把当前的代码包下载到GOPATH中的第一个工作区的src目录并安装。

go install支持在下载过程中加入-d参数,那么程序就只会下载源码而不安装它。还有一个比较重要的参数-u,通过它就可以强制使用网络去更新包和它的依赖包。如果不使用这个参数,对存在的包运行go get命令后会什么都不执行;加了这个参数,go get就会使用git pull命令拉取最新版的代码并安装。

对于go get命令,我们仍然可以使用-n参数来查看运行过程所使用到的命令,可以发现go get其实就是使用git clone下载源码,之后再对源码进行编译安装。

我们在使用go get下载安装第三方包时,有时会因为网络问题导致下载失败,这时可以给git添加代理,以加速下载。

http代理:
git config --global https.proxy 'http://127.0.0.1:1080'
git config --global https.proxy 'https://127.0.0.1:1080'
socks5代理:
git config --global http.proxy 'socks5://127.0.0.1:1080'
git config --global https.proxy 'socks5://127.0.0.1:1080'
取消代理:
git config --global --unset http.proxy
git config --global --unset https.proxy

127.0.0.1:1080是本机的代理IP和端口

15.3 格式化代码 fmt

Go语言官方为了统一代码风格,推出了gofmt工具来帮助开发者使用相同的规范格式化他们的代码。gofmt是一个cli程序,而go fmt命令是对gofmt命令进行简单的封装,它们能够格式化所有Go语言源码文件,即包括命令源码文件、库源码文件和测试源码文件。

gofmt会优先读取标准输入,如果传入了文件路径,会格式化这个文件;如果传入一个目录,会格式化目录中的所有.go文件;如果不传参数,会格式化当前目录下的所有.go文件。

gofmt命令需要使用参数-w,否则格式化结果不会写入文件。gofmt的对象可以是目录,如gofmt -w src/可以格式化整个项目。gofmt默认不对代码进行简化,使用-s参数可以开启简化代码功能。 go fmt命令是对gofmt命令进行简单的封装,在调用gofmt时添加了-l -w参数,相当于执行了gofmt -l -w。go fmt支持的参数只有-n和-x,如果需要更多的功能,如清除注释、简化代码、自定义缩进等,则需要使用gofmt命令。

一般很少使用go fmt命令,因为开发工具在保存时有自动格式化代码的功能,底层也是调用了go fmt命令格式化代码。

15.4 注释文档 doc

Go语言文档工具go doc和go fmt一样,也是对godoc的简单封装。通常使用go doc查看指定包的文档。

C:\Users\admin>go doc fmt.Println

gofmt命令有一个非常重要的参数-http,这个参数的作用是开启Web服务,提供交互式的文档查看页面。

godoc-http :8080

可通过网页的方式查看Go语言文档。

15.5 代码测试 test

go test命令用于对Go语言编写的代码包进行测试。可以指定要测试的文件,也可以直接对整个包进行测试,但需要包中含有测试文件,测试文件都是以“_test.go”结尾。

代码测试的目的是确保代码按照你的设想执行,编写测试代码可以尽早地发现程序所存在的问题。

15.5.1 单元测试

在Go语言中可以使用测试框架test编写单元测试,使用命令行即可对代码进行测试及输出测试报告。

编写测试代码就需要导入测试框架testing。单元测试的函数名使用“Test”为前缀,后面为要测试的函数名,并且需要传入*testing.T类型的参数。

func TestAdd(t *testing.T) {
    // 测试代码
}
15.5.2 基准测试

基准测试提供可自定义的计时器和一套基准测试算法,能方便快速地分析一段代码可能存在的CPU性能和内存分配问题。类似于单元测试,使用者无须准备高精度的计时器和各种分析工具,基准测试本身即可打印出非常标准的测试报告。

基准测试的使用也有一套书写规范,使用“Benchmark”为前缀,后面加上需要测试的函数名,并使用*testing.B作为函数参数。

func BenchmarkFunc(b *testing.B) {
    b.ResetTimer()
 	for idx := 0; idx < b.N; idx++ {
 	// 要测试的代码
 	}
 	b.StopTimer()
}
15.5.3 覆盖率测试

覆盖率测试用于统计通过运行程序包的测试有多少代码得到了执行。如果执行测试代码有70%的语句得到了运行,则测试覆盖率为70%。使用-cover参数就可以对源码文件进行覆盖率测试。

go test -cover xxx.go

15.6 性能分析

性能分析和调优是一种强大的技术,用于验证是否满足实际性能要求。Go语言支持使用go tool pprof工具进行性能查看及调优。性能分析工具可以将程序的CPU耗用、内存分配、竞态等问题以图形化方式展现出来。

15.6.1 安装Graphviz

http://www.graphviz.org/download/

在这里插入图片描述

15.6.2 通过文件方式

为了能分析Go程序的性能,我们需要在程序中导入runtime/pprof来生成性能分析所需要的profile文件。pprof包提供了StartCPUProfile(w io.Writer)接口来输出CPU性能参数到文件中。

对于内存堆栈可以使用pprof包的WriteHeapProfile(w io.Writer)接口,用法和StartCPUProfile类似。

15.6.3 通过HTTP方式

通过文件的方式可以对大部分cli程序进行性能分析,但如果是Web程序,上述方法就有点捉襟见肘了。Go语言提供了另一个针对Web程序的实时性能分析方式,只需要使用匿名导的方式引入如下包并启动HTTP服务器即可。

package main

import (
   "fmt"
   "log"
   "net/http"
)

const Num int = 10000

func concat_str(writer http.ResponseWriter, request *http.Request) {
   var str string
   for i := 0; i < Num; i++ {
      str = fmt.Sprintf("%s%d", str, i)
   }
}

func main() {
   http.HandleFunc("/str", concat_str)
   log.Fatal(http.ListenAndServe(":8080", nil))
}

浏览器只要访问http:// 127.0.0.1:8080/str,就会执行concat_str函数。

由于导入了net/http/pprof包,在浏览器访问如下地址,就可以看到相关的性能参数。

http://127.0.0.1:8080/debug/pprof/

使用如下命令对CPU性能进行分析:

go tool pprof http://127.0.0.1:8080/debug/pprof/profile?seconds=5