Command Line

Commands in Go include a complete toolchain. These commands cover documentation, formatting, code checking, compilation, testing, dependency management, and many other aspects, covering almost every aspect of Go development.
bug Report bugs
build Compile packages and dependencies
clean Remove object files
doc Show documentation for source code
env Print Go environment information
fix Fix API compatibility issues caused by Go version changes
fmt Format source code
generate Generate code
get Add dependencies
install Compile and install packages
list List packages/modules
mod Module maintenance
work Workspace maintenance
run Compile and run
test Test
tool Run specified go tool
version Print Go version information
vet Scan and output potential issues in source codeThis article only briefly describes and introduces their usage. All content is referenced from official documentation. For more details, please visit cmd/go.
help
The first command to know is help, through which you can read the usage of commands. There are two ways to use it. If you want to get brief usage information, you can add the -h flag after the specified command, for example
$ go env -h
usage: go env [-json] [-u] [-w] [var ...]
Run 'go help env' for details.go will concisely display the usage of that command. It also prompts that if you want more detailed information, you need to use the help command
$ go help env
usage: go env [-json] [-u] [-w] [var ...]
Env prints Go environment information.
By default env prints information as a shell script
(on Windows, a batch file). If one or more variable
names is given as arguments, env prints the value of
each named variable on its own line.
The -json flag prints the environment in JSON format
instead of as a shell script.
The -u flag requires one or more arguments and unsets
the default setting for the named environment variables,
if one has been set with 'go env -w'.
The -w flag requires one or more arguments of the
form NAME=VALUE and changes the default settings
of the named environment variables to the given values.
For more about environment variables, see 'go help environment'.Make good use of the help command, through which you can get a lot of information about commands.
doc
$ go doc -h
Usage of [go] doc:
go doc
go doc <pkg>
go doc <sym>[.<methodOrField>]
go doc [<pkg>.]<sym>[.<methodOrField>]
go doc [<pkg>.][<sym>.]<methodOrField>
go doc <pkg> <sym>[.<methodOrField>]
For more information run
go help doc
Flags:
-C dir
change to dir before running command
-all
show all documentation for package
-c symbol matching honors case (paths not affected)
-cmd
show symbols with package docs even if package is a command
-short
one-line representation for each symbol
-src
show source code for symbol
-u show unexported symbols as well as exportedThe doc command outputs documentation comments for specified packages, constants, functions, types, variables, methods, and even struct fields. Without any arguments, it outputs the comments of the current package
$ go docYou can also specify to view a certain package, for example, view the documentation comments of the runtime package
$ go doc runtime
package runtime // import "runtime"
Package runtime contains operations that interact with Go's runtime system,
such as functions to control goroutines. It also includes the low-level type
information used by the reflect package; see reflect's documentation for the
programmable interface to the run-time type system.
......Or a certain type
$ go doc unsafe.Pointer
package unsafe // import "unsafe"
type Pointer *ArbitraryType
Pointer represents a pointer to an arbitrary type. There are four special
operations available for type Pointer that are not available for other
types:
- A pointer value of any type can be converted to a Pointer.
- A Pointer can be converted to a pointer value of any type.
- A uintptr can be converted to a Pointer.
- A Pointer can be converted to a uintptr.
...Or a certain function
$ go doc runtime.GC
package runtime // import "runtime"
func GC()
GC runs a garbage collection and blocks the caller until the garbage
collection is complete. It may also block the entire program.It has the following commonly used flags
-u: View private types-all: View all documentation of the specified package-short: Only one-line brief description-src: Output source code-cmd: For some packages that belong to go commands, also output their package code documentation.
For example, view the runtime.inf variable, which is an unexported variable
$ go doc -u runtime.inf
package runtime // import "runtime"
var inf = float64frombits(0x7FF0000000000000)Making good use of the doc command can help you read documentation more conveniently.
Another way to read command documentation is to read the source code, because some command documentation may not be written in detail, but there are more detailed explanations in the source code. Since all these commands are written in Go, it is quite convenient to read them. These commands are all located in the src/cmd package, and each subpackage is a separate command. The entry is located in the cmd/go/main.go file
func init() {
base.Go.Commands = []*base.Command{
bug.CmdBug,
work.CmdBuild,
clean.CmdClean,
doc.CmdDoc,
envcmd.CmdEnv,
fix.CmdFix,
fmtcmd.CmdFmt,
generate.CmdGenerate,
modget.CmdGet,
work.CmdInstall,
list.CmdList,
modcmd.CmdMod,
workcmd.CmdWork,
run.CmdRun,
test.CmdTest,
tool.CmdTool,
version.CmdVersion,
vet.CmdVet,
help.HelpBuildConstraint,
help.HelpBuildmode,
help.HelpC,
help.HelpCache,
help.HelpEnvironment,
help.HelpFileType,
modload.HelpGoMod,
help.HelpGopath,
get.HelpGopathGet,
modfetch.HelpGoproxy,
help.HelpImportPath,
modload.HelpModules,
modget.HelpModuleGet,
modfetch.HelpModuleAuth,
help.HelpPackages,
modfetch.HelpPrivate,
test.HelpTestflag,
test.HelpTestfunc,
modget.HelpVCS,
}
}Here you will find all subcommands of go, as well as their help documentation information.
bug
$ go help bug
usage: go bug
Bug opens the default browser and starts a new bug report.
The report includes useful system information.This command has no parameters or flags. It will use your default browser to visit the issue interface of the github.com/golang/go repository, making it convenient for you to report bugs. It has no other purpose.
version
Through the version command, you can view the current go version information.
$ go version -h
usage: go version [-m] [-v] [file ...]When executed without any parameters, it outputs the current go language version
$ go version
go version go1.21.0 windows/amd64It also accepts file paths as parameters, and it will output the go version used when compiling all recognizable binary files in that path.
$ go version -v ./
buf.exe: go1.20.2
cobra-cli.exe: go1.21.0
dlv.exe: go1.20.2
goctl.exe: go1.20.2
goimports.exe: go1.20.2
golangci-lint.exe: go1.20.2
gopls.exe: go1.19.3
kratos.exe: go1.20.2
main.exe: go1.19.1
protoc-gen-go-grpc.exe: go1.20.2
protoc-gen-go-http.exe: go1.20.2
protoc-gen-go.exe: go1.20.2
protoc-gen-openapi.exe: go1.20.2
swag.exe: go1.21.0
wire.exe: go1.21.0The -v parameter specifies the version command to try to output the go version of unrecognizable files, and the -m parameter outputs the module information of the binary file, as well as some compilation parameters. Here is a simple example.
$ go version -v -m wire.exe
wire.exe: go1.21.0
path github.com/google/wire/cmd/wire
mod github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
dep github.com/google/subcommands v1.0.1 h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k=
dep github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
dep golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b h1:NVD8gBK33xpdqCaZVVtd6OFJp+3dxkXuz7+U7KaVN6s=
build -buildmode=exe
build -compiler=gc
build DefaultGODEBUG=panicnil=1
build CGO_ENABLED=1
build CGO_CFLAGS=
build CGO_CPPFLAGS=
build CGO_CXXFLAGS=
build CGO_LDFLAGS=
build GOARCH=amd64
build GOOS=windows
build GOAMD64=v1go itself is also a binary file. In fact, without any parameters, go version outputs the go language version of its own binary file, because all toolchains of cmd/go are implemented by the go language itself.
env
Through the env command, you can view the information of all go environment variables. Modifying these environment variables will affect the behavior of the go toolchain.
$ go env -h
usage: go env [-json] [-u] [-w] [var ...]
Run 'go help env' for details.Executing this command without any parameters will output the values of all go environment variables
$ go env
set GO111MODULE=on
set GOARCH=amd64
...Taking a certain environment variable name as a parameter will only output the value of that variable
$ go env GO111MODULE
onAdding -json can output it in json format
$ go env -json
{
"AR": "ar",
"CC": "gcc",
......
}Through the -w flag, and taking var=value form as a parameter, it will permanently modify the value of a certain variable
$ go env -w GO111MODULE=onUsing the -u flag can restore a certain variable to its default value
$ go env -u GO111MODULEExecuting go help environment can view the introduction of each environment variable
$ go help environment
The go command and the tools it invokes consult environment variables
for configuration. If an environment variable is unset or empty, the go
command uses a sensible default setting. To see the effective setting of
the variable <NAME>, run 'go env <NAME>'. To change the default setting,
run 'go env -w <NAME>=<VALUE>'. Defaults changed using 'go env -w'
are recorded in a Go environment configuration file stored in the
per-user configuration directory, as reported by os.UserConfigDir.
The location of the configuration file can be changed by setting
the environment variable GOENV, and 'go env GOENV' prints the
effective location, but 'go env -w' cannot change the default location.
See 'go help env' for details.
General-purpose environment variables:
GO111MODULE
Controls whether the go command runs in module-aware mode or GOPATH mode.
May be "off", "on", or "auto".
See https://golang.org/ref/mod#mod-commands.
GCCGO
The gccgo command to run for 'go build -compiler=gccgo'.
GOARCH
The architecture, or processor, for which to compile code.
Examples are amd64, 386, arm, ppc64.
GOBIN
The directory where 'go install' will install a command.
GOCACHE
The directory where the go command will store cached
information for reuse in future builds.
......Below are some commonly used environment variables
GOVERSION
The value of this environment variable depends on the go language version, and the version number comes from the $GOROOT/VERSION file, which records the current go version number and build time.
$ cat $GOROOT/VERSION
go1.21.3
time 2023-10-09T17:04:35ZThe value of the runtime.Version variable is the same as the value of GOVERSION, and this environment variable cannot be modified.
GOENV
There will be a default configuration file named go.env in the $GOROOT directory
$ cat $GOROOT/go.env
# This file contains the initial defaults for go command configuration.
# Values set by 'go env -w' and written to the user's go/env file override these.
# The environment overrides everything else.
# Use the Go module mirror and checksum database by default.
# See https://proxy.golang.org for details.
GOPROXY=https://proxy.golang.org,direct
GOSUMDB=sum.golang.org
# Automatically download newer toolchains as directed by go.mod files.
# See https://go.dev/doc/toolchain for details.
GOTOOLCHAIN=autoIts format is simply key=value form. The environment variable values modified by the go env -w key=value command will be written to the configuration file. However, you can also not use the default configuration file. The GOENV environment variable can manually specify the address of the env configuration file, and the value of the GOENV environment variable can only be overridden by the operating system's environment variables, not by the go env -w command.
GOHOSTARCH
Represents the CPU architecture of the local machine, only for display. The value of this environment variable is not read from the configuration file and cannot be modified.
GOHOSTOS
Represents the operating system of the local machine, only for display. The value of this environment variable is not read from the configuration file and cannot be modified.
GOOS
When compiling, the value of GOOS will determine which target system's binary file the source code is compiled into. The default value is GOHOSTOS, which is the local machine's operating system. It has the following options
linuxdarwinwindowsnetbsdaixandroid
The actually supported operating systems are not limited to these. Use the command go tool dist list to view all supported values
$ go tool dist list | awk -F '/' '{print $1}' | awk '!seen[$0]++'
aix
android
darwin
dragonfly
freebsd
illumos
ios
js
linux
netbsd
openbsd
plan9
solaris
wasip1
windowsGOARCH
When compiling, the value of GOARCH will determine which CPU architecture's instructions are used during compilation. The default value is GOHOSTARCH, which is the local machine's CPU architecture. It has the following options
amd64386armppc64
The actually supported architectures are not limited to these. Use the go tool dist list command to view all supported values
$ go tool dist list | awk -F '/' '{print $2}' | awk '!seen[$0]++'
ppc64
386
amd64
arm
arm64
riscv64
wasm
loong64
mips
mips64
mips64le
mipsle
ppc64le
s390xNote that GOOS and GOARCh cannot be arbitrarily combined. Some operating systems can only support specific CPU architectures.
GOROOT
GOROOT represents the root directory of the go language installation location. The value of GOROOT cannot be directly modified and can only be overridden by the operating system's environment variables.
$ ls $GOROOT -1
api
bin
codereview.cfg
CONTRIBUTING.md
doc
go.env
lib
LICENSE
misc
PATENTS
pkg
README.md
SECURITY.md
src
test
VERSIONIn the root directory, there are the following important folders or files
lib, stores some dependencies. Currently, there is only a library containing timezone information from various countries, located at$GOROOT/lib/time. The compiled binary files will not include this timezone information.pkg, stores some tool libraries and header files. For example, thego toolcommand will look for go toolchain binary files in the$GOROOT/pkg/tooldirectorybin, stores binary files. By default, there are only two executable files,goandgofmt.$GOROOT/binshould be added to the system variables, otherwise the go command cannot be used.src, stores go source codeVERSION, this file stores the go language version informationgo.env, this file is the defaultenvconfiguration file
GOPATH
The default value of GOPATH is $HOME/go. The value of this environment variable specifies where to look for imported files when parsing import statements. In the early days without gomod, GOPATH was specifically used to store various third-party libraries. Its structure is as follows
GOPATH=/home/user/go
/home/user/go/
src/
foo/
bar/ (go code in package bar)
x.go
quux/ (go code in package main)
y.go
bin/
quux (installed command)
pkg/
linux_amd64/
foo/
bar.a (installed package object)After gomod was born, GOPATH is just a place to store dependencies downloaded by go get and binary files downloaded and compiled by go install. Note that the location of GOPATH cannot be the same as GOROOT, otherwise it will have no effect.
$ go env GOBIN
warning: GOPATH set to GOROOT (/home/user/go) has no effectAs of the time I wrote this article, the go language version has reached go1.21.3. Except for very old projects, basically no one uses gopath to manage dependencies anymore.
GOBIN
GOBIN is used to store third-party binary executable files downloaded and compiled by go install. Its default value is $GOPATH/bin. Like $GOROOT/bin, this directory should be added to the operating system's environment variables, otherwise the binary files in the GOBIN directory cannot be used.
GOMODCACHE
GOMODCACHE represents the location where dependencies downloaded by go get are stored. Its default value is $GOPATH/pkg/mod. Its storage format is as follows
$GOMODCACHE/domain/username/project@verionIn the same directory, there will also be a folder named sumdb, which is used to store dependency checksum database related information.
GOCACHE
Stores cache information used for compilation. Its default value is $HOME/.cache/go-build. A README file will be generated in this directory.
$ cat $(go env GOCACHE)/README
This directory holds cached build artifacts from the Go build system.
Run "go clean -cache" if the directory is getting too large.
Run "go clean -fuzzcache" to delete the fuzz cache.
See golang.org to learn more about Go.Every build produces many files. Go will cache these files for reuse in the next compilation.
GOTEMPDIR
Used for temporary files generated during compilation, such as temporary binary files to be run by go run. Its default value is the temporary directory specified by the operating system. On mac or linux it is /tmp, on windows it is %TEMP%. It can also be modified to a location specified by the user.
GO111MODULE
This environment variable indicates which method to use to manage go project dependencies. It has the following three available values
off, turn off gomod, use gopath, and ignore allgo.modfileson, use gomod, do not use gopath (default).auto, automatic detection. If the project file containsgo.mod, it will use gomod for management
TIP
Why is it called GO111MODULE instead of directly GOMODULE? Because gomod was first introduced in the go1.11 version.
GOPROXY
Go module proxy, the default value is https://proxy.golang.org,direct. URLs are separated by commas. direct means to use VCS directly to skip the module proxy. It will only execute the latter when the former cannot be accessed. Another available option is off, which means to prohibit downloading any modules. In addition, GOPROXY can also be a file address, for example
GOPROXY=file://$(go env GOMODCACHE)/cache/downloadThrough go get -x, you can view the commands executed during the dependency download process, so you can know whether the proxy is used.
$ go get -x github.com/spf13/cast
# get https://goproxy.cn/github.com/@v/list
# get https://goproxy.cn/github.com/spf13/cast/@v/list
# get https://goproxy.cn/github.com/spf13/@v/list
# get https://goproxy.cn/github.com/spf13/@v/list: 404 Not Found (0.118s)
# get https://goproxy.cn/github.com/@v/list: 404 Not Found (0.197s)
# get https://goproxy.cn/github.com/spf13/cast/@v/list: 200 OK (0.257s)
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.info
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.info: 200 OK (0.013s)
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.mod
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.mod: 200 OK (0.015s)
# get https://goproxy.cn/sumdb/sum.golang.org/supported
# get https://goproxy.cn/sumdb/sum.golang.org/supported: 200 OK (0.064s)
# get https://goproxy.cn/sumdb/sum.golang.org/lookup/github.com/spf13/cast@v1.5.1
# get https://goproxy.cn/sumdb/sum.golang.org/lookup/github.com/spf13/cast@v1.5.1: 200 OK (0.014s)
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x079/736
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x079/736: 200 OK (0.016s)
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x068/334
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/1/266
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x068/334: 200 OK (0.023s)
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/1/266: 200 OK (0.028s)
go: downloading github.com/spf13/cast v1.5.1
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.zip
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.zip: 200 OK (0.024s)
go: added github.com/spf13/cast v1.5.1Using a module proxy can effectively speed up module downloads. For users in China, the default official proxy is usually inaccessible without a proxy. The following are publicly available and trusted third-party module proxies:
https://proxy.golang.com.cn,开源同时提供企业版服务https://goproxy.cn,七牛云提供并开源
当然也有开源的自建模块代理方案:goproxy
GOSUMDB
GOSUMDB用于设置依赖库的校验和检测数据库地址,默认是sum.golang.org,当你设置了代理后,go 就会通过代理来访问校验数据库。
GOPRIVATE
GOPRIVATE环境变量用于设置私有的库,匹配的库将不会通过 sumdb 进行校验,也不会走代理,将通过 VCS 直接下载依赖。该支持通配符设置,使用逗号分隔,如下所示,所有后缀为corp.example.com和名为github.com/gohper/myproject的依赖都不会走代理和 sumdb。
GOPRIVATE=*.corp.example.com,github.com/gohper/myprojectYou can also set it directly for a specific user or organization
GOPRIVATE=github.com/gopher,github.com/myorganizationGONOPROXY
Indicates which dependencies should not go through the proxy. The rules are the same as GOPRIVATE, and it will override GOPRIVATE.
GONOSUMDB
Indicates which dependencies should not go through the sumdb. The rules are the same as GOPRIVATE, and it will override GOPRIVATE.
GOINSECURE
Indicates which dependencies should be downloaded directly using VCS. The rules are the same as GOPRIVATE, and it will be overridden by GONOPROXY and GONOSUMDB.
GOVCS
Sets the version control system to be used for module management. The default value is public:git|hg,private:all. You can also limit the VCS for specific domains, for example
GOVCS=github.com:git,evil.com:off,*:git|hgIn the above restrictions, github can only use git, while evil.com is not allowed to use any VCS. You can use | to represent multiple VCS. If no restrictions are set, you can set it as follows
GOVCS=*:allIf no VCS is allowed, you can set it as follows
GOVCS=*:offGOWORK
Sets whether to enable the workspace. The default value is empty, which means enabled. If set to off, it will be disabled, and all go.work files will be ignored.
GOTOOLDIR
Sets the location of the go toolchain to be used. The default value is $GOROOT/pkg/tool, and the default toolchain is also stored in this location.
GODEBUG
Sets debug options to control the partial execution behavior of go programs. For example
GODEBUG=http2client=0,http2server=0These settings are provided so that, when an incompatible change is introduced during a version update, Go can easily fall back to the previous behavior. For example, starting with Go 1.21, panic(nil) is no longer allowed. For this reason, the Go team maintains a dedicated “GODEBUG History”; visit GODEBUG for more details.
CGO_ENABLED
Indicates whether to enable cgo. The default is 1 (enabled); set it to 0 to disable.
The environment variables listed above are the most commonly used ones. For less frequently used ones—such as CGO, WASM, etc.—we won’t go into detail here; feel free to explore them on your own if interested.
build
Go supports two compilers: gccgo and gc. gcc is a veteran C/C++ compiler that supports multiple languages, including Go. The latter, gc, does not stand for garbage collection; it means “Go compiler.” Go completed its self-hosting transition in Go 1.5, and gc is now a compiler written entirely in Go. Its source code lives in the cmd/compile package, and because it is implemented purely in Go, studying its internals is straightforward. By default, the toolchain uses gc for compilation. Incidentally, Go also offers two debuggers: gdb and dlv. gdb is the classic C/C++ debugger that supports many languages, including Go, while dlv is a debugger written in Go and offers better support for Go programs. It is open-source and the recommended choice.
The build command compiles Go source files into an executable binary, delivering the famously fast compilation experience that is one of Go’s hallmark features.
$ go build -h
usage: go build [-o output] [build flags] [packages]
Run 'go help build' for details.它接收三个参数,一个是-o标志所指示的文件输出路径,一个是用于定义编译行为的构建标志build flas,最后一个就是要编译的包,该参数必须放在最后。下面是一个简单的例子,没用到构建标志。
# Windows
$ go build -o .\bin\golearn.exe golearn
# macOS / Linux
$ go build -o ./bin/golearn golearn./bin/golearn.exe是表示输出路径,golearn表示要编译的模块,也可以是一个入口文件,或是一个文件夹。比如下面简单的例子是以main.go入口文件作为编译目标。
# Windows
$ go build -o .\bin\golearn.exe main.go
# macOS / Linux
$ go build -o ./bin/golearn main.go在编译的时候它会忽略掉所有以_test.go结尾的文件,因为些文件按照约定都是测试文件。
除此之外,build命令还支持相当多的构建标志用于控制编译时的一些行为。
-x:输出编译过程中的详细指令-n:与-x类似,但是区别是它只是输出这些指令,但实际上并不会执行。-v:输出编译的包-p:编译过程中的并发数-a:强制重新构建,即使已经是最新的了。-compiler:指定使用哪个编译器,gccgo或者gc,后者是由 go 编写的编译器。-race:开启竞态检测-msan:开启内存分析-asan:开启地址分析-cover:开启代码覆盖检测-buildmode:指定编译模式,有archive,c-archive,c-shared,default,shared,exe,pie,plugin这几个选项。-pgo,指定 pgo 文件-trimpath:消除源文件路径前缀,比如相对路径/var/lib/go/src/main.go,消除后在运行时通过runtime获取到的文件名就只有相对于模块路径的相对路径/main.go,开启此项后,编译耗时会明显上升,大概在 20-40%左右,取决于文件数量。-toolexec,在编译前执行的一些 go 命令,格式为-toolexec 'cmd args'。-gcflags:指定编译器 gc 的一些 tag-gccgoflags:指定编译器 gccgo 的一些 tag-ldflags:指定 link 工具的一些 tag
对于一些 ldflags 之类的传递参数,可以传递"-help"这样的参数来获取其可能的值,比如
$ go build -ldflags -help
usage: link [options] main.o
-B note
add an ELF NT_GNU_BUILD_ID note when using ELF
-E entry
set entry symbol name
......上述这些是比较常用的,对于其它不怎么常用的可以自行了解。
gcflags
通过gcflags可以向编译器 gc 传递一些参数以控制特定的行为,它的使用格式是-gcflags="pattern=args list",ages list就是参数列表,pattern就是作用范围,有以下几个可用的值
main,入口文件所在的顶级包路径all,当前模块以及当前模式的所有依赖std,标准库cmd,作用cmd包下的所有源文件- 通配符,比如
.,./...,cmd/...。
该pattern规则适用于所有支持该格式的标志,例如ldflags。通过如下命令查看其参数可用的值
$ go build -gcflags -help
usage: compile [options] file.go...
-% debug non-static initializers
-+ compiling runtime
-B disable bounds checking
-C disable printing of columns in error messages
-D path
set relative path for local imports
-E debug symbol export
-I directory
add directory to import search path
-K debug missing line numbers
-L also show actual source file names in error messages for positions affected by //line directives
-N disable optimizations
-S print assembly listing
-V print version and exit
-W debug parse tree after type checking
......下面介绍几个常用的参数
-S:输出代码的汇编形式-N:关闭编译优化-m:输出优化决策-l:关闭函数内联-c:编译的并发数-dwarf:生成 DWARF 标志
比如如果要查看代码的汇编形式,可以使用-S参数,并且还要关闭优化和内联,这样才能还原其本来的形式,如下
$ go build -trimpath -gcflags="-N -l -S" main.go
main.main STEXT size=171 args=0x0 locals=0x58 funcid=0x0 align=0x0
0x0000 00000 (./main.go:9) TEXT main.main(SB), ABIInternal, $88-0
0x0000 00000 (./main.go:9) CMPQ SP, 16(R14)
0x0004 00004 (./main.go:9) PCDATA $0, $-2
0x0004 00004 (./main.go:9) JLS 161
0x000a 00010 (./main.go:9) PCDATA $0, $-1
0x000a 00010 (./main.go:9) PUSHQ BP
0x000b 00011 (./main.go:9) MOVQ SP, BP
0x000e 00014 (./main.go:9) SUBQ $80, SP
0x0012 00018 (./main.go:9) FUNCDATA $0, gclocals·J5F+7Qw7O7ve2QcWC7DpeQ==(SB)
0x0012 00018 (./main.go:9) FUNCDATA $1, gclocals·bDfKCdmtOiGIuJz/x+yQyQ==(SB)
0x0012 00018 (./main.go:9) FUNCDATA $2, main.main.stkobj(SB)
0x0012 00018 (./main.go:10) MOVUPS X15, main..autotmp_0+40(SP)
0x0018 00024 (./main.go:10) LEAQ main..autotmp_0+40(SP), CX
0x001d 00029 (./main.go:10) MOVQ CX, main..autotmp_2+32(SP)ldflags
通过 ldflags 可以向链接器传递一些参数以控制特定的行为,通过如下命令来查看ldflags所有可用的值,接近二三十个。
$ go build -ldflags -help
usage: link [options] main.o
-B note
add an ELF NT_GNU_BUILD_ID note when using ELF
-E entry
set entry symbol name
-H type
set header type
-I linker
use linker as ELF dynamic linker
-L directory
add specified directory to library path
-R quantum
set address rounding quantum (default -1)
-T int
set the start address of text symbols (default -1)
-V print version and exit
-X definition
add string value definition of the form importpath.name=value
-a no-op (deprecated)
.....ldflags的-X参数是一个非常实用的功能,它可以在链接时定义指定包的字符串变量的值。通过这个功能,我们可以很方便的在编译时注入一些元信息。而且它只是一个变量,所以也方便在运行时获取,下面是一个简单的例子。
package main
import "fmt"
var (
Version string
)
func main() {
fmt.Println(Version)
}执行命令
go build -ldflags "-X main.Version=$(git describe --always)" main.go运行后就会输出 git 提交的 sha1 校验和。
5e3fd7a另外一些比较实用的参数有
-w:不生成 DWARF,这是一种方便调试源码的信息。-s:禁用符号表
这两个通常放一块用,可以显著的减小编译后的二进制文件的体积,大概有 40%-50%左右,缺点也很明显,没法进行调试,下面是一个例子。
$ go build -ldflags="-w -s" main.go交叉编译
go 语言编译总共有两大特点,第一个就是快,另外一大特点就是交叉编译,交叉编译指的是可以在本地编译成其它系统的目标代码,例如在windows上编译成linux或darwin上的二进制文件,反过来也是一样。交叉编译支持的语言非常多,这并不是什么稀奇事,但是 go 语言交叉编译非常的简单,只需要以下两步
- 设置 GOOS 环境变量,选择你的目标操作系统
- 设置 GOARCH 环境变量,选择你的目标 CPU 架构
- 像平时一样使用
go build进行编译
整个过程非常短,不需要使用额外的工具或配置,而且速度跟平时一样快。如下所示
build_linux:
SET CGO_ENABLED=0
SET GOOS="linux"
SET GOARCH="amd64"
go build -o golearn main.go
build_mac:
SET CGO_ENABLED=0
SET GOOS="darwin"
SET GOARCH="amd64"
go build -o golearn main.go
build_win:
SET CGO_ENABLED=0
SET GOOS="win"
SET GOARCH="amd64"
go build -o golearn.exe main.go
.PHONY: build_linux \
build_mac \
build_win第一步SET CGO_ENABLED=0这一项禁用了 cgo,一旦你的代码中使用了 cgo,那么就无法正常使用交叉编译。第二步SET GOOS设置目标系统,可选的有linux,darwin,windwos,netbsd。第三步设置 CPU 架构,SET GOARCH,可选的有amd64,386,arm,ppc64。最后一步就是像往常一样进行编译。
编译控制
build命令可以通过tags来达到控制编译的效果,它以一种指令的方式存在于源代码中,看个例子,product.go文件
// +build product
package main
import "fmt"
func main() {
fmt.Println("product")
}debug.go文件
// +build debug
package main
import "fmt"
func main() {
fmt.Println("debug")
}它们都有一个// +build指令,表示它们在什么情况下才会被编译。其基本格式为
// +build tag1 tag2
package pkg_name有几个必须遵守的规则
//与+build必须隔一个空格- 它必须位于包声明的上方
- 与包声明必须隔一行空行
除此之外,它还可以通过简单的间隔来达到逻辑控制的目的,空格表示 OR,逗号表示 AND,!表示 NOT。比如下面这个例子
// +build windows linux
package pkg_name表示在 windows 或者 linux 平台下会将当前文件编译进去。
// +build windows,amd64,!cgo linux,i386,cgo
package pkg_name这个例子表示的是在 windows 平台 amd64 架构且未启用 cgo 或者是 linux 平台 i386 架构且启用了 cgo 才会将其编译。如果你只是单纯的不想让某个文件不参加编译,可以使用ignore。
// +build ignore
package pkg_name也可以存在多行指令
// +build windows
// +build amd64
package pkg_name多行指令以 AND 方式进行处理。对于平台和架构这些 tag,在编译时 go 会自动传入,我们也可以传入自定义的 tag,就拿最开始的拿两个文件举例
$ go build -tags="debug" . && ./golearn.exe
debug
$ go build -tags="product" . && ./golearn.exe
product可以看到传入不同 tag 时输出不同,编译控制的目的也就达到了。
run
run命令与build都会将源代码进行编译,不同的是run命令在编译完成后会直接运行。 run命令为了加快编译速度,在编译过程中不会生成调试信息,所以也就不支持调试,并且只是生成一个临时的二进制文件,通常存放在GOTMEPDIR目录下,例如/temp/go-build2822241271/b001/exe/main.exe。
$ go run -h
usage: go run [build flags] [-exec xprog] package [arguments...]
Run 'go help run' for details.它也支持build命令的构建标志,还提供了一个参数-exec来指明由哪个程序来运行二进制文件,[arguments...]指的是程序的运行参数。下面是一个例子
package main
import (
"fmt"
"os"
)
var (
Version string
)
func main() {
fmt.Println(Version)
fmt.Println(os.Args[1:])
}使用go run运行
$ go run -ldflags="-X main.Version=$(git describe --always)" main.go hello
5e3fd7a
[hello]总体上使用起来与go build没有太大的差别,就不再做过多的赘述。
tool
tool命令本身没有任何功能,它的作用是直接调用cmd/目录下的工具,例如cmd/compile就是自带的编译器。通过go tool可以直接调用这些工具,不用去手动执行这些工具的二进制文件。
$ go tool -h
usage: go tool [-n] command [args...]使用-n参数打印出其所有支持的命令参数
$ go tool -n
addr2line
asm
buildid
cgo
compile
covdata
cover
doc
fix
link
nm
objdump
pack
pprof
test2json
trace
vet这些工具存放在GOROOT/pkg/tool目录下,并且根据操作系统和 CPU 架构对工具进行分组,如下。
$ ls $GOROOT/pkg/tool/windows_amd64/ -1
addr2line.exe*
asm.exe*
buildid.exe*
cgo.exe*
compile.exe*
covdata.exe*
cover.exe*
doc.exe*
fix.exe*
link.exe*
nm.exe*
objdump.exe*
pack.exe*
pprof.exe*
test2json.exe*
trace.exe*
vet.exe*使用go doc cmd/command格式查看每个命令的用法,比如
$ go doc cmd/compile
Usage:
go tool compile [flags] file...
The specified files must be Go source files and all part of the same package.
The same compiler is used for all target operating systems and architectures.
The GOOS and GOARCH environment variables set the desired target.
Flags:
-D path
Set relative path for local imports.
-I dir1 -I dir2
Search for imported packages in dir1, dir2, etc,
after consulting $GOROOT/pkg/$GOOS_$GOARCH.
-L
Show complete file path in error messages.
...cmd/compile支持的标志参数,也就是前面提到过的gcflags支持的参数。go tool compile与go build的不同在于,前者只是负责编译,并且只能以文件作为参数,后者可以以文件夹,包,文件作为参数,而且不仅做了编译源代码这一件事,还负责链接文件,清除无用的文件等,前者是后者的一部分。我们可以打印build过程中执行的命令
$ go build -n main.go
#
# internal/goarch
#
mkdir -p $WORK\b004\
cat >$WORK\b004\importcfg << 'EOF' # internal
# import config
EOF
"/golang/pkg/tool/windows_amd64/compile.exe" -o "$WORK/b004/_pkg_.a" -trimpath "$WORK/b004=>" -p internal/goarch -std -+ -complete -buildid 3gunEkUExGdhOPa2rFsh/3gunEkUExGdhOPa2rFsh -goversion go1.21.0 -c=4 -nolocalimports -importcfg "$WORK/b004/importcfg" -pack "/golang/src/internal/goarch/goarch.go" "/golang/src/internal/goarch/goarch_amd64.go" "/golang/src/internal/goarch/zgoarch_amd64.go"
"/golang/pkg/tool/windows_amd64/buildid.exe" -w "$WORK/b004/_pkg_.a" # internal
...在过程中可以看到有这么一段/golang/pkg/tool/windows_amd64/compile.exe,正是调用了编译器。除了compile之外,还有很多工具可以调用,很多 go 命令实际上都是它们的别名。
clean
clean命令用于清除编译过程中生成的对象文件
$ go clean -h
usage: go clean [clean flags] [build flags] [packages]
Run 'go help clean' for details.它支持以下标志
-i:清除对应的归档文件或二进制文件-n:打印将要清除过程要执行的命令但实际并不执行-x:打印清除过程中的要执行的命令并执行-r:通过import path递归的进行清除-cache,清除所有go build产生的缓存-testcache:清除所有产生的测试缓存-modcache:清除所有下载的模块缓存-fuzzcache:清除fuzz test产生的缓存。
当使用go tool compile时,是直接调用编译器命令,并不像go build那会做很多善后处理,就会产生对象文件。比如执行如下的命令
go tool compile -N -S -l main.go就会生成一个名为main.o的文件,使用go clean命令清除即可。或者使用-n参数打印将要执行的命令。
$ go clean -n
rm -f golearn golearn.exe golearn golearn.exe golearn.test golearn.test.exe golearn.test golearn.test.exe api api.exe main main.exe清除编译缓存,它会删除GOCACHE目录下产生的编译缓存
$ go clean -cache -n
rm -r /cache/00 /cache/01 /cache/02清除 fuzz test 产生的缓存,这些缓存默认存放在GOCACHE/fuzz/目录下
$ go clean -fuzzcache -n
rm -rf /cache/fuzzfix
go 语言截至到撰写本文时已经有十年了,在语言不断更新和修改的过程中,难免会出现一些因 API 的变化而导致的不兼容,fix命令就是为此而生的,它会检测源文件中那些已经过时的 API 并将其替换为新的 API。
$ go fix -h
usage: go fix [-fix list] [packages]
Run 'go help fix' for details.它支持文件夹,文件名,目录作为参数,接收-fix标志来传递参数以表明进行何种修改,可以通过got tool fix -help命令查看可用的值
$ go tool fix -help
usage: go tool fix [-diff] [-r fixname,...] [-force fixname,...] [path ...]
-diff
display diffs instead of rewriting files
-force string
force these fixes to run even if the code looks updated
-go string
go language version for files
-r string
restrict the rewrites to this comma-separated list
Available rewrites are:
buildtag
Remove +build comments from modules using Go 1.18 or later
cftype
Fixes initializers and casts of C.*Ref and JNI types
context
Change imports of golang.org/x/net/context to context
egl
Fixes initializers of EGLDisplay
eglconf
Fixes initializers of EGLConfig
gotypes
Change imports of golang.org/x/tools/go/{exact,types} to go/{constant,types}
jni
Fixes initializers of JNI's jobject and subtypes
netipv6zone
Adapt element key to IPAddr, UDPAddr or TCPAddr composite literals.
https://codereview.appspot.com/6849045/
printerconfig
Add element keys to Config composite literals.下面举个例子,源代码中用到了golang.org/x/net/context包
package main
import (
"fmt"
"golang.org/x/net/context"
)
func main() {
background := context.Background()
fmt.Println(background.Err())
}使用go fix修正,将其替换为标准库中的context包,我们可以如下命令来进行替换
$ go fix -fix context main.go也可以不替换,看看前后文件变化。
$ go tool fix -r context -diff main.go
main.go: fixed context
diff main.go fixed/main.go
--- main.go
+++ fixed/main.go
@@ -1,8 +1,8 @@
package main
import (
+ "context"
"fmt"
- "golang.org/x/net/context"
)
func main() {go 语言诞生了十多年只有九个可用的替换参数,可见兼容性保持的还算可以。
fmt
fmt命令是 go 语言自带的格式化工具,用于格式化 go 源代码文件。
$ go fmt -h
usage: go fmt [-n] [-x] [packages]
Run 'go help fmt' for details.通过命令go doc gofmt查看其详细文档
$ go doc cmd/gofmt
Gofmt formats Go programs. It uses tabs for indentation and blanks for
alignment. Alignment assumes that an editor is using a fixed-width font.
Usage:
gofmt [flags] [path ...]
The flags are:
-d
Do not print reformatted sources to standard output.
If a file's formatting is different than gofmt's, print diffs
to standard output.
-e
Print all (including spurious) errors.
-l
Do not print reformatted sources to standard output.
If a file's formatting is different from gofmt's, print its name
to standard output.
-r rule
Apply the rewrite rule to the source before reformatting.
-s
Try to simplify code (after applying the rewrite rule, if any).
-w
Do not print reformatted sources to standard output.
If a file's formatting is different from gofmt's, overwrite it
with gofmt's version. If an error occurred during overwriting,
the original file is restored from an automatic backup.gofmt使用tab进行缩进,空格进行对齐,在默认情况下格式化后的代码将会输出到标准输出中,并非覆盖到原文件。go fmt命令实际上用到的是gofmt命令,它是一个独立的二进制文件,位于GOROOT/bin目录下。
$ ls $GOROOT/bin -1
go.exe*
gofmt.exe*给go fmt命令加上-n标志就可以知晓其将要执行的指令。
$ go fmt main.go
/golang/bin/gofmt.exe -l -w main.go可以看出go fmt其实就是是gofmt -l -w的别名,gofmt命令有以下参数
-d:输出格式化前后的文件差异-e:输出所有错误-l:输出发生变化的文件名-r:应用格式化规则-s:尝试简化代码-w:覆盖源文件,如果发生错误就恢复备份
假设现在有如下源文件
$ cat main.go
package main
import "fmt"
func main() {
fmt.Println("hello world!")}通过-d参数可以预览其变化
$ gofmt -d main.go
diff main.go.orig main.go
--- main.go.orig
+++ main.go
@@ -3,5 +3,5 @@
import "fmt"
func main() {
-fmt.Println("hello world!")}
-
+ fmt.Println("hello world!")
+}-l参数将会输出将要修改的文件名
$ gofmt -l .
main.go如果存在语法错误的话,-e参数可以输出的更详细
$ gofmt -d -e main.go
main.go:6:27: missing ',' in argument list
main.go:6:28: expected operand, found newline
main.go:7:2: expected ')', found 'EOF'
main.go:7:2: expected ';', found 'EOF'
main.go:7:2: expected ';', found 'EOF'
main.go:7:2: expected '}', found 'EOF'
main.go:7:2: missing ',' in argument list-w会将修改应用到源文件中
$ gofmt -l -w .
main.go
$ cat main.go
package main
import "fmt"
func main() {
fmt.Println("hello world!")
}你可以发现作为一个格式化工具,gofmt完全没有提供任何的自定义配置,而专为美化 js 代码的格式化器prettify它就提供了相当多的配置用于格式化代码,这里可以体现出 go 官方的一个态度,别想搞什么个性化,所有人代码风格最好都是一致的,至少有一个好处就是在阅读代码的时候不用去适应他人的习惯。不过其实它还是保留了一个自定义项的,就是格式化代码的替换规则,规则是可以自定义的,格式如下
pattern -> replacement比如去除冗余的括号
(a) -> a查看文件变更
$ gofmt -r "(a) -> a" -d -l .
main.go
diff main.go.orig main.go
--- main.go.orig
+++ main.go
@@ -3,5 +3,5 @@
import "fmt"
func main() {
- fmt.Println(("hello world!"))
+ fmt.Println("hello world!")
}可以看到gofmt会将冗余的括号删除掉。
get
get命令绝对是 go 开发过程中最常用的了,它的作用是将指定地址包源代码下载到GOMODCACHE所对应的目录中。
$ go get -h
usage: go get [-t] [-u] [-v] [build flags] [packages]
Run 'go help get' for details.-u:尝试更新包的次要版本以及补丁版本,如果涉及到主版本的改变,比如v1->v2,将不会更新。-t:更新测试中的依赖版本-v:输出被编译的包,实际上属于build flags的参数之一
在古早时期,go get的作用和go install类似,它会下载并编译这些包,然而随着 go 模块的诞生与完善,这一部分的作用渐渐的被废弃了,get命令现在最常用的作用就是对 go 模块下载并解析依赖,所以你可以看到go get命令还支持build flags这类构建标志,并且如果你尝试在模块外像使用go install一样使用go get,它会提示你此用法已经被废弃了。
$ go get github.com/wire/wire
go: go.mod file not found in current directory or any parent directory.
'go get' is no longer supported outside a module.
To build and install a command, use 'go install' with a version,
like 'go install example.com/cmd@latest'
For more information, see https://golang.org/doc/go-get-install-deprecation
or run 'go help get' or 'go help install'.至于为什么在文档描述中还保留这些也是不得而知,翻看get命令的源代码,你还会发现它保留了以前的那些 flag。
var (
getD = CmdGet.Flag.Bool("d", true, "")
getF = CmdGet.Flag.Bool("f", false, "")
getFix = CmdGet.Flag.Bool("fix", false, "")
getM = CmdGet.Flag.Bool("m", false, "")
getT = CmdGet.Flag.Bool("t", false, "")
getU upgradeFlag
getInsecure = CmdGet.Flag.Bool("insecure", false, "")
// -v is cfg.BuildV
)回到正题,get命令会将指定的包的源代码下载到本地的全局依赖目录中,也就是GOCACHE对应的目录,然后将信息记录到go.mod和go.sum文件中,前者负责记录版本,后者负责记录 sha1 校验和确保安全性。get命令实际上是基于 VCS,也就是本地的版本控制系统,总共支持下面几个
- git
- hg (Mercurial)
- bzr (Bazaar)
- svn
- fossil
其中,默认只支持 git 和 hg,可以GOVCS中进行配置,格式如下
GOVCS=github.com:git,example.com:hg,*:git|hg,*:allGOVCS仅支持 git 和 hg 作为 VCS,其它三个需要在GOPRIVATE中配置。
go get命令总共有下面几种用法,可以直接将依赖地址作为参数
$ go get golang.org/x/net也可以指定版本
$ go get golang.org/x/net@0.17.0指定最新版本
$ go get golang.org/x/net@latest尝试更新版本
$ go get -u golang.org/x/net移除某一依赖
$ go get golang.org/x/net@none上面这些是用来管理普通的依赖,它还可以用来管理不那么普通的依赖,比如更新 go 语言的版本
$ go get go@latest
go: updating go.mod requires go >= 1.21.3; switching to go1.21.3
go: downloading go1.21.3 (windows/amd64)
go: upgraded go 1.21.0 => 1.21.3甚至还可以用来更新 go 工具链的版本
$ go get toolchain@latest当你使用go get更新 go 和工具链版本时,它们会在GOMODCACHE/golang.org/目录下安装新版本的 go
$ ls $(go env GOMODCACHE)/golang.org -1
'toolchain@v0.0.1-go1.21.3.windows-amd64'/
x/这时候再手动修改一下GOROOT就可以切换到指定的版本了。
install
install命令与get命令类似,它们都是用于下载第三方的依赖,不过区别在于get命令下载的是源码,而install命令会将源码编译成本机可执行的二进制文件,二进制文件存放路径首先在GOBIN目录下,其次是GOPATH/bin。该命令的主要功能是用来下载第三方公开的一些命令行工具,得益于 go 语言的编译速度和可移植性,并不需要下载二进制文件,而是直接下载源代码然后在本地进行编译。
$ go install -h
usage: go install [build flags] [packages]
Run 'go help install' for details.install命令接收构建标志和包名作为参数,在 gomod 开启的情况下,包名必须携带版本号。例如下载 delve 调试器
$ go install -x github.com/go-delve/delve/cmd/dlv@latest
# get https://goproxy.cn/github.com/@v/list
# get https://goproxy.cn/github.com/go-delve/delve/cmd/@v/list
# get https://goproxy.cn/github.com/go-delve/delve/cmd/dlv/@v/list
# get https://goproxy.cn/github.com/go-delve/delve/@v/list
# get https://goproxy.cn/github.com/go-delve/@v/list
# get https://goproxy.cn/github.com/@v/list: 404 Not Found (0.014s)
# get https://goproxy.cn/github.com/go-delve/delve/cmd/@v/list: 404 Not Found (0.027s)
# get https://goproxy.cn/github.com/go-delve/delve/cmd/dlv/@v/list: 404 Not Found (0.027s)
# get https://goproxy.cn/github.com/go-delve/delve/@v/list: 200 OK (0.027s)
# get https://goproxy.cn/github.com/go-delve/@v/list: 404 Not Found (0.027s)
WORK=/home/user/tmp/go-build2033992495
mkdir -p $WORK/b001/
cat >/home/user/tmp/go-build2033992495/b001/importcfg.link << 'EOF' # internal
packagefile github.com/go-delve/delve/cmd/dlv=/home/user/.cache/go-build/f1/f11d552287458c0fce625abe50bf928c487064c36bbb1251ad8b1968772c3e4b-d
......
......
mkdir -p /home/wyh/gomod/bin/
mv $WORK/b001/exe/a.out /home/wyh/gomod/bin/dlv
rm -r $WORK/b001/它首先会将源代码下载到GOMODCACHE所存放的路径,这一点跟get命令一致,然后切换到临时工作目录,对其进行编译,编译完成后将二进制文件移动到GOPATH/bin目录下,最后再删除临时文件夹。install命令还有一个限制就是下载的包必须是该项目的入口包,也就是说必须要包含main.go入口文件,否则的话会提示你无法安装。例如,使用go install下载 gin
$ go install -x github.com/gin-gonic/gin@latest
# get https://goproxy.cn/github.com/@v/list
# get https://goproxy.cn/github.com/gin-gonic/gin/@v/list
# get https://goproxy.cn/github.com/gin-gonic/@v/list
# get https://goproxy.cn/github.com/@v/list: 404 Not Found (0.022s)
# get https://goproxy.cn/github.com/gin-gonic/gin/@v/list: 200 OK (0.027s)
# get https://goproxy.cn/github.com/gin-gonic/@v/list: 404 Not Found (0.028s)
package github.com/gin-gonic/gin is not a main packagegin 是一个 web 框架依赖库,并不是一个命令行工具,自然也就没有入口文件,所以也就会安装失败。
list
list命令会列出指定位置的包,一行一个,并且支持自定义格式化输出,支持很多的参数,使用它的前提是必须在一个支持 gomod 的项目内。
$ go list -h
usage: go list [-f format] [-json] [-m] [list flags] [build flags] [packages]
Run 'go help list' for details.它支持的参数如下
-f:格式化参数-json:json 格式输出-compiled:展示所有会被编译器编译的包-deps:展示每一个包及其所依赖的每一个包的名称-test:展示每一个包的测试包-e:遇到错误的包时正常输出-find:不解析这些包的依赖关系-export:使用该参数时,设置结构体Package.Export字段值为包含指定包的最新的导出信息的文件,以及设置Package.BuildID字段值为包的BuildID,主要是格式化输出用。
模块信息参数,
-m:输出模块而不是输出包-versions:展示一个模块所有可用的信息-retracted:展示一个模块的撤回版本
[packages]参数可以是一个指定的包名,或者文件夹,也可以是all,表示任何地方,当使用-m参数时,all表示当前模块引用的所有依赖。
例如,当前文件只有一个main.go文件,且只有一行输出"hello world"的代码,执行go list -deps .后,它输出了从当前项目到fmt及其引用的所有依赖的包。
$ ls
go.mod go.sum main.go
$ cat main.go
package main
import "fmt"
func main() {
fmt.Println("hello world")
}
$ go list -deps .
internal/goarch
unsafe
internal/abi
internal/unsafeheader
internal/cpu
internal/bytealg
internal/coverage/rtcov
internal/godebugs
internal/goexperiment
internal/goos
runtime/internal/atomic
runtime/internal/math
runtime/internal/sys
runtime
......
......
path
io/fs
os
fmt
golearn或者输出当前项目下所有的模块依赖
$ go list -m all
golearn
cloud.google.com/go v0.26.0
github.com/246859/containers v0.0.1
github.com/246859/river v0.1.0 => D:\WorkSpace\Code\riverdb
github.com/BurntSushi/toml v0.3.1
github.com/Jleagle/steam-go v0.0.0-20230725082712-1053b441b1f2
github.com/Jleagle/unmarshal-go v0.0.0-20210227002040-694f544f9265
github.com/KyleBanks/depth v1.2.1
github.com/Microsoft/go-winio v0.6.1
github.com/PuerkitoBio/purell v1.1.1
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578
github.com/andeya/ameda v1.5.3
github.com/andeya/goutil v1.0.1
...format
list命令的输出是以行为单位,每一个行输出都是一个包。官方提供了可以让我们自定义行输出格式的参数-f,它所接受的值也就是template/text模板引擎包所定义的模板语法,例如下面的示例
-f "package {{ .Dir }} {{ .Name }}"每一个迭代的包都将以下面结构体的形式传入,该结构体中的所有字段都可以作为模板参数。
type Package struct {
Dir string // directory containing package sources
ImportPath string // import path of package in dir
ImportComment string // path in import comment on package statement
Name string // package name
Doc string // package documentation string
Target string // install path
Shlib string // the shared library that contains this package (only set when -linkshared)
Goroot bool // is this package in the Go root?
Standard bool // is this package part of the standard Go library?
Stale bool // would 'go install' do anything for this package?
StaleReason string // explanation for Stale==true
Root string // Go root or Go path dir containing this package
ConflictDir string // this directory shadows Dir in $GOPATH
BinaryOnly bool // binary-only package (no longer supported)
ForTest string // package is only for use in named test
Export string // file containing export data (when using -export)
BuildID string // build ID of the compiled package (when using -export)
Module *Module // info about package's containing module, if any (can be nil)
Match []string // command-line patterns matching this package
DepOnly bool // package is only a dependency, not explicitly listed
DefaultGODEBUG string // default GODEBUG setting, for main packages
// Source files
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
CgoFiles []string // .go source files that import "C"
CompiledGoFiles []string // .go files presented to compiler (when using -compiled)
IgnoredGoFiles []string // .go source files ignored due to build constraints
IgnoredOtherFiles []string // non-.go source files ignored due to build constraints
CFiles []string // .c source files
CXXFiles []string // .cc, .cxx and .cpp source files
MFiles []string // .m source files
HFiles []string // .h, .hh, .hpp and .hxx source files
FFiles []string // .f, .F, .for and .f90 Fortran source files
SFiles []string // .s source files
SwigFiles []string // .swig files
SwigCXXFiles []string // .swigcxx files
SysoFiles []string // .syso object files to add to archive
TestGoFiles []string // _test.go files in package
XTestGoFiles []string // _test.go files outside package
// Embedded files
EmbedPatterns []string // //go:embed patterns
EmbedFiles []string // files matched by EmbedPatterns
TestEmbedPatterns []string // //go:embed patterns in TestGoFiles
TestEmbedFiles []string // files matched by TestEmbedPatterns
XTestEmbedPatterns []string // //go:embed patterns in XTestGoFiles
XTestEmbedFiles []string // files matched by XTestEmbedPatterns
// Cgo directives
CgoCFLAGS []string // cgo: flags for C compiler
CgoCPPFLAGS []string // cgo: flags for C preprocessor
CgoCXXFLAGS []string // cgo: flags for C++ compiler
CgoFFLAGS []string // cgo: flags for Fortran compiler
CgoLDFLAGS []string // cgo: flags for linker
CgoPkgConfig []string // cgo: pkg-config names
// Dependency information
Imports []string // import paths used by this package
ImportMap map[string]string // map from source import to ImportPath (identity entries omitted)
Deps []string // all (recursively) imported dependencies
TestImports []string // imports from TestGoFiles
XTestImports []string // imports from XTestGoFiles
// Error information
Incomplete bool // this package or a dependency has an error
Error *PackageError // error loading package
DepsErrors []*PackageError // errors loading dependencies
}
type PackageError struct {
ImportStack []string // shortest path from package named on command line to this one
Pos string // position of error (if present, file:line:col)
Err string // the error itself
}如果迭代的是模块,则以下面结构体的形式传入,它的所有字段也可以作为模板参数。
type Module struct {
Path string // module path
Query string // version query corresponding to this version
Version string // module version
Versions []string // available module versions
Replace *Module // replaced by this module
Time *time.Time // time version was created
Update *Module // available update (with -u)
Main bool // is this the main module?
Indirect bool // module is only indirectly needed by main module
Dir string // directory holding local copy of files, if any
GoMod string // path to go.mod file describing module, if any
GoVersion string // go version used in module
Retracted []string // retraction information, if any (with -retracted or -u)
Deprecated string // deprecation message, if any (with -u)
Error *ModuleError // error loading module
Origin any // provenance of module
Reuse bool // reuse of old module info is safe
}
type ModuleError struct {
Err string // the error itself
}查看所有包
$ go list -f "package {{.Dir}} {{.Name}}" ./...
package /golearn main
package /golearn/app cmd
package /golearn/cmd cmd
package /golearn/docs docs
package /golearn/tool tool
package /golearn/tool_test tool查看模块
$ go list -m -f "mod {{.Path}} {{.Version}} {{.GoVersion}} {{.GoMod}}"
mod golearn 1.21.3 /golearn/go.modmod
go mod是专用于管理 go 模块的命令。
$ go mod help
Go mod provides access to operations on modules.
Note that support for modules is built into all the go commands,
not just 'go mod'. For example, day-to-day adding, removing, upgrading,
and downgrading of dependencies should be done using 'go get'.
See 'go help modules' for an overview of module functionality.
Usage:
go mod <command> [arguments]
The commands are:
download download modules to local cache
edit edit go.mod from tools or scripts
graph print module requirement graph
init initialize new module in current directory
tidy add missing and remove unused modules
vendor make vendored copy of dependencies
verify verify dependencies have expected content
why explain why packages or modules are needed
Use "go help mod <command>" for more information about a command.它有下面几个子命令
download:将go.mod文件中所有标明的依赖下载到本地缓存edit:编辑go.mod文件,它提供的命令行接口主要是提供给其它工具或脚本调用的。init:在当前目录初始化一个 gomod 项目tidy:下载缺失的依赖,删除无用的依赖graph:输出依赖图verify:验证本地的依赖why:解释为什么会依赖这些模块vendor:导出项目依赖到 vendor 目录
init
$ go help mod init
usage: go mod init [module-path]init命令用于初始化一个 gomod 项目,其唯一的参数是模块路径,日后如果别人要下载你的依赖就需要通过此模块路径来作为依据。它的命名规则一般为
domain_name/user_name/repo_name比如一般大家都会把项目放在 github 上,所以可以是
github.com/jack/gotour不太建议使用一些特殊符号作为模块路径。下面看一个使用案例
$ mkdir gotour
$ cd gotour
$ go mod init "github.com/jack/gotour"
go: creating new go.mod: module github.com/jack/gotourtidy
$ go help mod tidy
usage: go mod tidy [-e] [-v] [-x] [-go=version] [-compat=version]tidy命令会清除go.mod中的无用依赖项,也就是没有被引用的依赖项,以及会下载哪些了被引用但是不存在的依赖项。它支持以下参数
-v,输出那些被删除的模块依赖-e,如果过程中发生错误则忽略它继续执行-x,输出执行过程-go=version,更新go.mod文件中的 go 版本-compact=version,保留从指定的主要 Go 版本中所需的任何附加校验和,以便成功加载模块图,并且如果该版本的go命令从不同模块版本中加载任何已导入的包,将导致 tidy 出错。通常很少会用到这个参数,一般在版本版本变更时才会出错,可以前往 stackoverflow 看看这个回答go modules - go mod tidy error message: "but go 1.16 would select" - Stack Overflow
看一个使用例子
$ go mod tidy -v
unused github.com/246859/containers
unused github.com/246859/river
unused github.com/Jleagle/steam-go
unused github.com/Jleagle/unmarshal-go
unused github.com/KyleBanks/depth
unused github.com/Microsoft/go-winio
unused github.com/PuerkitoBio/purell
unused github.com/PuerkitoBio/urlesc
unused github.com/andeya/ameda
unused github.com/andeya/goutil
unused github.com/asaskevich/govalidator
unused github.com/buger/jsonparser
unused github.com/bwmarrin/snowflake
unused github.com/bytedance/go-tagexpr/v2
unused github.com/bytedance/sonic
unused github.com/cespare/xxhash/v2
unused github.com/chenzhuoyu/base64x
......download
$ go help mod download
usage: go mod download [-x] [-json] [-reuse=old.json] [modules]download命令的名称虽然翻译过来叫下载,但它只是把依赖下载到本地的依赖缓存中,不会修改go.mod文件,它的作用是预下载依赖到本地的文件缓存中,如果你想要下载某一个依赖,建议使用go get或者go mod tidy。
下面是几个使用例子
$ go mod download -x gorm.io/gorm
# get https://goproxy.cn/gorm.io/gorm/@v/list
# get https://goproxy.cn/gorm.io/gorm/@v/list: 200 OK (0.084s)如果不带任何参数,它会下载所有存在于go.mod文件中但是又不存在与本地依赖缓存中的依赖项,如果没有需要下载的它会输出
go: no module dependencies to downloadedit
$ go help mod edit
usage: go mod edit [editing flags] [-fmt|-print|-json] [go.mod]edit是一个命令行接口,用于修改go.mod文件,通常是提供给其它程序使用的,一些编辑器 IDE 为提供 gomod 支持就会使用这些命令。它支持下面几个参数
-module,修改模块路径-go=version,修改期望的 go 版本-require=path@version,新增一个依赖项-droprequire=path@version,删除一个依赖项-exclude=path@version,新增一个排除依赖项-dropexclude=path@version,删除一个排除依赖项-replace=old@version=new@version,新增一个替换依赖项-dropreplace=old@version,删除一个替换依赖项-retract=version,新增一个版本回退项-dropretract=version,删除一个版本回退项
还有一些其它用于展示的参数
-print,输出文件内容-json,以 json 格式输出
比如下面这个例子
$ go mod edit -print
module golearn
go 1.21.3
require (
github.com/dstgo/task v1.2.0
github.com/spf13/cast v1.5.1
github.com/swaggo/swag v1.16.2
golang.org/x/net v0.19.0
gorm.io/gorm v1.25.5
)graph
$ go help mod graph
usage: go mod graph [-go=version] [-x]graph命令会输出当前项目下的依赖图,其可读性很差,并且大多数时候也不是给人类阅读的,其结果通常会被处理再以可视化的形式展示。每一行是就是一个依赖,其格式如下
引用方 被引用方比如
golearn go@1.21.3它还支持两个参数
-go=version,使用给定 go 版本加载依赖图,其值不能小于go.mod文件中的版本。-x,展示过程中所执行的命令。
看一个简单的使用例子
$ go mod graph
golearn github.com/246859/containers@v0.0.1
golearn github.com/246859/river@v0.1.0
golearn github.com/Jleagle/steam-go@v0.0.0-20230725082712-1053b441b1f2
golearn github.com/Jleagle/unmarshal-go@v0.0.0-20210227002040-694f544f9265
golearn github.com/KyleBanks/depth@v1.2.1
golearn github.com/Microsoft/go-winio@v0.6.1
golearn github.com/PuerkitoBio/purell@v1.1.1
golearn github.com/PuerkitoBio/urlesc@v0.0.0-20170810143723-de5bf2ad4578
golearn github.com/andeya/ameda@v1.5.3
golearn github.com/andeya/goutil@v1.0.1
golearn github.com/asaskevich/govalidator@v0.0.0-20230301143203-a9d515a09cc2
golearn github.com/buger/jsonparser@v1.1.1
golearn github.com/bwmarrin/snowflake@v0.3.0
golearn github.com/bytedance/go-tagexpr/v2@v2.9.11
......vendor
$ go help mod vendor
usage: go mod vendor [-e] [-v] [-o outdir]vendor 是早期 gomod 没有推出之前的一个 gopath 的替代方案,每一个 go 项目下都会有一个 vendor 目录,按照domain/user/project这种格式单独存放每一个项目的依赖,就像隔壁 nodeJs 臃肿的node_module一样每一个项目的依赖分开放,这种依赖管理方式现在看起来确实很愚蠢,但是在那个时候确实没有更好的方案了,之所以保留 vendor 是因为 go 秉承的向下兼容的承诺,有一些老项目包括 go 源代码里面可能还在使用 vendor。
回到正题,vendor是go mod的一个子命令,它可以将当前模块所引用的全局依赖导出到 vendor 目录中。
$ go mod vendor -h
usage: go mod vendor [-e] [-v] [-o outdir]
Run 'go help mod vendor' for details.它有以下几个参数
-o:指定输出路径文件夹-v:输出每一个依赖-e:出现错误时不退出仍然继续
下面看一个示例,先用go list -m all查看下当前项目所引用的依赖
$ go list -m all
github.com/dstgo/task
github.com/davecgh/go-spew v1.1.1
github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0
github.com/stretchr/objx v0.5.0
github.com/stretchr/testify v1.8.4
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405
gopkg.in/yaml.v3 v3.0.1导出到当前的 vendor 目录下
$ go mod vendor -v -e -o vendor
# github.com/davecgh/go-spew v1.1.1
## explicit
github.com/davecgh/go-spew/spew
# github.com/pkg/errors v0.9.1
## explicit
github.com/pkg/errors
# github.com/pmezard/go-difflib v1.0.0
## explicit
github.com/pmezard/go-difflib/difflib
# github.com/stretchr/testify v1.8.4
## explicit; go 1.20
github.com/stretchr/testify/assert
# gopkg.in/yaml.v3 v3.0.1
## explicit
gopkg.in/yaml.v3导出后的目录结构如下
└─vendor
├─github.com
│ ├─davecgh
│ │ └─go-spew
│ │ └─spew
│ ├─pkg
│ │ └─errors
│ ├─pmezard
│ │ └─go-difflib
│ │ └─difflib
│ └─stretchr
│ └─testify
│ └─assert
└─gopkg.in
| └─yaml.v3
|
|--modules.txt其中的modules.txt是描述所有依赖项的文件,就类似于现在的go.mod。
verify
$ go help mod verify
usage: go mod verify该命令会检查项目的依赖自下载到本地以后是否被修改过。比如,如果没问题就输出all modules verified
$ go mod verify
all modules verified否则它会报告哪里发生了改变,并以非正常状态结束命令。比如
$ go mod verify
gorm.io/gorm v1.25.5: dir has been modified (/go/mod/libs/gorm.io/gorm@v1.25.5)why
$ go help mod why
usage: go mod why [-m] [-vendor] packages...解释为什么这个包被依赖,实际上是输出有关它的依赖图。比如
$ go mod why gorm.io/gorm
# gorm.io/gorm
golearn
gorm.io/gorm默认只会解析从main的导入,加上-m参数可以分析每一个包的导入情况。
work
命令 work 是一个用于 go 多模块管理的本地开发工具
$ go work help
Work provides access to operations on workspaces.
Note that support for workspaces is built into many other commands, not
just 'go work'.
The commands are:
edit edit go.work from tools or scripts
init initialize workspace file
sync sync workspace build list to modules
use add modules to workspace file
vendor make vendored copy of dependencies
Use "go help work <command>" for more information about a command.init
init子命令用于初始化一个 workspace,该命令会创建一个名为go.work的文件
$ go work init -h
usage: go work init [moddirs]
Run 'go help work init' for details.接收参数[moddirs]指定将哪些模块纳入管理,例如
$ go work init ./service ./apiuse
use子命令用于向go.work中添加纳入管理的模块目录
$ go help work use
usage: go work use [-r] [moddirs]
Use provides a command-line interface for adding
directories, optionally recursively, to a go.work file.接收[moddirs]作为参数,还有一个-r表示在[moddirs]路径下递归搜索模块,例如
$ go work use -r ./oss-api ./multi_modulesedit
edit子命令的作用同go mod edit,都是留给命令行接口给其它工具和脚本操作的。
$ go help work edit
usage: go work edit [editing flags] [go.work]
Edit provides a command-line interface for editing go.work,
for use primarily by tools or scripts. It only reads go.work;
it does not look up information about the modules involved.
If no file is specified, Edit looks for a go.work file in the current
directory and its parent directories参数有如下
-fmt,格式化go.work文件-use,-dropuse,添加和移除模块路径-replace=old[@v]=new[@v],-dropreplace=old[@v]=new[@v],用于添加和移除要替换的模块-go,-toolchain=name,指定 go 版本,以及指定要使用的工具链-print,将最后的修改打印出来,不写回文件-json,以json格式输出,无法与-print同时存在,对应类型结构如下所示gotype GoWork struct { Go string Toolchain string Use []Use Replace []Replace } type Use struct { DiskPath string ModulePath string } type Replace struct { Old Module New Module } type Module struct { Path string Version string }
一些使用示例如下,格式化输出
$ go work edit -fmt -print
go 1.22.0
use (
./ab/cd
./auth
./user
)json 输出
$ go work edit -fmt -json
{
"Go": "1.22.0",
"Use": [
{
"DiskPath": "./ab/cd"
},
{
"DiskPath": "./auth"
},
{
"DiskPath": "./user"
}
],
"Replace": null
}sync
sync子命令用于将go.work中的模块列表回到 workspace 中的各个模块中。
$ go help work sync
usage: go work sync
Sync syncs the workspace's build list back to the
workspace's modules这个过程主要发生在本地开发完成后,各个模块已经完成发版工作,此时使用sync,它会根据各个模块的依赖关系来更新 worksapce 所有模块的go.mod中的依赖,从而不需要我们去手动更新。
vendor
vendor命令会将 workspace 中所有模块依赖的库做一份复制到vendor目录下。
$ go work help vendor
usage: go work vendor [-e] [-v] [-o outdir]功能同go mod vendor,不再做过多的赘述。
vet
命令vet是一个 go 语言源代码的静态错误检查工具,就像其它语言的 lint 工具,比如Eslint。
$ go vet -h
usage: go vet [build flags] [-vettool prog] [vet flags] [packages]
Run 'go help vet' for details.
Run 'go tool vet help' for a full list of flags and analyzers.
Run 'go tool vet -help' for an overview.先来看一个简单的示例,现有如下源代码
$ cat main.go
package main
import "fmt"
func main(){
fmt.Println("hello world!"
}在同级目录下不带任何参数执行go vet
$ go vet
vet: ./main.go:6:28: missing ',' before newline in argument list (and 1 more errors)vet会报告哪个文件哪一行出了什么问题。它支持构建标志作为参数,例如-n和-x,支持包,文件夹,文件名作为参数。
$ go vet .
$ go vet main.go
$ go vet ./cmd
$ go vet runtime通过如下命令查看其更详细的参数和解释。
$ go tool vet help
vet is a tool for static analysis of Go programs.
vet examines Go source code and reports suspicious constructs,
such as Printf calls whose arguments do not align with the format
string. It uses heuristics that do not guarantee all reports are
genuine problems, but it can find errors not caught by the compilers.
Registered analyzers:
asmdecl report mismatches between assembly files and Go declarations
assign check for useless assignments
atomic check for common mistakes using the sync/atomic package
bools check for common mistakes involving boolean operators
buildtag check //go:build and // +build directives
......go tool vet命令并不能直接用来对代码进行检查,应该使用go vet。go vet参数中的[vet flag]支持设置代码分析器,可用的值如下
asmdecl 检查汇编文件是否与go声明不匹配
assign 检查是否有无用的变量
atomic 检查使用sync/atomic时是否破坏了原子性
bools 检查是否错误使用逻辑运算符
buildtag 检查build tag
cgocall 检查违反cgao指针传递规则的行为
composites 检查未初始化的复合结构,比如map,chan
copylocks 检查是否发生了锁的值复制
directive 检查go工具链指令
errorsas 检查是否向errors.As传递非指针类型或非error类型的值
framepointer 检查编译优化后的汇编代码是否在保存帧指针之前对其进行清除
httpresponse 检查是否错误使用httpresponse
ifaceassert 检查接口到接口的类型断言
loopclosure 循环变量的引用问题
lostcancel context.WithCancel没有使用cancel函数
nilfunc 检查函数和nil之间是否存在无用的比较
printf 检查printf的格式化参数是否正确
shift 检查是否有等于或超过整数宽度的移位
sigchanyzer 检查无缓冲的chan os.Signal
slog 检查不合法的结构化日志调用
stdmethods 检查已知接口方法的签名是否正确
stringintconv 检查字符串整型转换
structtag 检查结构体tag是否正确
testinggoroutine 检查是否在测试中使用协程调用testing.Fatal
tests 检查测试和示例的常见错误用法
timeformat 使用(time.Time).Format 或 time.Parse的时间格式是否正确
unmarshal 向unmarshal传递非指针或非接口类型
unreachable 检查不可到达的代码
unsafeptr 检查uintptr到unsafe.Pointer不正确转换
unusedresult 检查未使用的函数返回值它们都是针对某一个点进行分析的分析器,比如timeformat分析器是检查time.Format的调用格式是否符合正确的语法。在默认情况下以上所有的分析器都会启用,单独启用可用使用如下的格式
$ go vet -timeformat main.go单独禁用
$ go vet -timeformat=false main.go这些分析器的源代码位于cmd/vendor/golang.org/x/tools/go/analysis/passes,每一个分析器都是 go 语言容易犯的一个坑,所以十分建议使用vet命令来检查你的代码。除此这些之外,它还支持一些其它的标志参数
-V,仅打印版本然后退出-json,以 json 形式输出-c=n,显示上下文中指定数目的冲突行(似乎并没有任何作用)
还有一些外置的分析器,比如shadows,它负责检测短变量命名的变量隐藏问题,由于是外置的所以需要用go install来进行下载
$ go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest使用格式如下
$ go vet -vettool=$(which shadow)test
$ go test -h
usage: go test [build/test flags] [packages] [build/test flags & test binary flags]
Run 'go help test' and 'go help testflag' for details.test命令是 go 语言工具链中提供测试功能的命令,这个功能相当的重要,对于一个软件而言,完善的测试的是必不可少的。这里只是简单的介绍下如何使用test命令,如果要了解更多测试相关,前往:测试
它除了支持build命令的编译参数之外,test还支持下面几个参数
-args,程序的入口参数-c,编译当前包的测试二进制文件到当前目录但并不执行,以pkg.test方式命名-exec,在测试开始之前执行一些其它的命令-json,测试的输出风格变为 json-o,指定测试二进制文件的路径名
它还支持许多testflag ,使用help命令查看所有testflag
$ go help testflag
The 'go test' command takes both flags that apply to 'go test' itself
and flags that apply to the resulting test binary.
The following flags are recognized by the 'go test' command and
control the execution of any test:
-bench regexp
-benchtime t
-count n
......介绍几个常用的
-v,输出每一个用例的测试结果。-timeout duration,测试执行超时时间-skip regexp,跳过指定的测试用例-short,让哪些运行时间很长的测试用例缩短运行时间-shuffle,打乱所有测试用例的执行顺序-run regexp,运行指定的测试用例-list regexp,列出每一个测试用例-cpu 1,2,4,指定 cpu 数量-count n,指定每个测试用例执行多少次
最简单的用法就是,不带任何参数,它会执行当前所在包下的所有测试用例,并输出结果。
$ ls *_test.go
hello_test.go
$ go test
PASS
ok golearn 0.522s指定某一个测试文件
$ go test hello_test.go
ok command-line-arguments 0.041s加上-v参数可以查看更详细的输出,它相当常用。
$ go test -v hello_test.go
=== RUN TestHello
hello_test.go:6: hello world!
--- PASS: TestHello (0.00s)
PASS
ok command-line-arguments 0.041s指定某一个测试用例
$ go test -v -run TestHello
=== RUN TestHello
hello_test.go:6: hello world!
--- PASS: TestHello (0.00s)
PASS
ok golearn 0.028s在测试时,test命令分两种模式,先来讲第一种文件夹模式,当不带package参数执行test命令时,它就会以文件夹模式执行测试,比如下面几个命令
$ go test
$ go test -v在这种模式下,禁用测试缓存。另一种模式是列表模式,当package参数不为空时,就会以列表模式进行测试,它与前者的区别就是是否开启测试缓存。比如下面几个
$ go test -v .
$ go test -v ./...
$ go test .
$ go test -v net/http在列表模式下,go 会将指定包下的每一个包的测试文件编译成二进制文件并执行,为了避免重复运行测试,go 默认会将结果缓存,二次运行时不会重新编译。使用下列参数时将会默认开启缓存
-benchtime-cpu-list-parallel-runshort-timeout-failfast-v
使用除了这些参数之外的其它参数就可以关闭缓存,官方提倡的方法是使用-count=1的方式来禁用缓存。比如
$ go test -v -count=1 ./...指令
与命令不同,go 的指令是以硬编码的形式存在于源文件中的,它们有另一个比较术语化的名字:编译指示(progma directives)。
编译器和链接器会因为它们改变自身的行为从而达到控制编译的效果,就有点类似于 c 语言中的宏,当然并非所有指令都是用来影响编译的,部分用于其它功能性行为,比如generate指令通常用于代码生成的功能。这些指令通常以注释的形式存在,并且以//go:作为前缀,中间不能包含任何的空格,比如//go:generate指令。所有指令类型总共分为两种
- 功能性指令,这类是 go 提供的功能性指令可以随意使用,比如
generate,embed,build。 - 编译器指令,这类指令需要谨慎使用,胡乱使用可能导致无法预测的结果。
除了功能性指令外,大多数指令都只能作用于函数签名上。对于编译器指令可以执行命令go doc compile查看其指令。对于全部指令,可以在cmd/compile/internal/ir/node.go: 440找到有关它们的信息。
generate
$ go help generate
usage: go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]generate指令顾名思义就是跟生成有关的,通常它的作用是用于执行那些会生成代码以及更新源代码的命令,不过实际上它可以执行任何命令。并且,generate指令与其它指令不同,它有一个专门的命令可以用于执行所有位于源文件中的 generate 指令。它可以以文件名或者包名来作为输入参数来表示执行哪些文件的generate指令,下面是它的其它参数。
-run=regex,运行指定的 generate 指令-skip=regex,跳过指定的 generate 指令-n,打印将要执行的命令-x,打印过程中执行的命令-v,输出处理的文件
除此之外,在generate指令中执行的命令还支持以下几个内置参数
$GOARCH,cpu 架构$GOOS,操作系统$GOFILE,文件名$GOLINE,行号$GOPACKAGE,包名$GOROOT,go root$DOLLAR,美元符号$PATH,path 环境变量
看个例子,什么代码都没有只有一行注释
package main
//go:generate echo "hello world!"执行命令
$ go generate .
hello world!这个例子是执行 go 命令
package main
//go:generate go version执行命令
$ go generate .
go version go1.21.3 windows/amd64generate指令可以用于执行任何命令,比如 swagger 生成 API 文档,或者 Wire 生成 IOC 代码。不过这个指令不适合执行特别复杂的命令,它适合执行简短的命令,如果有复杂的需求可以使用脚本或者 makefile 来代替。
embed
embed指令是 1.16 新增的,它的作用是将可以将静态文件一同打包进二进制文件中,比如 HTML 模板之类的。它的格式如下
//go:embed patternpattern可以是 glob 表达式,也可以是文件夹或者某一个具体文件。看一个例子
package main
import "embed"
//go:embed *
var static embed.FSembed指令要求必须位于一个类型为embed.Fs的全局变量上方,注意必须是全局变量,并且使用它必须导入embed包,在这个例子中,*代表了会将当前文件夹下的所有文件都打包进二进制文件中,不过它不会允许.开头的文件夹存在。
下面这个例子展示了从嵌入的文件中读取内容
package main
import (
"embed"
"fmt"
)
//go:embed *.txt
var static embed.FS
func main() {
bytes, err := static.ReadFile("hello.txt")
if err != nil {
panic(err)
}
fmt.Println(string(bytes))
}它只有三个方法,使用起来跟平常文件系统没什么区别,并且由于它实现了io/Fs接口,所以也可以作为Fs对象来进行传递。
func (f FS) Open(name string) (fs.File, error)
func (f FS) ReadFile(name string) ([]byte, error)
func (f FS) ReadDir(name string) ([]fs.DirEntry, error)下面这个例子展示了通过embed指令嵌入 html 文件,并通过 http 服务访问。
package main
import (
"embed"
"net/http"
)
//go:embed index.html
var htmlFs embed.FS
func main() {
http.Handle("/", http.FileServer(http.FS(htmlFs)))
http.ListenAndServe(":8080", http.DefaultServeMux)
}访问结果如下
$ curl -s -GET 127.0.0.1:8080
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello world!</title>
</head>
<body>
<H1>Hello World!</H1>
<script>
alert("hello world!");
</script>
</body>
</html>embed指令还支持全局变量的类型可以为[]byte,比如下面这个例子
package main
import (
_ "embed"
"net/http"
)
//go:embed index.html
var rawdata []byte
func main() {
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
writer.Write(rawdata)
})
http.ListenAndServe(":8080", http.DefaultServeMux)
}它实现的效果跟上一个例子是差不多的。
$ curl -s -GET 127.0.0.1:8080
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello world!</title>
</head>
<body>
<H1>Hello World!</H1>
<script>
alert("hello world!");
</script>
</body>
</html>build
在build-编译控制部分,讲到了用// +build指令来控制编译行为。而//go:build指令是在 1.17 新出的,意在取代先前的指令,不过现在都 1.21 也还没有取代,估计以后会以共存的方式存在,关于这个新指令,官方文档也有介绍:build constraints。它的功能与前者没什么区别,但是语法更加严格,支持布尔表达式,看一个例子
//go:build (linux && 386) || (darwin && !cgo)
package pkg_name这种方式可读性要比原来那种高得多。
line
line指令会影响其下一行的行号,列号,已经文件名,它的作用仅限于此,大部分时候可能会用来调试错误之类的。比如在发生错误时,会改变编译器输出的信息。
package main
var a undefinedType
func main() {
}正常情况下,编译器会输出
.\main.go:3:7: undefined: undefinedType但如果使用了line指令,就不一样了
package main
//line abc.go:10:100
var a undefinedType
func main() {
}那么它的输出就是
abc.go:10:106: undefined: undefinedType并且因为历史遗留原因,line指令也是唯一一个用法跟其它指令不同的指令。它的格式是
//line filename:line:column可以看到它并不需要go:作为前缀。
linkname
这个指令的操作可以用于链接其它包的函数或者全局变量,即便它是私有类型,这种操作经常在标准库尤其是runtime中出现,有一些函数没有函数体就是通过这种方式来实现的,另一部分空函数体的函数则是由汇编实现。来看下它的用法,使用格式如下
//go:linkname 链接类型名称 被链接的类型并且在使用之前,比如导入unsafe包。看一个简单的链接标准库中的私有类型的例子
import (
"fmt"
"unsafe"
)
//go:linkname memhash runtime.memhash
func memhash(p unsafe.Pointer, h, s uintptr) uintptr
func MemHash(data []byte) uint64 {
ptr := unsafe.Pointer(unsafe.SliceData(data))
return uint64(memhash(ptr, 0, uintptr(len(data))))
}
func main() {
fmt.Println(MemHash([]byte("hello")))
}输出
15395306441938000233它将runtime.memhash这个私有函数与我们自己声明的函数链接到了一起,这个函数没有函数体只有一个签名,只起到一个载体的作用。memhash的作用是给定一个指针,哈希种子,和内存偏移量,根据内存来计算哈希值。这个链接过程在编译期完成,
如果不是标准库的,那么情况就有些不一样了,比如在example包下有一个test函数,在链接之前首先要匿名导入这个包。
package example
// 一个私有类型,外界无法访问。
func test() string {
return "a"
}package main
import (
"fmt"
_ "golearn/example"
_ "unsafe"
)
//go:linkname test golearn/example.test
func test() string
func main() {
fmt.Println(test())
}output:
aAs you can see, the linking succeeded. This approach can bypass Go’s module system and do whatever you want, but it is not recommended for large-scale use unless you know exactly what you are doing.
noinline
The noinline directive indicates that a function should not be inlined by the optimizer, even if it is very simple. Let's look at a simple example
package main
func val() string {
return "val"
}
func main() {
var c = val()
_ = c
}val is a very simple function that returns a string literal. Since it is so simple and its result is always predictable, the compiler will optimize it into the following form at compile time:
package main
func main() {
var c = "val"
_ = c
}To see the assembly code, let's look at the output of the compiler. You can see that there is no call to the val function in the assembly code.
TEXT main.val(SB), NOSPLIT|NOFRAME|ABIInternal, $0-0
FUNCDATA $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
FUNCDATA $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
LEAQ go:string."val"(SB), AX
MOVL $3, BX
FUNCDATA $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
RETNext, let's add the noinline directive to the val function.
package main
//go:noinline
func val() string {
return "val"
}
func main() {
var c = val()
_ = c
}To see the assembly code, let's look at the output of the compiler. You can see that there is a call to the val function in the assembly code.
CMPQ SP, 16(R14)
PCDATA $0, $-2
JLS 17
PCDATA $0, $-1
PUSHQ BP
MOVQ SP, BP
FUNCDATA $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
FUNCDATA $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
PCDATA $1, $0
CALL main.val(SB)
POPQ BP
RETThis time you can clearly see the call to main.val, which is exactly what the noinline directive does—it prevents the compiler from inlining the function during optimization.
nospilit
The nospilit directive indicates that a function should not have its stack split. Since Go's concurrency model is preemptive, it is often necessary to mark a function as nospilit if it will run low-level code that should not be interrupted.
//go:nosplit
func nospilitFn()Using the nospilit directive ensures that the function will not have its stack split, which can prevent stack overflow errors.
noescape
noescape, as its name suggests, is related to escape analysis. It indicates that the current function will not cause any memory to escape to the heap; all resources are reclaimed after execution. The function must have only a signature and no body, with its implementation typically provided in assembly.
For example, the memhash function uses the noescape directive to ensure that it does not cause any memory to escape to the heap.
//go:noescape
//go:linkname memhash runtime.memhash
func memhash(p unsafe.Pointer, h, s uintptr) uintptrBy using the noescape directive, the compiler will not perform escape analysis on the function, which can improve performance. However, you must ensure that the function does not cause any memory to escape; otherwise, you may run into undefined behavior.
uintptrescapes
The uintptrescapes directive indicates that a function will convert uintptr type parameters to pointer values and escape to the heap. It is typically used for low-level system calls, and most cases do not require explicit use of this directive.
//go:uintptrescapes
func nospilit(ptr uintptr) uintptrIn previous versions, there was also a notinheaps directive to prevent types from allocating memory on the heap. However, this directive has been removed in recent versions.
norace
The norace directive indicates that a function's memory accesses do not require race analysis. It is typically used when running low-level code that is not suitable for race analysis.
//go:norace
func low_level_code(ptr uintptr) uintptrTIP
Some directives are restricted to use only within the runtime package and cannot be used externally. They involve deeper mechanisms; for details, see Runtime-only compiler directives.
