Go 编译与环境
Sep 15, 2018 21:00 · 2621 words · 6 minute read
1. 环境变量
通过 go env
命令输出 Go 环境变量信息
$ go env
GOARCH="amd64"
GOBIN="/Users/crazytaxii/MyRepo/workspace/go/bin"
GOCACHE="/Users/crazytaxii/Library/Caches/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/crazytaxii/MyRepo/workspace/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/5m/vj3rj35s4td1kvy1zrcj6n5h0000gn/T/go-build579140915=/tmp/go-build -gno-record-gcc-switches -fno-common"
环境变量名称 | 说明 |
---|---|
GOARCH | 程序构建环境的目标计算架构 |
GOBIN | 存放可执行文件的目录的绝对路径 |
GOEXE | 可执行文件的后缀 |
GOHOSTARCH | 程序运行环境的目标计算架构 |
GOOS | 程序构建环境的目标操作系统 |
GOHOSTOS | 程序运行环境的目标操作系统 |
GOPATH | 工作区目录的绝对路径 |
GOROOT | Go 语言的安装目录的绝对路径 |
GOROOT
该环境变量的值为 Go 语言的当前安装目录。
GOPATH
GOPATH 是最重要的环境变量,代表 Go 语言的工作区,是放置 Go 语言源码文件的目录。
可以设置多个工作区,每个不同的目录用 :
分隔。
每个工作区都包含了3个目录:
$ tree -L 1 $GOPATH
/Users/crazytaxii/MyRepo/workspace/go
├── bin
├── pkg
└── src
- bin 目录下存放通过
go install
命令生成(安装)的可执行文件 (executable file) - pkg 目录下存放
go install
命令安装后代码包的归档文件(.a 后缀) - src 目录放置源码,每个子目录都是一个代码包
所有归档文件会被存放至 pkg 目录下的平台相关目录中 ($GOPATH/pkg/$GOOS_$GOARCH),GOOS 是 Go 所在的操作系统类型,GOARCH 是 Go 所在的计算架构。macOS 上这个目录名就是 darwin_amd64。
GOBIN
该环境变量的值为 Go 程序的可执行文件的目录。
2. 源码
Go 源码文件分成三种:
- 命令源码文件
- 库源码文件
- 测试源码文件
命令源码
声明属于 main 代码包、包含无参数声明和结果声明的 main 函数所在的文件就是命令源码文件。 命令源码文件是程序的入口,是必须有的。
学习阶段会编写一些实验性的命令源码文件,直接用 go run
命令执行。
编译 (go build
) 或者安装 (go install
) 所生成的可执行文件一般与源码文件所在的父目录同名。
// hello.go
package main
import "fmt"
func main() {
fmt.Println("Hello, world!")
}
命令源码文件应该是被单独放在一个代码包中。
库源码
**不具备包含无参数声明和结果声明的 main 函数的特征。**库源码文件不能直接运行,但是可以被其他代码导入并使用。“其他代码”可以在同一个目录下,也可以在其他代码包中。
库源码文件被安装后,相应的归档文件(.a 后缀)会被存放到当前工作区的 pkg 的平台相关目录下。
测试源码
_test.go
为后缀的代码文件,并且必须包含 Test 或者 Benchmark 名称前缀的函数。
import "testing"
func TestXXX(t *testing.T) {
}
func BenchmarkXXX(b *testing.B) {
}
- Test 为名称前缀的函数,只能接受 testing.T 的参数,是功能*测试函数。
- Benchmark 为名称前缀的函数,只能接受 testing.B 的参数,是性能*测试函数。
Go 程序是通过代码包 (package) 来组织的。
每一个可独立运行的 Go 程序,必定包含一个 package main,在这个 main 包中必定包含一个入口函数 main,而这个函数既没有参数,也没有返回值。
每个代码包都有导入路径。在使用代码包之前,必须先在当前代码中导入,比如:import "github.com/crazytaxii/alipay"
。
3. 编译
Go 只有17个基本命令:
$ go help
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 download and install packages and dependencies
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
运行 (go run)
只能用来运行命令源代码文件。
带上
-n
标记能够打印执行过程中将用到的所有命令
$ go run -n hello.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
packagefile runtime=/usr/local/go/pkg/darwin_amd64/runtime.a
EOF
cd /Users/crazytaxii/MyRepo/workspace/go/src/me
/usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b001/_pkg_.a -trimpath $WORK/b001 -p main -complete -buildid v48szpJUa_QoDKLrwvCP/v48szpJUa_QoDKLrwvCP -dwarf=false -goversion go1.11 -D _/Users/crazytaxii/MyRepo/workspace/go/src/me -importcfg $WORK/b001/importcfg -pack -c=4 ./hello.go
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/_pkg_.a # internal
cat >$WORK/b001/importcfg.link << 'EOF' # internal
packagefile command-line-arguments=$WORK/b001/_pkg_.a
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 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/sys=/usr/local/go/pkg/darwin_amd64/runtime/internal/sys.a
packagefile sync/atomic=/usr/local/go/pkg/darwin_amd64/sync/atomic.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 math/bits=/usr/local/go/pkg/darwin_amd64/math/bits.a
packagefile internal/race=/usr/local/go/pkg/darwin_amd64/internal/race.a
EOF
mkdir -p $WORK/b001/exe/
cd .
/usr/local/go/pkg/tool/darwin_amd64/link -o $WORK/b001/exe/hello -importcfg $WORK/b001/importcfg.link -s -w -buildmode=exe -buildid=i0g6hiz4oOTS8eWsD-1w/v48szpJUa_QoDKLrwvCP/v48szpJUa_QoDKLrwvCP/i0g6hiz4oOTS8eWsD-1w -extld=clang $WORK/b001/_pkg_.a
$WORK/b001/exe/hello
可以看到先创建了一个临时目录 $WORK/b001/,命令源码文件导入了两个 .a 后缀的归档文件,runtime 是 Go 内置的运行时,然后将源码编译成归档文件 pkg.a。 之后又创建了一个临时目录 $WORK/b001/exe/,执行链接命令生成最终可执行文件 hello,最后执行此文件。
$WORK 环境变量所对应的路径用 go run -work
查看:
$ go run -work hello.go
WORK=/var/folders/5m/vj3rj35s4td1kvy1zrcj6n5h0000gn/T/go-build362157519
但只是临时生成的,结束后就会被删除。
**第二次对相同的源码文件执行 go run
命令,如果源码没有发生变化,将直接使用缓存(归档文件)来加速编译过程。**使用 -a
标记可以强制重新编译:go run -a xxx.go
。
编译 (go build)
当代码包中有且仅有一个命令源码文件的时候,在文件夹所在目录中执行
go build
命令,会在该目录下生成一个与目录同名的可执行文件。
$pwd
/Users/crazytaxii/MyRepo/workspace/go/src/me
$ ls
hello.go
$ go build && ls
hello.go me
go build
用于编译代码包和依赖包,如果存在命令源码文件,会生成一个和父目录同名的可执行文件。如果编译库源码文件,不会生成任何新文件,只是检查性的编译。
-a
标记同样会强制重新编译所有源码,否则将使用归档文件链接来加速编译。
常用标记
标记 | 说明 |
---|---|
-a | 强行重新编译所有源码,不使用缓存 |
-n | 打印编译期间所用到的所有其他命令,但是并不真正执行 |
-p n | 指定编译任务并发数量,默认等于CPU的逻辑核数 |
-v | 打印所有被编译的代码包 |
-work | 打印出编译时生成的临时工作目录的路径,并在编译结束时保留它 |
-x | 打印编译期间所用到的其它命令,也会执行 |
$ go build -a -n hello.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
packagefile runtime=/usr/local/go/pkg/darwin_amd64/runtime.a
EOF
cd /Users/crazytaxii/MyRepo/workspace/go/src/me
/usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b001/_pkg_.a -trimpath $WORK/b001 -p main -complete -buildid 2ID8FwFqGLi7IER8Ykgf/2ID8FwFqGLi7IER8Ykgf -goversion go1.11 -D _/Users/crazytaxii/MyRepo/workspace/go/src/me -importcfg $WORK/b001/importcfg -pack -c=4 ./hello.go
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/_pkg_.a # internal
cat >$WORK/b001/importcfg.link << 'EOF' # internal
packagefile command-line-arguments=$WORK/b001/_pkg_.a
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 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/sys=/usr/local/go/pkg/darwin_amd64/runtime/internal/sys.a
packagefile sync/atomic=/usr/local/go/pkg/darwin_amd64/sync/atomic.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 math/bits=/usr/local/go/pkg/darwin_amd64/math/bits.a
packagefile internal/race=/usr/local/go/pkg/darwin_amd64/internal/race.a
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=ZLacLDyRoM51ku4upaLn/2ID8FwFqGLi7IER8Ykgf/2ID8FwFqGLi7IER8Ykgf/ZLacLDyRoM51ku4upaLn -extld=clang $WORK/b001/_pkg_.a
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/exe/a.out # internal
mv $WORK/b001/exe/a.out hello
与 go run
的最后一步不同,go run
会运行一遍可执行文件,但是 go build
把可执行文件移动到了当前目录下。
安装 (go install)
go install
命令是用来编译并安装代码包或者源码文件。安装代码包会在 $GOPATH/pkg 的平台相关目录下生成归档文件。如果是命令源码文件,还会在 $GOPATH/src 生成可执行文件。
$ go install -x
WORK=/var/folders/5m/vj3rj35s4td1kvy1zrcj6n5h0000gn/T/go-build166561238
mkdir -p $WORK/b001/
cat >$WORK/b001/importcfg.link << 'EOF' # internal
packagefile me=/Users/crazytaxii/Library/Caches/go-build/2b/2b3f269c2593ebde1ae01c5cb1090284922a152c0547548df883f539690da093-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 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/sys=/usr/local/go/pkg/darwin_amd64/runtime/internal/sys.a
packagefile sync/atomic=/usr/local/go/pkg/darwin_amd64/sync/atomic.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 math/bits=/usr/local/go/pkg/darwin_amd64/math/bits.a
packagefile internal/race=/usr/local/go/pkg/darwin_amd64/internal/race.a
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=1NK0kgLjipeyvtEHOEes/RD5Adwnjlliku8CHG8Lt/SvxDzzmjdf-1di9R9ab-/1NK0kgLjipeyvtEHOEes -extld=clang /Users/crazytaxii/Library/Caches/go-build/2b/2b3f269c2593ebde1ae01c5cb1090284922a152c0547548df883f539690da093-d
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/exe/a.out # internal
mkdir -p /Users/crazytaxii/MyRepo/workspace/go/bin/
mv $WORK/b001/exe/a.out /Users/crazytaxii/MyRepo/workspace/go/bin/me
rm -r $WORK/b001/
前面几步依旧和 go run
、go build
完全一致,最后一步会把 a.out (可执行文件)安装到 $GOPATH/bin 目录。如果是库源码文件,就会被安装到 $GOPATH/pkg 的平台相关目录下。
下载+安装 (go get)
go get
命令用于从远程代码仓库上下载并安装代码包。代码包会被下载至 $GOPATH/src 目录。
常用标记
标记 | 说明 |
---|---|
-d | 只下载不安装 |
-t | 同时下载并安装指定的代码包中的测试源码文件中依赖的代码包 |
-u | 更新已有代码包及其依赖包 |
$ go get -x github.com/go-errors/errors
cd .
git clone https://github.com/go-errors/errors /Users/crazytaxii/MyRepo/workspace/go/src/github.com/go-errors/errors
cd /Users/crazytaxii/MyRepo/workspace/go/src/github.com/go-errors/errors
git submodule update --init --recursive
cd /Users/crazytaxii/MyRepo/workspace/go/src/github.com/go-errors/errors
git show-ref
cd /Users/crazytaxii/MyRepo/workspace/go/src/github.com/go-errors/errors
git submodule update --init --recursive
WORK=/var/folders/5m/vj3rj35s4td1kvy1zrcj6n5h0000gn/T/go-build940469532
mkdir -p $WORK/b001/
cat >$WORK/b001/importcfg << 'EOF' # internal
# import config
packagefile bytes=/usr/local/go/pkg/darwin_amd64/bytes.a
packagefile fmt=/usr/local/go/pkg/darwin_amd64/fmt.a
packagefile io/ioutil=/usr/local/go/pkg/darwin_amd64/io/ioutil.a
packagefile reflect=/usr/local/go/pkg/darwin_amd64/reflect.a
packagefile runtime=/usr/local/go/pkg/darwin_amd64/runtime.a
packagefile strconv=/usr/local/go/pkg/darwin_amd64/strconv.a
packagefile strings=/usr/local/go/pkg/darwin_amd64/strings.a
EOF
cd /Users/crazytaxii/MyRepo/workspace/go/src/github.com/go-errors/errors
/usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b001/_pkg_.a -trimpath $WORK/b001 -p github.com/go-errors/errors -complete -buildid _9IFsUytP94j0WnKU0p7/_9IFsUytP94j0WnKU0p7 -goversion go1.11 -D "" -importcfg $WORK/b001/importcfg -pack -c=4 ./error.go ./parse_panic.go ./stackframe.go
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/_pkg_.a # internal
cp $WORK/b001/_pkg_.a /Users/crazytaxii/Library/Caches/go-build/7a/7a86fe8c0bd53e5366f1adb295a5b6fb562434b34b38f2b6780c1cfebe3334c7-d # internal
mkdir -p /Users/crazytaxii/MyRepo/workspace/go/pkg/darwin_amd64/github.com/go-errors/
mv $WORK/b001/_pkg_.a /Users/crazytaxii/MyRepo/workspace/go/pkg/darwin_amd64/github.com/go-errors/errors.a
rm -r $WORK/b001/
go get
会调用 git 克隆源码至 $GOPATH/src 对应的路径下,之后会编译成归档文件后移动至 $GOPATH/pkg 对应的相关平台目录下,可以看到归档文件名为 errors.a
4. 重点
- Go 语言源码的组织方式
- 源码运行、编译和安装的差异
- 编译命令的一些进阶用法