工作空间
依照规范,工作空间由src
、bin
、pkg
三个目录组成。通常需要将空间路径添加到GOPATH
环境变量列表中, 以便相关工具能正常工作。
1 | workspace/ |
在工作空间里,包括子包在内的所有源码文件都保存在src
目录下。至于bin
、pkg
两个目录, 其主要影响 go install/get
命令,他们会将编译结果(可执行文件或静态库)安装到这两个目录下,以实现增量编译。
环境变量
编译器等相关工具按GOPATH
设置的路径搜索目标。也就是说在导入目标库时,排在列表前面的路径比当前工作空间优先级更高。另外,go get
默认将下载的第三方包保存到列表中第一个工作空间内。
环境变量GOPATH
用于指示工具链和标准库的存放位置。在生成工具链时,相关路径就已经嵌入到可执行文件内,故无需额外设置。
除通过设置GOROOT
环境变量覆盖内部路径外,还可移动目录(改名、符号链接等), 或重新编译工具链来解决。
至于GOBIN
, 则是强制替代工作空间的bin
目录,作为go install
目标保存路径。这可避免将所有工作空间的bin
路径添加到PATH
环境变量当中。
导入包
使用标准库或第三方包前,须用import
导入,参数是工作空间中以src
为起始的绝对路径。编译器从标准库开始搜索,然后依次搜索GOPATH
列表中的各个工作空间。
1 | import "net/http" // 实际路径: /usr/local/go/src/net/http |
除使用默认包名外,还可使用别名,以解决同名冲突问题。
1 | import osx "github.com/apple/osx/lib" |
注意:
import
导入参数是路径,而非包名。尽管习惯将包和目录名保持一致,但这不是强制规定。在代码中引用包成员时,使用包名而非目录名。
有四种不同的导入方式。
1 | import "github.com/Mercy1101/test" // 默认方式: test.A |
不能直接或间接导入自己,不支持任何形式的循环导入。
未使用的导入(不包括初始化方式)会被编译器视为错误。
相对路径
除工作空间和绝对路径外,部分工具还支持相对路径。可在非工作空间目录下,直接运行、编译一些测试代码。
但在设置了GOPATH
的工作空间后相对路径会导致编译失败。go run
不受影响。
初始化
包内每个源码文件都可定义一到多个初始化函数,但编译器不保证执行次序。
实际上,所有这些初始化函数(包括标准库和导入的第三方包)都由编译器自动生成的一个包装函数进行调用,因此可保证在单一线程上执行,且仅执行一次。
编译器首先确保完成所有全局变量初始化,然后才开始执行初始化函数。直到这些全部结束后,运行时才正式进入main.main
入口函数。
可在初始化函数中创建goroutine
,或等到它结束执行。
1 | func init(){ |
如果在多个初始化函数中引用全局变量,那么最好在变量定义处直接赋值。因无法保证执行次序,所以任何初始化函数中的赋值都有可能”延迟无效”。
内部包
内部包机制相当于增加了新的访问权限控制:所有保存在internal
目录下的包(包括自身)仅能被其父目录下的包(包含所有子目录) 访问。
1 | workspace/ |
在lib
目录外(比如main.go
)导入内部包会引发编译错误。
导入内部包必须使用完整路径, 例如: import “lib/internal/a”
依赖管理
如何使用vendor
,专门存放第三方包,实现将源码和依赖完整打包分发。
1 | workspace/ |
1 | package main |
在
main.go
中导入github.com/mercy1101/test
时,优先使用vendor/github.com/mercy1101/test
导入vendor
中的第三方包,参数是以vendor/
为起点的绝对路径。这避免了vendor
目录位置带来的麻烦,让导入无论使用vendor
,还是GOPATH
都能保持一致。
注意:
vendor
优先级比标准库高
当多个vendor
目录嵌套时,匹配规则如下:
从当前源文件所在目录开始,逐级向上构造vendor
全路径,直到发现路径匹配的目标为止。匹配失败,则依旧搜索GOPATH
要使用vendor
机制,须开启GO15VENDOREXPERIMENT=1
环境变量开关(Go 1.6默认开启),且必须设置了GOPATH
的工作空间。
使用
go get
下载第三方包时,依旧使用GOPATH
第一个工作空间,而非vendor
目录。当前工具链中并没有真正意义上的包依赖管理,好在由不少第三放工具可选。