Go
本身包含来大量用于处理 Go
程序的命令和工具。
命令 | 作用 |
---|---|
build | 用于编译指定的代码包或 Go 语言源码文件。命令源码文件会被编译成可执行文件,并存放到命令执行的目录或指定目录下。而库源码文件被编译后,则不会在非临时目录中留下任何文件。 |
clean | 用于清除因执行其他 go 命令而遗留下来的临时目录和文件。 |
doc | 用于显示打印 Go 语言代码包以及程序实体的文档。 |
env | 用于打印 Go 语言相关的环境信息。 |
fix | 用于修正指定代码的源码文件中包含的过时语法和代码调用。这使得我们在升级 Go 语言版本时,可以非常方便地同步升级程序 |
fmt | 用于格式化指定代码包中的 Go 源码文件。实际上,它是通过执行 gofmt 命令来实现功能的。 |
generate | 用于识别指定代码中资源文件中的 “go:generate” 注释,并执行其携带的任意命令。该命令独立于 Go 语言标准的编译和安装体系。如果你有需要解析的 “go:generate” 注释,就单独运行它。这个命令非常有用,我常用它自动生成或改动 Go 源码文件 |
get | 用于下载,编译并安装指定改动代码包及其依赖包。从我们自己的代码中转站或第三方代码库上自动拉取代码,就全靠它了。 |
install | 用于编译并安装指定的代码包及其依赖包。安装命令源码文件后,代码包所在的工作区目录的 bin 子目录,或者当前环境变量 GOBIN 指向的目录中会生成相应的可执行文件。安装库源码文件后,会在代码包所在的工作目录的 pkg 子目录中生成相应的归档文件。 |
list | 用于显示指定代码包的信息,它可谓是代码包分析的一大便利工具。利用 Go 语言标准代码库代码包 “text/template” 中规定的模版语法,你可以非常灵活的控制输出信息。 |
run | 用于编译并运行指定的代码源码文件。当你想不生成可执行文件而直接运行命令源码文件时,就需要用到它。 |
test | 用于测试指定的代码包,前提是该代码包目录中必须存在测试源代码文件。 |
tool | 用于运行 Go 语言的特殊工具。 |
vet | 用于检查指定代码包中的 Go 语言代码,并报告发现可疑代码问题。该命令提供了除编译之外的又一个程序检查方法,可用于找到程序中的潜在错误。 |
version | 用于显示当前安装的 Go 语言的版本信息以及计算环境。 |
Go
语言中使用 go build
命令主要用于编译代码。在包的编译过程中,若有必要,会同时编译与之相关联的包。
go build
有很多种编译方法,如无参数编译、文件列表编译、指定包编译等,使用这些方法都可以输出可执行文件。
在进行 go build
时可以使用 export
命令指定当前 GOPATH
路径。
export GOPATH=/home/wohu/gocode
go build -o main main.go
./main
代码相对于 GOPATH
如下:
wohu@wohu:~/gocode/src$ tree -L 2
└── main
├── main.go
└── upload.go
upload.go
代码内容:
package main
import "fmt"
func upload() {
fmt.Println("This is upload function ")
}
main.go
代码内容:
package main
func main() {
upload()
}
wohu@wohu:~/gocode/src$
wohu@wohu:~/gocode/src$ cd main/
wohu@wohu:~/gocode/src/main$ go build
wohu@wohu:~/gocode/src/main$ ls
main main.go upload.go
wohu@wohu:~/gocode/src/main$ ./main # 执行 main 后的输出内容
This is upload function
go build
在编译开始时,会搜索当前目录的 go
源码。这个例子中, go build
会找到 upload.go
和 main.go
两个文件。编译这两个文件后,生成当前目录名的可执行文件并放置于当前目录下,这里的可执行文件是 main
。
编译同目录的多个源码文件时,可以在 go build
的后面提供多个文件名, go build
会编译这些源码,输出可执行文件,“go build+文件列表”的格式如下:
go build file1.go file2.go……
注意:使用“go build+文件列表”方式编译时,可执行文件默认选择文件列表中第一个源码文件作为可执行文件名输出。
wohu@wohu:~/gocode/src/main$ go build upload.go main.go
wohu@wohu:~/gocode/src/main$ ls
main.go upload upload.go
wohu@wohu:~/gocode/src/main$ ./upload
This is upload function
wohu@wohu:~/gocode/src/main$
wohu@wohu:~/gocode/src/main$ ls
main.go upload.go
wohu@wohu:~/gocode/src/main$ go build main.go upload.go
wohu@wohu:~/gocode/src/main$ ls
main main.go upload.go
wohu@wohu:~/gocode/src/main$ ./main
This is upload function
如果需要指定输出可执行文件名,可以使用-o
参数,参见下面的例子:
wohu@wohu:~/gocode/src/main$ ls
main.go upload.go
wohu@wohu:~/gocode/src/main$ go build -o test main.go upload.go
wohu@wohu:~/gocode/src/main$ ls
main.go test upload.go
wohu@wohu:~/gocode/src/main$ ./test
This is upload function
wohu@wohu:~/gocode/src/main$
使用“go build+文件列表”编译方式编译时,文件列表中的每个文件必须是同一个包的 Go 源码。
“go build+文件列表”方式更适合使用 Go
语言编写的只有少量文件的工具。
“go build+包”在设置 GOPATH
后,可以直接根据包名进行编译,即便包内文件被增(加)删(除)也不影响编译指令。
代码格式如下:
wohu@wohu:~/gocode/src$ tree -L 3
└── mypackage
├── main.go
└── upload
└── upload.go
main.go
代码:
package main
import "mypackage/upload"
func main() {
upload.Demo()
}
upload.go
代码:
package upload
import "fmt"
func Demo() {
fmt.Println("This is upload function ")
}
编译命令:
wohu@wohu:~/gocode/src$ export GOPATH=/home/wohu/gocode
wohu@wohu:~/gocode/src$ go build -o main mypackage/
wohu@wohu:~/gocode/src$ ls
github.com golang.org main mypackage
wohu@wohu:~/gocode/src$ ./main
This is upload function
wohu@wohu:~/gocode/src$
设置环境变量 GOPATH
为自己的目录,注意 GOPATH
下的目录结构,源码必须放在 GOPATH
下的 src
目录下。所有目录中不要包含中文。
每个包可以由它们的导入路径指定,就像前面看到的那样,或者用一个相对目录的路径名指定,相对路径必须以.
或..
开头。如果没有指定参数,那么默认指定为当前目录对应的包。下面的命令用于构建同一个包,虽然它们的写法各不相同:
$ 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".
-o
执行指定输出文件为 main
,后面接要编译的包名。包名是相对于 GOPATH
下的 src
目录开始的。
go build
还有一些附加参数,可以显示更多的编译信息和更多的操作,详见下表所示。
附加参数 | 备 注 |
---|---|
-v |
编译时显示包名 |
-p n |
开启并发编译,默认情况下该值为 CPU 逻辑核数 |
-a |
强制重新构建 |
-n |
打印编译时会用到的所有命令,但不真正执行 |
-x |
打印编译时会用到的所有命令 |
-race |
开启竞态检测,常用于并发模式下的共享变量检测 |
-o |
后接文件名,强制对输出的文件进行重命名 |
-work |
打印编译工作的临时目录 |
-gcflags |
后面的参数可以是多个,用空格进行分隔,并用 "" 进行包裹,这些参数将传递到 go tool compile 工具中进行调用。例如,go build -gcflags "-l -m" |
-ldflags |
后面的参数可以是多个,用空格进行分隔,并用 "" 进行包裹,这些参数将传递到 go tool link 工具中进行调用。例如,go build -ldflags "-w -s" 。这个命令可以隐藏所有代码实现相关的信息,并减少生成文件的大小。其中,-w 可以移除调试信息(无法使用gdb 调试),-s 可以移除符号表 |
在运行 go build
命令的时候,默认不会编译目标代码包所依赖的那些代码包。当然,如果被依赖的代码包的归档文件不存在,或者源码文件有了变化,那它还是会被编译。
如果要强制编译它们,可以在执行命令的时候加入标记 -a
。此时,不但目标代码包总是会被编译,它依赖的代码包也总会被编译,即使依赖的是标准库中的代码包也是如此。
如果不但要编译依赖的代码包,还要安装它们的归档文件,那么可以加入标记 -i
。那么我们怎么确定哪些代码包被编译了呢?有两种方法。
go build
命令时加入标记 -x
,这样可以看到 go build
命令具体都执行了哪些操作;-n
,这样可以只查看具体操作而不执行它们。运行 go build
命令时加入标记 -v
,这样可以看到 go build
命令编译的代码包的名称。它在与 -a
标记搭配使用时很有用;什么是跨平台编译呢?就是你在 macOS
开发,可以编译 Linux
、Windows
等平台上的可执行程序,这样你开发的程序,就可以在这些平台上运行。也就是说,你可以选择喜欢的操作系统做开发,并跨平台编译成需要发布平台的可执行程序即可。
使用示例:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
参数说明
CGO_ENABLED
: CGO
表示 golang
中的工具,CGO_ENABLED=0
表示 CGO
禁用,交叉编译中不能使用 CGO
;GOOS
: 环境变量用于指定目标操作系统,mac
对应 darwin
,linux
对应 linux
,windows
对应 windows
,还有其它的 freebsd
、android
等;GOARCH
:环境变量用于指定处理器的类型,386 也称 x86
对应 32位操作系统、amd64
也称 x64 对应 64 位操作系统,arm
这种架构一般用于嵌入式开发。比如 Android
, iOS
, Win mobile
等;针对不同操作系统或 CPU
的交叉编译(交叉构建)也是很简单的。只需要设置好目标对应的 GOOS
和 GOARCH
,然后运行构建命令即可。
通过组合不同的 GOOS
和 GOARCH
,就可以编译出不同的可执行程序。比如在 macOS AMD64
系统想编译出 Linux AMD64
的可执行程序,只需要执行如下代码:
$ GOOS=linux GOARCH=amd64 go build ./main.go
nvidia@tegra-ubuntu:~$ uname -a
Linux tegra-ubuntu 4.4.38-tegra #1 SMP PREEMPT Fri Jul 28 09:55:22 PDT 2017 aarch64 aarch64 aarch64 GNU/Linux
root@orangepizerolts:/tmp# uname -a
Linux orangepizerolts 5.4.27-sunxi #2.0.8 SMP Tue Jun 9 18:36:35 CST 2020 armv7l armv7l armv7l GNU/Linux
aarch64
和 armv7l
都是 arm
架构
GOOS=linux GOARCH=arm go build ./main.go
Golang
支持在一个平台下生成另一个平台可执行程序的交叉编译功能。交叉编译不支持 CGO
,所以设置 CGO_ENABLED=0
。
Mac
下编译 Linux
, Windows
平台的 64 位可执行程序:$ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
$ CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go
Linux
下编译 Mac
, Windows
平台的 64 位可执行程序:$ CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go
$ CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go
Windows
下编译 Mac
, Linux
平台的 64 位可执行程序:$ SET CGO_ENABLED=0 SET GOOS=darwin SET GOARCH=amd64 go build main.go
$ SET CGO_ENABLED=0 SET GOOS=linux SET GOARCH=amd64 go build main.go
Go
语言中go clean
命令可以移除当前源码包和关联源码包里面编译生成的文件,这些文件包括以下几种:
执行go build
命令时在当前目录下生成的与包名或者 Go 源码文件同名的可执行文件。
执行go test
命令并加入-c
标记时在当前目录下生成的以包名加“.test”后缀为名的文件。
执行go install
命令安装当前代码包时产生的结果文件。如果当前代码包中只包含库源码文件,则结果文件指的就是在工作区 pkg 目录下相应的归档文件。如果当前代码包中只包含一个命令源码文件,则结果文件指的就是在工作区 bin 目录下的可执行文件。
在编译 Go 或 C 源码文件时遗留在相应目录中的文件或目录 。包括:“_obj”和“_test”目录,名称为“_testmain.go”、“test.out”、“build.out”或“a.out”的文件,名称以“.5”、“.6”、“.8”、“.a”、“.o”或“.so”为后缀的文件。这些目录和文件是在执行go build
命令时生成在临时目录中的。
go clean
命令对应的参数的含义如下所示:
-i
清除关联的安装的包和可运行文件,也就是通过go install
安装的文件;-n
把需要执行的清除命令打印出来,但是不执行,这样就可以很容易的知道底层是如何运行的;-r
循环的清除在 import 中引入的包;-x
打印出来执行的详细命令,其实就是 -n 打印的执行版本;-cache
删除所有go build
命令的缓存-testcache
删除当前包所有的测试结果使用演示:
wohu@wohu:~/gocode/src/mypackage$ go build -race -v -o main ../mypackage/
wohu@wohu:~/gocode/src/mypackage$ go clean -n
cd /home/wohu/gocode/src/mypackage
rm -f mypackage mypackage.exe mypackage.test mypackage.test.exe main main.exe
wohu@wohu:~/gocode/src/mypackage$ ls
main main.go upload
wohu@wohu:~/gocode/src/mypackage$ go clean -x
cd /home/wohu/gocode/src/mypackage
rm -f mypackage mypackage.exe mypackage.test mypackage.test.exe main main.exe
wohu@wohu:~/gocode/src/mypackage$ ls
main.go upload
wohu@wohu:~/gocode/src/mypackage$
go run
命令会编译源码,并且直接执行源码的 main()
函数,不会在当前目录留下可执行文件。
wohu@wohu:~/gocode/src/mypackage$ ls
main.go upload
wohu@wohu:~/gocode/src/mypackage$ go run main.go
This is upload function
go run
不会在运行目录下生成任何文件,可执行文件被放在临时文件中被执行,工作目录被设置为当前目录。
在 go run
的后部可以添加参数,这部分参数会作为代码可以接受的命令行输入提供给程序。
go run
不能使用“go run+包”的方式进行编译,如需快速编译运行包,需要使用如下步骤来代替:
go build
生成可执行文件;Go
语言的开发团队制定了统一的官方代码风格,并且推出了 gofmt
工具( gofmt
或 go fmt
)来帮助开发者格式化他们的代码到统一的风格。
gofmt
是一个 cli
程序,会优先读取标准输入,如果传入了文件路径的话,会格式化这个文件,如果传入一个目录,会格式化目录中所有 .go
文件,如果不传参数,会格式化当前目录下的所有 .go
文件。
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 path/to/your/package
注意: gofmt
使用 tab
来表示缩进,并且对行宽度无限制,如果手动对代码进行了换行, gofmt
不会强制把代码格式化回一行。
go fmt 和 gofmt 区别:
gofmt
是一个独立的 cli 程序,而 Go
语言中还有一个go fmt
命令,go fmt
命令是 gofmt
的简单封装。
go fmt
命令本身只有两个可选参数-n
和-x
:
-n
仅打印出内部要执行的go fmt
的命令;-x
命令既打印出go fmt
命令又执行它,如果需要更细化的配置,需要直接执行 gofmt 命令。go install
命令的功能和 go build
中命令类似,附加参数绝大多数都可以与 go build
通用。 go install
只是将编译的中间文件放在 GOPATH
的 pkg
目录下,以及固定地将编译结果放在 GOPATH
的 bin
目录下。
这个命令在内部实际上分成了两步操作:第一步是生成结果文件(可执行文件或者 .a 包),第二步会把编译好的结果移到 $GOPATH/pkg
或者 $GOPATH/bin
。
go install
的编译过程有如下规律:
go install
是建立在 GOPATH
上的,无法在独立的目录里使用 go install
。GOPATH
下的 bin
目录放置的是使用 go install
生成的可执行文件,可执行文件的名称来自于编译时的包名。go install
输出目录始终为 GOPATH
下的 bin
目录,无法使用-o
附加参数进行自定义。GOPATH
下的 pkg
目录放置的是编译期间的中间文件。go get
命令可以借助代码管理工具通过远程拉取或更新代码包及其依赖包,并自动完成编译和安装。整个过程就像安装一个 App 一样简单。
这个命令可以动态获取远程代码包,目前支持的有 BitBucket
、 GitHub
、 Google Code
和 Launchpad
。在使用 go get
命令前,需要安装与远程包匹配的代码管理工具,如 Git
、 SVN
、 HG
等,参数中需要提供一个包名。
这个命令在内部实际上分成了两步操作:
go install
;所以为了 go get
命令能正常工作,你必须确保安装了合适的源码管理工具,并同时把这些命令加入你的 PATH
中。其实 go get
支持自定义域名的功能。
默认情况下, go get
可以直接使用。例如,想获取 go 的源码并编译,使用下面的命令行即可:
$ go get github.com/davyxu/cellnet
附加参数 | 备 注 |
---|---|
-v |
显示操作流程的日志及信息,方便检查错误 |
-u |
下载并安装所有的包和依赖的包的,以保证它们都是最新的版本,然后重新编译和安装。如果不包含该标志参数的话,而且如果该包本地已经存在,那么这个包的代码将不会被自动更新。 |
-d |
只下载代码包,不安装代码包 |
-insecure |
允许使用不安全的 HTTP 方式进行下载操作 |
-t |
同时也下载需要为运行测试所需要的包 |
-fix |
在下载代码包后先运行一个用于根据当前 Go 语言版本修正代码的工具,然后再安装代码包 |
该命令打印其后所指定的实体的声明与文档注释,该实体可能是一个包:
$ 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.
它提供可以相互交叉引用的 HTML 页面,但是包含和 go doc
命令相同以及更多的信息。 godoc
的在线服务 https://godoc.org ,包含了成千上万的开源包的检索工具。
也可以在自己的工作区目录运行 godoc
服务。运行下面的命令,然后在浏览器查看 http://localhost:8000/pkg 页面:
$ godoc -http :8000
其中-analysis=type
和-analysis=pointer
命令行标志参数用于打开文档和代码中关于静态分析的结果。
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
包的模板语言定义输出文本的格式。下面的命令将打印 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/...
tool
命令允许特定的 go
工具,比如 compile
工具,我们可以在命令行中输入 go tool cmd/compile
来查看具体的说明信息。compile
工具的基本用法为:
go tool compile [flags] file...
注意,这个命令的 file
文件必须是 Go
源代码文件,并且属于同一个包,[flags]
参数主要有以下几个:
-N
:禁止编译器优化。-S
:打印汇编语言列表到标准输出窗口(只打印代码code部分)。-S –S
:打印汇编语言列表到标准输出窗口(只打印代码code和数据data)。-blockprofile file
:将编译期间采样的 block profile
信息写入文件 file
中。-cpuprofile file
:将编译期间采样的 cpu profile
信息写入文件 file
中。-dynlink
:实验特性,允许共享库引用 Go symbols
。-e
:移除错误报告的数量限制(默认限制是10)。-h
:在检测到第一个错误时停止栈跟踪。-l
:禁止函数内联。-lang version
:设置Go语言的版本。-m
:打印编译器的优化策略信息。-memprofile file
:将编译期间采样的 memory profile
信息写入文件 file
中。-memprofilerate rate
:设置 runtime.MemProfileRate
的内存采集频率。-mutexprofile file
:将编译期间采样的 mutex profile
信息写入文件 file
中。-race
:开启竞争检测。-traceprofile file
:将执行的跟踪信息 execution trace
信息写入文件 file
中。Go 1.1
后提供了强大的检查工具 race
,它可以排查数据争用问题。race
可以用在多个 Go
指令中。
$ go test -race mypkg
$ go run -race mysrc.go
$ go build -race mycmd
$ go install -race mypkg
动态工具指的是需要实际运行指定代码才能够分析出问题的工具。go race
工具可以完成静态分析,但是有些并发冲突是静态分析难以发现的,所以 go race
在运行时也可以开启,完成动态的数据争用检测,一般在上线之前使用。
不过,竞争检测也有一定成本,它因程序的不同而有所差异。对于典型的程序来说,内存使用量可能增加 5~10 倍,执行时间会增加 2~20 倍。同时,竞争检测器还会为当前每个 defer
和 recover
语句额外分配 8 字节,在 Goroutine
退出前,这些额外分配的字节不会被回收。这意味着,如果有一个长期运行的 Goroutine
,而且定期有 defer
和 recover
调用,那么程序的内存使用量可能无限增长。
安装 Go
后,一般会建议执行 go version
看看是否安装成功,如下所示:
$ go version
go version go1.13.6 linux/amd64
$
但实际上,go version
还有其他用途,先 go help version
看看:
$ go help version
usage: go version [-m] [-v] [file ...]
Version prints the build information for Go executables.
Go version reports the Go version used to build each of the named
executable files.
If no files are named on the command line, go version prints its own
version information.
If a directory is named, go version walks that directory, recursively,
looking for recognized Go binaries and reporting their versions.
By default, go version does not report unrecognized files found
during a directory scan. The -v flag causes it to report unrecognized files.
The -m flag causes go version to print each executable's embedded
module version information, when available. In the output, the module
information consists of multiple lines following the version line, each
indented by a leading tab character.
See also: go doc runtime/debug.BuildInfo.
可见这个命令主要是用于输出 Go
可执行文件的编译信息的,只是如果没有提供可执行文件,则输出当前安装的 Go
版本信息。
go vet
是 Go
官方提供的代码静态诊断器,他可以对代码风格进行检查,并且报告可能有问题的地方,例如错误的锁使用、不必要的赋值等。
由于 go vet
本身是多种 Linter
的聚合器,我们可以通过 go tool vet help
命令查看它拥有的功能。
不过,当前企业中使用得最普遍的不是 go vet
而是 golangci-lint。这是因为 Go
中的分析器非常容易编写,社区已经创建了许多有用的 Linter
,而 golangci-lint
正是对多种 Linter 的集合。要查看 golangci-lint
支持的 Linter
列表以及 golangci-lint
启用 / 禁用哪些 Linter
,可以通过golangci-lint help linters
查看帮助文档或者查看官方文档 golangci-lint 。