【Golang】Golang 命令

Posted by 西维蜀黍 on 2020-01-30, Last Modified on 2021-09-21

Golang 命令

$ go
Go is a tool for managing Go source code.

Usage:

	go <command> [arguments]

The commands are:

	bug         start a bug report
	build       compile packages and dependencies
	clean       remove object files and cached files
	doc         show documentation for package or symbol
	env         print Go environment information
	fix         update packages to use new APIs
	fmt         gofmt (reformat) package sources
	generate    generate Go files by processing source
	get         add dependencies to current module and install them
	install     compile and install packages and dependencies
	list        list packages or modules
	mod         module maintenance
	run         compile and run Go program
	test        test packages
	tool        run specified go tool
	version     print Go version
	vet         report likely mistakes in packages

Use "go help <command>" for more information about a command.

Additional help topics:

	buildmode   build modes
	c           calling between Go and C
	cache       build and test caching
	environment environment variables
	filetype    file types
	go.mod      the go.mod file
	gopath      GOPATH environment variable
	gopath-get  legacy GOPATH go get
	goproxy     module proxy protocol
	importpath  import path syntax
	modules     modules, module versions, and more
	module-get  module-aware go get
	module-auth module authentication using go.sum
	module-private module configuration for non-public modules
	packages    package lists and patterns
	testflag    testing flags
	testfunc    testing functions

Use "go help <topic>" for more information about that topic.

go run - compile and run Go program

$ go help run
usage: go run [build flags] [-exec xprog] package [arguments...]

Run compiles and runs the named main Go package.
Typically the package is specified as a list of .go source files from a single directory,
but it may also be an import path, file system path, or pattern
matching a single known package, as in 'go run .' or 'go run my/cmd'.

By default, 'go run' runs the compiled binary directly: 'a.out arguments...'.
If the -exec flag is given, 'go run' invokes the binary using xprog:
	'xprog a.out arguments...'.
If the -exec flag is not given, GOOS or GOARCH is different from the system
default, and a program named go_$GOOS_$GOARCH_exec can be found
on the current search path, 'go run' invokes the binary using that program,
for example 'go_nacl_386_exec a.out arguments...'. This allows execution of
cross-compiled programs when a simulator or other execution method is
available.

The exit status of Run is not the exit status of the compiled binary.

For more about build flags, see 'go help build'.
For more about specifying packages, see 'go help packages'.

See also: go build.

专门用来运行命令源码文件的命令,注意,这个命令不是用来运行所有 Go 的源码文件的!

go run 命令只能接受一个命令源码文件以及若干个库源码文件(必须同属于 main 包)作为文件参数,且不能接受测试源码文件。它在执行时会检查源码文件的类型。如果参数中有多个或者没有命令源码文件,那么 go run 命令就只会打印错误提示信息并退出,而不会继续执行。

go run 命令究竟做了些什么

这个命令具体干了些什么事情呢?来分析分析:

$ go run -n client.go

#
# command-line-arguments
#

mkdir -p $WORK/b001/
cat >$WORK/b001/_gomod_.go << 'EOF' # internal
...
/usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b001/_pkg_.a -trimpath "$WORK/b001=>" -p main -lang=go1.13 -complete -buildid fm10K7IB67jgZlhQ5l69/fm10K7IB67jgZlhQ5l69 -dwarf=false -goversion go1.13 -D _/Users/weishi/Working/GoPlayground -importcfg $WORK/b001/importcfg -pack -c=4 ./client.go $WORK/b001/_gomod_.go
...
mkdir -p $WORK/b001/exe/
cd .
/usr/local/go/pkg/tool/darwin_amd64/link -o $WORK/b001/exe/client -importcfg $WORK/b001/importcfg.link -s -w -buildmode=exe -buildid=5bRkAVWd8EsLfZRj5SA1/fm10K7IB67jgZlhQ5l69/fm10K7IB67jgZlhQ5l69/5bRkAVWd8EsLfZRj5SA1 -extld=clang $WORK/b001/_pkg_.a
$WORK/b001/exe/client

这里可以看到创建了一个临时文件夹 $WORK/b001/,先执行了 compile 命令,然后 link,生成了归档文件.a 和 最终可执行文件,最终的可执行文件放在 exe 文件夹里面。命令的最后一步就是执行了可执行文件。

生成的临时文件可以用 go run -work 看到,比如当前生成的临时文件夹是如下的路径:

$ go run -work client.go
WORK=/var/folders/42/27cgq8k50hsc48kdnpwpt4080000gn/T/go-build099278625
$ cd /var/folders/42/27cgq8k50hsc48kdnpwpt4080000gn/T/go-build099278625
$ tree
.
└── b001
    ├── exe
    │   └── client
    └── importcfg.link

2 directories, 2 files

可以看到,最终go run命令是生成了2个文件,一个是importcfg.link,一个是可执行文件。

go run 命令在第二次执行的时候,如果发现导入的代码包没有发生变化,那么 go run 不会再次编译这个导入的代码包。直接静态链接进来。

$ go run -a

加上-a的标记可以强制编译所有的代码,即使归档文件.a存在,也会重新编译。

如果嫌弃编译速度慢,可以加上-p n,这个是并行编译,n是并行的数量。n一般为逻辑 CPU 的个数。

go build - compile packages and dependencies

  go build是我们非常常用的命令,它可以启动编译,把我们的包和相关的依赖编译成一个可执行的文件。

$ go help build
usage: go build [-o output] [-i] [build flags] [packages]

Build compiles the packages named by the import paths,
along with their dependencies, but it does not install the results.

If the arguments to build are a list of .go files from a single directory,
build treats them as a list of source files specifying a single package.

When compiling packages, build ignores files that end in '_test.go'.

When compiling a single main package, build writes
the resulting executable to an output file named after
the first source file ('go build ed.go rx.go' writes 'ed' or 'ed.exe')
or the source code directory ('go build unix/sam' writes 'sam' or 'sam.exe').
The '.exe' suffix is added when writing a Windows executable.

When compiling multiple packages or a single non-main package,
build compiles the packages but discards the resulting object,
serving only as a check that the packages can be built.

The -o flag forces build to write the resulting executable or object
to the named output file or directory, instead of the default behavior described
in the last two paragraphs. If the named output is a directory that exists,
then any resulting executables will be written to that directory.

The -i flag installs the packages that are dependencies of the target.

The build flags are shared by the build, clean, get, install, list, run,
and test commands:

	-a
		force rebuilding of packages that are already up-to-date.
	-n
		print the commands but do not run them.
	-p n
		the number of programs, such as build commands or
		test binaries, that can be run in parallel.
		The default is the number of CPUs available.
	-race
		enable data race detection.
		Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64.
	-msan
		enable interoperation with memory sanitizer.
		Supported only on linux/amd64, linux/arm64
		and only with Clang/LLVM as the host C compiler.
	-v
		print the names of packages as they are compiled.
	-work
		print the name of the temporary work directory and
		do not delete it when exiting.
	-x
		print the commands.

	-asmflags '[pattern=]arg list'
		arguments to pass on each go tool asm invocation.
	-buildmode mode
		build mode to use. See 'go help buildmode' for more.
	-compiler name
		name of compiler to use, as in runtime.Compiler (gccgo or gc).
	-gccgoflags '[pattern=]arg list'
		arguments to pass on each gccgo compiler/linker invocation.
	-gcflags '[pattern=]arg list'
		arguments to pass on each go tool compile invocation.
	-installsuffix suffix
		a suffix to use in the name of the package installation directory,
		in order to keep output separate from default builds.
		If using the -race flag, the install suffix is automatically set to race
		or, if set explicitly, has _race appended to it. Likewise for the -msan
		flag. Using a -buildmode option that requires non-default compile flags
		has a similar effect.
	-ldflags '[pattern=]arg list'
		arguments to pass on each go tool link invocation.
	-linkshared
		link against shared libraries previously created with
		-buildmode=shared.
	-mod mode
		module download mode to use: readonly or vendor.
		See 'go help modules' for more.
	-pkgdir dir
		install and load all packages from dir instead of the usual locations.
		For example, when building with a non-standard configuration,
		use -pkgdir to keep generated packages in a separate location.
	-tags tag,list
		a comma-separated list of build tags to consider satisfied during the
		build. For more information about build tags, see the description of
		build constraints in the documentation for the go/build package.
		(Earlier versions of Go used a space-separated list, and that form
		is deprecated but still recognized.)
	-trimpath
		remove all file system paths from the resulting executable.
		Instead of absolute file system paths, the recorded file names
		will begin with either "go" (for the standard library),
		or a module path@version (when using modules),
		or a plain import path (when using GOPATH).
	-toolexec 'cmd args'
		a program to use to invoke toolchain programs like vet and asm.
		For example, instead of running asm, the go command will run
		'cmd args /path/to/asm <arguments for asm>'.

The -asmflags, -gccgoflags, -gcflags, and -ldflags flags accept a
space-separated list of arguments to pass to an underlying tool
during the build. To embed spaces in an element in the list, surround
it with either single or double quotes. The argument list may be
preceded by a package pattern and an equal sign, which restricts
the use of that argument list to the building of packages matching
that pattern (see 'go help packages' for a description of package
patterns). Without a pattern, the argument list applies only to the
packages named on the command line. The flags may be repeated
with different patterns in order to specify different arguments for
different sets of packages. If a package matches patterns given in
multiple flags, the latest match on the command line wins.
For example, 'go build -gcflags=-S fmt' prints the disassembly
only for package fmt, while 'go build -gcflags=all=-S fmt'
prints the disassembly for fmt and all its dependencies.

For more about specifying packages, see 'go help packages'.
For more about where packages and binaries are installed,
run 'go help gopath'.
For more about calling between Go and C/C++, run 'go help c'.

Note: Build adheres to certain conventions such as those described
by 'go help gopath'. Not all projects can follow these conventions,
however. Installations that have their own conventions or that use
a separate software build system may choose to use lower-level
invocations such as 'go tool compile' and 'go tool link' to avoid
some of the overheads and design decisions of the build tool.

See also: go install, go get, go clean.

当代码包中**有且仅有一个命令源码文件(包含main 函数)**的时候,在文件夹所在目录中执行 go build 命令,会在该目录下生成一个与该目录同名的可执行文件。

# 假设当前文件夹名叫 myGoRepo
$ ~/helloworld/src/myGoRepo $ ls
helloworld.go
$ ~/helloworld/src/myGoRepo $ go build
$ ~/helloworld/src/myGoRepo $ ls
helloworld.go  myGoRepo

于是在当前目录直接生成了以当前文件夹为名的可执行文件( 在 Mac 平台下是 Unix executable 文件,在 Windows 平台下是 exe 文件)

但是这种情况下,如果使用 go install 命令,如果 GOPATH 里面只有一个工作区,就会在当前工作区的 bin 目录下生成相应的可执行文件。如果 GOPATH 下有多个工作区,则是在 GOBIN 下生成对应的可执行文件。

go build 用于编译我们指定的源码文件或代码包以及它们的依赖包。,但是注意如果用来编译非命令源码文件,即库源码文件,go build 执行完是不会产生任何结果的。这种情况下,go build 命令只是检查库源码文件的有效性,只会做检查性的编译,而不会输出任何结果文件。

go build 编译命令源码文件,则会在该命令的执行目录中生成一个可执行文件,上面的例子也印证了这个过程。

go build 后面不追加目录路径的话,它就把当前目录作为代码包并进行编译。go build 命令后面如果跟了代码包导入路径作为参数,那么该代码包及其依赖都会被编译。

-a - 强行对所有涉及到的代码包(包含标准库中的代码包)进行重新构建,即使它们已经是最新的了

-n - 打印编译期间所用到的其它命令,但是并不真正执行它们

-o - 定生成二进制文件的名称

# 指定生成二进制文件的名称
$ go build -o hello hello.go

go build ./... - build 所有包含main 函数的文件

# 编译
$ go build ./...

# 我们可以看看这个命令在底层做了什么
$ nohup go build -n ./... > test.log 2>&1 &

-n - go build 命令究竟做了些什么

命令源码文件

go build 命令究竟做了些什么呢?

我们来打印一下每一步的执行过程。先看看命令源码文件执行了 go build 干了什么事情。

我们用一个最简单的命令源码文件:

package main

import "fmt"

func main() {
	var s2 []int  = make([]int, 3)
	fmt.Printf("%#v", s2)
}
$ go build -n client.go
mkdir -p $WORK/b001/

# 把所以每个 Golang binary compilation 要用到的二进制文件列出来(fmt=/usr/local/go/pkg/darwin_amd64/fmt.a),除此之外,还包括 fmt=/usr/local/go/pkg/darwin_amd64/fmt.a,这是唯一一个本 demo 中显式依赖的库
cat >$WORK/b001/importcfg.link << 'EOF' # internal
packagefile command-line-arguments=/Users/weishi/Library/Caches/go-build/2c/2c4ba4148db029901d0d93b84e9e66c0b24f0638ed1b2cc915083046153e1962-d
packagefile fmt=/usr/local/go/pkg/darwin_amd64/fmt.a
packagefile runtime=/usr/local/go/pkg/darwin_amd64/runtime.a
packagefile errors=/usr/local/go/pkg/darwin_amd64/errors.a
packagefile internal/fmtsort=/usr/local/go/pkg/darwin_amd64/internal/fmtsort.a
packagefile io=/usr/local/go/pkg/darwin_amd64/io.a
packagefile math=/usr/local/go/pkg/darwin_amd64/math.a
packagefile os=/usr/local/go/pkg/darwin_amd64/os.a
packagefile reflect=/usr/local/go/pkg/darwin_amd64/reflect.a
packagefile strconv=/usr/local/go/pkg/darwin_amd64/strconv.a
packagefile sync=/usr/local/go/pkg/darwin_amd64/sync.a
packagefile unicode/utf8=/usr/local/go/pkg/darwin_amd64/unicode/utf8.a
packagefile internal/bytealg=/usr/local/go/pkg/darwin_amd64/internal/bytealg.a
packagefile internal/cpu=/usr/local/go/pkg/darwin_amd64/internal/cpu.a
packagefile runtime/internal/atomic=/usr/local/go/pkg/darwin_amd64/runtime/internal/atomic.a
packagefile runtime/internal/math=/usr/local/go/pkg/darwin_amd64/runtime/internal/math.a
packagefile runtime/internal/sys=/usr/local/go/pkg/darwin_amd64/runtime/internal/sys.a
packagefile internal/reflectlite=/usr/local/go/pkg/darwin_amd64/internal/reflectlite.a
packagefile sort=/usr/local/go/pkg/darwin_amd64/sort.a
packagefile sync/atomic=/usr/local/go/pkg/darwin_amd64/sync/atomic.a
packagefile math/bits=/usr/local/go/pkg/darwin_amd64/math/bits.a
packagefile internal/oserror=/usr/local/go/pkg/darwin_amd64/internal/oserror.a
packagefile internal/poll=/usr/local/go/pkg/darwin_amd64/internal/poll.a
packagefile internal/syscall/unix=/usr/local/go/pkg/darwin_amd64/internal/syscall/unix.a
packagefile internal/testlog=/usr/local/go/pkg/darwin_amd64/internal/testlog.a
packagefile syscall=/usr/local/go/pkg/darwin_amd64/syscall.a
packagefile time=/usr/local/go/pkg/darwin_amd64/time.a
packagefile unicode=/usr/local/go/pkg/darwin_amd64/unicode.a
packagefile internal/race=/usr/local/go/pkg/darwin_amd64/internal/race.a
EOF
mkdir -p $WORK/b001/exe/
cd .

# link
/usr/local/go/pkg/tool/darwin_amd64/link -o $WORK/b001/exe/a.out -importcfg $WORK/b001/importcfg.link -buildmode=exe -buildid=Mb3smZRf8ih_2eL8pqQz/sq1j5TdmyFiKVZp6qC0Q/Gnz6ykqHlkhQ_pXVvSiL/Mb3smZRf8ih_2eL8pqQz -extld=clang /Users/weishi/Library/Caches/go-build/2c/2c4ba4148db029901d0d93b84e9e66c0b24f0638ed1b2cc915083046153e1962-d
# 编译出基于 darwin_amd64 arch 的机器码
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/exe/a.out # internal
mv $WORK/b001/exe/a.out client

可以看到,执行过程和 go run 大体相同,唯一不同的就是在最后一步,go run 是执行了可执行文件,但是 go build 命令是把可执行文件移动到了当前目录的文件夹中。

$ go build -work client.go
WORK=/var/folders/42/27cgq8k50hsc48kdnpwpt4080000gn/T/go-build476329169
$ cd /var/folders/42/27cgq8k50hsc48kdnpwpt4080000gn/T/go-build476329169
$ tree
.
└── b001
    ├── exe
    └── importcfg.link

库源码文件

再来看看如果对库源码文件执行 go build 会发生什么:

$ go build -n p1/redis_try.go

#
# command-line-arguments
#

mkdir -p $WORK/b001/
cat >$WORK/b001/importcfg << 'EOF' # internal
# import config
packagefile fmt=/usr/local/go/pkg/darwin_amd64/fmt.a
EOF
cd /Users/weishi/Working/GoPlayground/p1
/usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b001/_pkg_.a -trimpath "$WORK/b001=>" -p command-line-arguments -lang=go1.13 -complete -buildid pUImG8ujt5D0rSe8DNSy/pUImG8ujt5D0rSe8DNSy -goversion go1.13 -D _/Users/weishi/Working/GoPlayground/p1 -importcfg $WORK/b001/importcfg -pack -c=4 ./redis_try.go
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/_pkg_.a # internal
$ go build -work client.go
WORK=/var/folders/42/27cgq8k50hsc48kdnpwpt4080000gn/T/go-build789804991
$ cd /var/folders/42/27cgq8k50hsc48kdnpwpt4080000gn/T/go-build789804991
$ tree

➜  go-build789804991 tree
.
└── b001
    ├── _pkg_.a
    └── importcfg

1 directory, 2 files

go install - compile and install packages and dependencies

go install 用于编译并安装指定的代码包及它们的依赖包。当指定的代码包的依赖包还没有被编译和安装时,该命令会先去处理依赖包。

与 go build 命令一样,传给 go install 命令的代码包参数应该以导入路径的形式提供。并且,go build 命令的绝大多数标记也都可以用于go install 命令。实际上,go install 命令只比 go build 命令多做了一件事,即:安装编译后的结果文件到指定目录。

go install 命令究竟做了些什么

go install 命令究竟做了些什么呢?我们来打印一下每一步的执行过程。

命令源码文件

$ go install -n google.golang.org/protobuf/cmd/protoc-gen-go

#
# google.golang.org/protobuf/types/descriptorpb
#
mkdir -p $WORK/b080/
cat >$WORK/b080/importcfg << 'EOF' # internal
...

#
# google.golang.org/protobuf/reflect/protodesc
#
mkdir -p $WORK/b075/
cat >$WORK/b075/importcfg << 'EOF' # internal
...

#
# google.golang.org/protobuf/types/pluginpb
#
mkdir -p $WORK/b092/
cat >$WORK/b092/importcfg << 'EOF' # internal
...

cd /Users/weishi/go/pkg/mod/google.golang.org/protobuf@v1.25.0/compiler/protogen
/usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b041/_pkg_.a -trimpath "$WORK/b041=>" -p google.golang.org/protobuf/compiler/protogen -lang=go1.9 -complete -buildid u13JGRB83k2VpYOEgfhx/u13JGRB83k2VpYOEgfhx -goversion go1.13 -D "" -importcfg $WORK/b041/importcfg -pack -c=4 ./protogen.go
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b041/_pkg_.a # internal

#
# google.golang.org/protobuf/cmd/protoc-gen-go/internal_gengo
#

mkdir -p $WORK/b033/
cat >$WORK/b033/importcfg << 'EOF' # internal
...
cd /Users/weishi/go/pkg/mod/google.golang.org/protobuf@v1.25.0/cmd/protoc-gen-go/internal_gengo
/usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b033/_pkg_.a -trimpath "$WORK/b033=>" -p google.golang.org/protobuf/cmd/protoc-gen-go/internal_gengo -lang=go1.9 -complete -buildid iq2KSfxPXFvSbBXZlkQr/iq2KSfxPXFvSbBXZlkQr -goversion go1.13 -D "" -importcfg $WORK/b033/importcfg -pack -c=4 ./init.go ./main.go ./reflect.go ./well_known_types.go
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b033/_pkg_.a # internal

#
# google.golang.org/protobuf/cmd/protoc-gen-go
#
mkdir -p $WORK/b001/
cat >$WORK/b001/_gomod_.go << 'EOF' # internal
...
cat >$WORK/b001/importcfg << 'EOF' # internal
...
EOF
mkdir -p $WORK/b001/exe/
cd .
/usr/local/go/pkg/tool/darwin_amd64/link -o $WORK/b001/exe/a.out -importcfg $WORK/b001/importcfg.link -buildmode=exe -buildid=vTf21L1llR0zAVLpFEB8/ith4kry68iax_vE4lLXg/ith4kry68iax_vE4lLXg/vTf21L1llR0zAVLpFEB8 -extld=clang $WORK/b001/_pkg_.a
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/exe/a.out # internal
mkdir -p /Users/weishi/go/bin/
mv $WORK/b001/exe/a.out /Users/weishi/go/bin/protoc-gen-go

前面几步依旧和 go run 、go build 完全一致,只是最后一步的差别。

go install 会把命令源码文件安装到当前工作区的 bin 目录(如果 GOPATH 下有多个工作区,就会放在 GOBIN 目录下)。

如果是库源码文件,就会被安装到当前工作区的 pkg 的平台相关目录下。

库源码文件

$ go install -n github.com/go-redis/redis/v8
go: finding github.com/go-redis/redis/v8 v8.0.0-beta.7
go: downloading github.com/go-redis/redis/v8 v8.0.0-beta.7
go: extracting github.com/go-redis/redis/v8 v8.0.0-beta.7
go: downloading golang.org/x/exp v0.0.0-20200513190911-00229845015e
go: downloading github.com/dgryski/go-rendezvous v0.0.0-20200624174652-8d2f3be8b2d9
go: downloading go.opentelemetry.io/otel v0.7.0
go: extracting github.com/dgryski/go-rendezvous v0.0.0-20200624174652-8d2f3be8b2d9
go: extracting go.opentelemetry.io/otel v0.7.0
go: extracting golang.org/x/exp v0.0.0-20200513190911-00229845015e
go: downloading google.golang.org/grpc v1.30.0
go: extracting google.golang.org/grpc v1.30.0
go: finding github.com/dgryski/go-rendezvous v0.0.0-20200624174652-8d2f3be8b2d9
go: finding golang.org/x/exp v0.0.0-20200513190911-00229845015e
go: finding go.opentelemetry.io/otel v0.7.0
go: finding google.golang.org/grpc v1.30.0

#
# github.com/dgryski/go-rendezvous
#

mkdir -p $WORK/b087/
cat >$WORK/b087/importcfg << 'EOF' # internal
# import config
EOF
cd /Users/weishi/go/pkg/mod/github.com/dgryski/go-rendezvous@v0.0.0-20200624174652-8d2f3be8b2d9
/usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b087/_pkg_.a -trimpath "$WORK/b087=>" -p github.com/dgryski/go-rendezvous -complete -buildid mVulgzejPZBvuxz5JJcG/mVulgzejPZBvuxz5JJcG -goversion go1.13 -D "" -importcfg $WORK/b087/importcfg -pack -c=4 ./rdv.go
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b087/_pkg_.a # internal

#
# github.com/go-redis/redis/v8/internal/util
#

mkdir -p $WORK/b091/
cat >$WORK/b091/importcfg << 'EOF' # internal
...
cd /Users/weishi/go/pkg/mod/github.com/go-redis/redis/v8@v8.0.0-beta.7/internal/util
/usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b091/_pkg_.a -trimpath "$WORK/b091=>" -p github.com/go-redis/redis/v8/internal/util -lang=go1.11 -complete -buildid 7hcNQjTtdq2OnURD9Joi/7hcNQjTtdq2OnURD9Joi -goversion go1.13 -D "" -importcfg $WORK/b091/importcfg -pack -c=4 ./strconv.go ./unsafe.go
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b091/_pkg_.a # internal

...
#
# go.opentelemetry.io/otel/api/internal
#

mkdir -p $WORK/b099/
cat >$WORK/b099/importcfg << 'EOF' # internal
...
cd /Users/weishi/go/pkg/mod/go.opentelemetry.io/otel@v0.7.0/api/internal
/usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b099/_pkg_.a -trimpath "$WORK/b099=>" -p go.opentelemetry.io/otel/api/internal -lang=go1.13 -complete -buildid IBv9rgWDWTYSyi-9Bbw5/IBv9rgWDWTYSyi-9Bbw5 -goversion go1.13 -D "" -importcfg $WORK/b099/importcfg -pack -c=4 ./rawhelpers.go
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b099/_pkg_.a # internal

...

#
# github.com/go-redis/redis/v8/internal
#

mkdir -p $WORK/b088/
cat >$WORK/b088/importcfg << 'EOF' # internal
...
cd /Users/weishi/go/pkg/mod/github.com/go-redis/redis/v8@v8.0.0-beta.7/internal
/usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b088/_pkg_.a -trimpath "$WORK/b088=>" -p github.com/go-redis/redis/v8/internal -lang=go1.11 -complete -buildid 6LZ7Wjxq8BcJC_99N3LY/6LZ7Wjxq8BcJC_99N3LY -goversion go1.13 -D "" -importcfg $WORK/b088/importcfg -pack -c=4 ./instruments.go ./internal.go ./log.go ./once.go ./unsafe.go ./util.go
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b088/_pkg_.a # internal

#
# github.com/go-redis/redis/v8/internal/hashtag
#

mkdir -p $WORK/b111/
cat >$WORK/b111/importcfg << 'EOF' # internal
...
cd /Users/weishi/go/pkg/mod/github.com/go-redis/redis/v8@v8.0.0-beta.7/internal/hashtag
/usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b111/_pkg_.a -trimpath "$WORK/b111=>" -p github.com/go-redis/redis/v8/internal/hashtag -lang=go1.11 -complete -buildid KWWMDfl3li-k9TaO92D1/KWWMDfl3li-k9TaO92D1 -goversion go1.13 -D "" -importcfg $WORK/b111/importcfg -pack -c=4 ./hashtag.go
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b111/_pkg_.a # internal

#
# github.com/go-redis/redis/v8/internal/pool
#

mkdir -p $WORK/b112/
cat >$WORK/b112/importcfg << 'EOF' # internal
...
cd /Users/weishi/go/pkg/mod/github.com/go-redis/redis/v8@v8.0.0-beta.7/internal/pool
/usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b112/_pkg_.a -trimpath "$WORK/b112=>" -p github.com/go-redis/redis/v8/internal/pool -lang=go1.11 -complete -buildid MGNMtnPULjz4zcy9L1pD/MGNMtnPULjz4zcy9L1pD -goversion go1.13 -D "" -importcfg $WORK/b112/importcfg -pack -c=4 ./conn.go ./pool.go ./pool_single.go ./pool_sticky.go
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b112/_pkg_.a # internal

#
# github.com/go-redis/redis/v8
#

mkdir -p $WORK/b001/
cat >$WORK/b001/importcfg << 'EOF' # internal
...
cd /Users/weishi/go/pkg/mod/github.com/go-redis/redis/v8@v8.0.0-beta.7
/usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b001/_pkg_.a -trimpath "$WORK/b001=>" -p github.com/go-redis/redis/v8 -lang=go1.11 -complete -buildid snjTAwbdrC3fnlT0aI0L/snjTAwbdrC3fnlT0aI0L -goversion go1.13 -D "" -importcfg $WORK/b001/importcfg -pack -c=4 ./cluster.go ./cluster_commands.go ./command.go ./commands.go ./doc.go ./error.go ./iterator.go ./options.go ./pipeline.go ./pubsub.go ./redis.go ./result.go ./ring.go ./script.go ./sentinel.go ./tx.go ./universal.go
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/_pkg_.a # internal

还是来看看 go install 生成的临时文件夹的结构:

$ go install -work github.com/go-redis/redis/v8
WORK=/var/folders/42/27cgq8k50hsc48kdnpwpt4080000gn/T/go-build314199619
$ cd 
$ tree
.
├── b001
│   ├── _pkg_.a
│   └── importcfg
├── b087
│   ├── _pkg_.a
│   └── importcfg
...

21 directories, 42 files

结构和运行了 go build 命令一样,最终生成的文件也都被移动到了相对应的目标目录中。

go get - add dependencies to current module and install them

go get命令所做的是从代码版本控制系统的远程仓库中检出/更新代码包并对其进行编译和安装。

名称 主命令 说明
Mercurial hg Mercurial是一种轻量级分布式版本控制系统,采用Python语言实现,易于学习和使用,扩展性强。
Git git Git最开始是Linux Torvalds为了帮助管理 Linux 内核开发而开发的一个开源的分布式版本控制软件。但现在已被广泛使用。它是被用来进行有效、高速的各种规模项目的版本管理。
Subversion svn Subversion是一个版本控制系统,也是第一个将分支概念和功能纳入到版本控制模型的系统。但相对于Git和Mercurial而言,它只算是传统版本控制系统的一员。
Bazaar bzr Bazaar是一个开源的分布式版本控制系统。但相比而言,用它来作为VCS的项目并不多。

go get 命令在检出代码包之前必须要知道代码包远程导入路径所对应的版本控制系统和远程仓库的URL。

如果该代码包在本地工作区中已经存在,则会直接通过分析其路径来确定这几项信息。go get 命令支持的几个版本控制系统都有一个共同点,那就是会在检出的项目目录中存放一个元数据目录,名称为“.”前缀加其主命令名。例如,Git会在检出的项目目录中加入一个名为“.git”的子目录。所以,这样就很容易判定代码包所用的版本控制系统。另外,又由于代码包已经存在,我们只需通过代码版本控制系统的更新命令来更新代码包,因此也就不需要知道其远程仓库的URL了。对于已存在于本地工作区的代码包,除非要求强行更新代码包,否则go get命令不会进行重复下载。如果想要强行更新代码包,可以在执行go get命令时加入-u标记。

$ go help get
usage: go get [-d] [-t] [-u] [-v] [-insecure] [build flags] [packages]

Get resolves and adds dependencies to the current development module
and then builds and installs them.

The first step is to resolve which dependencies to add.

For each named package or package pattern, get must decide which version of
the corresponding module to use. By default, get looks up the latest tagged
release version, such as v0.4.5 or v1.2.3. If there are no tagged release
versions, get looks up the latest tagged pre-release version, such as
v0.0.1-pre1. If there are no tagged versions at all, get looks up the latest
known commit. If the module is not already required at a later version
(for example, a pre-release newer than the latest release), get will use
the version it looked up. Otherwise, get will use the currently
required version.

This default version selection can be overridden by adding an @version
suffix to the package argument, as in 'go get golang.org/x/text@v0.3.0'.
The version may be a prefix: @v1 denotes the latest available version starting
with v1. See 'go help modules' under the heading 'Module queries' for the
full query syntax.

For modules stored in source control repositories, the version suffix can
also be a commit hash, branch identifier, or other syntax known to the
source control system, as in 'go get golang.org/x/text@master'. Note that
branches with names that overlap with other module query syntax cannot be
selected explicitly. For example, the suffix @v2 means the latest version
starting with v2, not the branch named v2.

If a module under consideration is already a dependency of the current
development module, then get will update the required version.
Specifying a version earlier than the current required version is valid and
downgrades the dependency. The version suffix @none indicates that the
dependency should be removed entirely, downgrading or removing modules
depending on it as needed.

The version suffix @latest explicitly requests the latest minor release of the
module named by the given path. The suffix @upgrade is like @latest but
will not downgrade a module if it is already required at a revision or
pre-release version newer than the latest released version. The suffix
@patch requests the latest patch release: the latest released version
with the same major and minor version numbers as the currently required
version. Like @upgrade, @patch will not downgrade a module already required
at a newer version. If the path is not already required, @upgrade and @patch
are equivalent to @latest.

Although get defaults to using the latest version of the module containing
a named package, it does not use the latest version of that module's
dependencies. Instead it prefers to use the specific dependency versions
requested by that module. For example, if the latest A requires module
B v1.2.3, while B v1.2.4 and v1.3.1 are also available, then 'go get A'
will use the latest A but then use B v1.2.3, as requested by A. (If there
are competing requirements for a particular module, then 'go get' resolves
those requirements by taking the maximum requested version.)

The -t flag instructs get to consider modules needed to build tests of
packages specified on the command line.

The -u flag instructs get to update modules providing dependencies
of packages named on the command line to use newer minor or patch
releases when available. Continuing the previous example, 'go get -u A'
will use the latest A with B v1.3.1 (not B v1.2.3). If B requires module C,
but C does not provide any packages needed to build packages in A
(not including tests), then C will not be updated.

The -u=patch flag (not -u patch) also instructs get to update dependencies,
but changes the default to select patch releases.
Continuing the previous example,
'go get -u=patch A@latest' will use the latest A with B v1.2.4 (not B v1.2.3),
while 'go get -u=patch A' will use a patch release of A instead.

When the -t and -u flags are used together, get will update
test dependencies as well.

In general, adding a new dependency may require upgrading
existing dependencies to keep a working build, and 'go get' does
this automatically. Similarly, downgrading one dependency may
require downgrading other dependencies, and 'go get' does
this automatically as well.

The -insecure flag permits fetching from repositories and resolving
custom domains using insecure schemes such as HTTP. Use with caution.

The second step is to download (if needed), build, and install
the named packages.

If an argument names a module but not a package (because there is no
Go source code in the module's root directory), then the install step
is skipped for that argument, instead of causing a build failure.
For example 'go get golang.org/x/perf' succeeds even though there
is no code corresponding to that import path.

Note that package patterns are allowed and are expanded after resolving
the module versions. For example, 'go get golang.org/x/perf/cmd/...'
adds the latest golang.org/x/perf and then installs the commands in that
latest version.

The -d flag instructs get to download the source code needed to build
the named packages, including downloading necessary dependencies,
but not to build and install them.

With no package arguments, 'go get' applies to Go package in the
current directory, if any. In particular, 'go get -u' and
'go get -u=patch' update all the dependencies of that package.
With no package arguments and also without -u, 'go get' is not much more
than 'go install', and 'go get -d' not much more than 'go list'.

For more about modules, see 'go help modules'.

For more about specifying packages, see 'go help packages'.

This text describes the behavior of get using modules to manage source
code and dependencies. If instead the go command is running in GOPATH
mode, the details of get's flags and effects change, as does 'go help get'.
See 'go help modules' and 'go help gopath-get'.

See also: go build, go install, go clean, go mod.

-u - 更新依赖

The -u flag instructs get to use the network to update the named packages and their dependencies. By default, get uses the network to check out missing packages but does not use it to look for updates to existing packages.

还有一个很有用的标记是-u标记,加上它可以利用网络来更新已有的代码包及其依赖包。如果已经下载过一个代码包,但是这个代码包又有更新了,那么这时候可以直接用-u标记来更新本地的对应的代码包。如果不加这个-u标记,并对一个已有的代码包执行 go get ,会发现命令什么都不执行。只有加了-u标记,命令会去执行 git pull 命令拉取最新的代码包的最新版本,下载并安装。

# To upgrade a dependency to the latest version:
# go get foo is equivalent to go get foo@latest
$ go get example.com/package

# 更新特定依赖到最新版本(同时也更新这个依赖的依赖到最新版本)
# To upgrade a dependency and all its dependencies to the latest version
$ go get example.com/package -u

# upgrades all the direct and indirect dependencies of your module, and now excludes test dependencies.
$ go get -u ./...

# Update all direct and indirect dependencies to latest patch upgrades
$ go get -u=patch ./...

# is similar, but also upgrades test dependencies.
$ go get -u -t ./... 

命令 go get 还有一个很值得称道的功能——智能下载。在使用它检出或更新代码包之后,它会寻找与本地已安装 Go 语言的版本号相对应的标签(tag)或分支(branch)。比如,本机安装 Go 语言的版本是1.x,那么 go get 命令会在该代码包的远程仓库中寻找名为 “go1” 的标签或者分支。如果找到指定的标签或者分支,则将本地代码包的版本切换到此标签或者分支。如果没有找到指定的标签或者分支,则将本地代码包的版本切换到主干的最新版本。

(如果有更新的版本)升级次要或修订版本号(x.y.z,z是 patch 版本号, y是 minor版本号):

$ go get -u rsc.io/quote

仅升级修订版本号(次要版本号不变):

$ go get -u=patch rscio/quote

指定版本下载

# 指定版本号下载
$ go get golang.org/x/text@v0.3.0

# 指定 git 的分支或 tag
$ go get foo@master

# 指定 git 提交哈希
$ go get foo@e3702bed2

This default version selection can be overridden by adding an @version suffix to the package argument, as in ‘go get golang.org/x/text@v0.3.0’.

The version suffix @latest explicitly requests the latest minor release of the module named by the given path. The suffix @upgrade is like @latest but will not downgrade a module if it is already required at a revision or pre-release version newer than the latest released version. The suffix @patch requests the latest patch release: the latest released version with the same major and minor version numbers as the currently required version. Like @upgrade, @patch will not downgrade a module already required at a newer version. If the path is not already required, @upgrade and @patch are equivalent to @latest.

gomod遵循最小版本选择原则,

  • 即如果当前版本是 v1.1.0,只会下载不超过这个最大版本号。
  • 如果使用 go get foo@master,下次在再次执行这个命令时,仍然会下载和第一次一样的版本,即使 master 分支更新了代码。

-d 只执行下载动作,而不执行build 和 install动作

The -d flag instructs get to stop after downloading the packages; that is, it instructs get not to install the packages.

-t - 同时下载依赖包中的测试源码文件

The -t flag instructs get to also download the packages required to build the tests for the specified packages.

The -t flag instructs get to consider modules needed to build tests of packages specified on the command line.

When the -t and -u flags are used together, get will update test dependencies as well.

-fix

The -fix flag instructs get to run the fix tool on the downloaded packages before resolving dependencies or building the code.

-f

The -f flag, valid only when -u is set, forces get -u not to verify that each package has been checked out from the source control repository implied by its import path. This can be useful if the source is a local fork of the original.

go list - 查看当前所有依赖

# View available upgrades for all direct and indirect dependencies
$ go list -m all

# View available minor and patch upgrades for all direct and indirect dependencies
$ go list -u -m all

# To view available minor and patch upgrades only for the direct dependencies, run:
$ go list -u -f '{{if (and (not (or .Main .Indirect)) .Update)}}{{.Path}}: {{.Version}} -> {{.Update.Version}}{{end}}' -m all 2> /dev/null

# json 格式输出
$ go list -m -json all

-v - 下载依赖时进行debug

The -v flag enables verbose progress and debug output.

# see details
$ go get -v github.com/go-redis/redis/v7

go test

# test all packages in the module
$ go test ./...

godoc

$ go get -u golang.org/x/tools/...

Reference