一句话概括,GOPATH中保存的是第三方依赖库的内容,GOROOT中保存的是原生的 go 工具的内容。
GOPATH
该环境变量的值为 Go 语言的工作区的集合(意味着可以有很多个)。工作区类似于工作目录。每个不同的目录之间用:
分隔。
工作区是放置 Go 源码文件的目录。一般情况下,Go 源码文件都需要存放到工作区中。
工作区一般会包含3个子文件夹,自己手动新建以下三个目录:src 目录,pkg 目录,bin 目录。
bin 目录里面存放的都是通过 go install 命令安装后,由 Go 命令源码文件生成的可执行文件( 在 Mac 平台下是 Unix executable 文件,在 Windows 平台下是 exe 文件)。
$ echo $GOPATH
/Users/wei.shi/go
$ cd $GOPATH/bin
$ pwd
/Users/wei.shi/go/bin
$ ls
air gin httptest mockgen richgo test_kafka_client
data_pipeline goimports ips_event_consumer protoc-gen-go spkit
easytags golangci-lint item_discount realize test_client
注意:有两种情况下,bin 目录会变得没有意义。
- 当设置了有效的 GOBIN 环境变量以后,bin 目录就变得没有意义。
- 如果 GOPATH 里面包含多个工作区路径的时候,必须设置 GOBIN 环境变量,否则就无法安装 Go 程序的可执行文件。
pkg 目录是用来存放通过 go install 命令安装后的代码包的归档文件(.a 文件)。归档文件的名字就是代码包的名字。所有归档文件都会被存放到该目录下的平台相关目录中,即在 $GOPATH/pkg/$GOOS_$GOARCH 中,同样以代码包为组织形式。
这里有两个隐藏的环境变量,GOOS 和 GOARCH。这两个环境变量是不用我们设置的,系统就默认的。GOOS 是 Go 所在的操作系统类型,GOARCH 是 Go 所在的计算架构。平台相关目录是以 $GOOS_$GOARCH 命名的,Mac 平台上这个目录名就是 darwin_amd64。
src目录下主要存放go的源文件。
$ cd ~
$ mkdir gopath
# 在~/.bash_profile中添加如下语句:
GOPATH=/Users/username/gopath
# GOPATH可以是一个目录列表, 通过 go get下载的第三方库, 都会被下载到 src 文件夹中
# 而且,还需要把GOPATH中的可执行目录也配置到环境变量中, 否则你自行下载的第三方go工具就无法使用了, 操作如下:
# 在~/bash_profile(或者~/.zshrc,根据你使用什么 bash 决定)中增加下面这行
export $PATH:$GOPATH/bin
Go 源码文件
命令源码文件
声明自己属于 main 代码包、包含无参数声明和结果声明的 main 函数。
命令源码文件被安装以后,GOPATH 如果只有一个工作区,那么相应的可执行文件会被存放当前工作区的 bin 文件夹下;如果有多个工作区,就会安装到 GOBIN 指向的目录下。
命令源码文件是 Go 程序的入口。
同一个代码包中最好也不要放多个命令源码文件。多个命令源码文件虽然可以分开单独 go run 运行起来,但是无法通过 go build 和 go install。
$ ~/LeetCode_Go/helloworld/src/me $ ls
helloworld.go helloworldd.go
先说明一下,在上述文件夹中放了两个命令源码文件,同时都声明自己属于 main 代码包。helloworld.go 文件输出 hello world,helloworldd.go 文件输出 worldd hello。接下来执行 go build 和 go install ,看看会发生什么。
$ ~/LeetCode_Go/helloworld/src/me $ go build
# _/Users/YDZ/LeetCode_Go/helloworld/src/me
./helloworldd.go:7: main redeclared in this block
previous declaration at ./helloworld.go:50
$ ~/LeetCode_Go/helloworld/src/me $ go install
# _/Users/YDZ/LeetCode_Go/helloworld/src/me
./helloworldd.go:7: main redeclared in this block
previous declaration at ./helloworld.go:50
这也就证明了多个命令源码文件虽然可以分开单独 go run 运行起来,但是无法通过 go build 和 go install。
同理,如果命令源码文件和库源码文件也会出现这样的问题,库源码文件不能通过 go build 和 go install 这种常规的方法编译和安装。
所以命令源码文件应该是被单独放在一个代码包中。
库源码文件
库源码文件就是不具备命令源码文件上述两个特征的源码文件。存在于某个代码包中的普通的源码文件。
库源码文件被安装后,相应的归档文件(.a 文件)会被存放到当前工作区的 pkg 的平台相关目录下。
测试源码文件
名称以 _test.go 为后缀的代码文件,并且必须包含 Test 或者 Benchmark 名称前缀的函数。
func TestXXX( t *testing.T) {
}
名称以 Test 为名称前缀的函数,只能接受 *testing.T 的参数,这种测试函数是功能测试函数。
func BenchmarkXXX( b *testing.B) {
}
名称以 Benchmark 为名称前缀的函数,只能接受 *testing.B 的参数,这种测试函数是性能测试函数。
总结
命令源码文件是可以单独运行的。可以使用 go run 命令直接运行,也可以通过 go build 或 go install 命令得到相应的可执行文件。所以命令源码文件是可以在机器的任何目录下运行的。
GOROOT
GOROOT就是go的安装路径。
在~/.bash_profile中添加下面语句:
GOROOT="/usr/local/go"
export PATH="$PATH:$GOPATH/bin"
GOBIN
该环境变量的值为 Go 程序的可执行文件的目录。
Go 环境变量
$ go env
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/weishi/Library/Caches/go-build"
GOENV="/Users/weishi/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GONOPROXY="git.garena.com"
GONOSUMDB="git.garena.com"
GOOS="darwin"
GOPATH="/Users/weishi/go"
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/dev/null"
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/42/27cgq8k50hsc48kdnpwpt4080000gn/T/go-build165822851=/tmp/go-build -gno-record-gcc-switches -fno-common"
Go 程序的入口函数
Go 程序是通过 package 来组织的。
package (假设我们的例子中是 package main)这一行告诉我们当前文件属于哪个包,而包名 main 则告诉我们它是一个可独立运行的包,它在编译后会产生可执行文件。除了 main 包之外,其它的包最后都会生成 *.a 文件(也就是包文件)并放置在 $GOPATH/pkg/$GOOS_$GOARCH中(以 Mac 为例就是 $GOPATH/pkg/darwin_amd64 )。
Go 使用 package(和 Python 的模块类似)来组织代码。main.main() 函数(这个函数位于主包)是每一个独立的可运行程序的入口点。
每一个可独立运行的 Go 程序,必定包含一个 package main,在这个 main 包中必定包含一个入口函数 main,而这个函数既没有参数,也没有返回值。