在这个工业化合作的时代,一个中等规模的程序可能包含一万个函数,显而易见,这不是凭借一个人可以完成的工程规模,多人如何在同一个项目中进行协作是现代编程语言设计中必须考虑的问题。 GoLang 中,通过包来管理不同的模块,从而实现代码的共享、重用与改进,同时,我们已经看到,包具有定义是否导出变量、函数的功能,从而实现代码的封装能力,保护维护者所编写的代码,同时提供变量、函数的命名空间功能。 Go 语言自带了 100 多个包,可以通过下面链接查看:
http://godoc.org
那么,我们如何创建我们自己的包,如何从网络中下载、构建已有的包,如何进行包版本的管理呢?本文我们就来详细解读。

每一个包都通过唯一的字符串进行标识,这个字符串被称为“导入路径”。 一个包需要使用其他包时,需要在 package 关键字生命的所在包行下面使用 import 关键字声明导入的包。
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Println(rand.Int())
}Go 语言本身没有对包的命名做任何限制,如何查找到包取决于 go 工具对包名的理解,但为了保证包导入路径的唯一性,除标准库中的包,其他包的导入路径应该以互联网域名(组织机构拥有的域名或用于存放包的域名)作为路径开始,从而保证唯一性的同时方便在互联网中查找到相应的包。 包的导入过程是很快的,主要是因为下面的三个原因:
正如上面的例子中那样,包需要在 import 语句中进行导入,但导入后的包在代码中只需要使用完整包名的最后一段。 关于最后一段,有三个例外情况:
既然包在实际的程序中只使用最后一段,那么很可能因为最后一段过长造成代码编写的不便,更为常见的,可能会出现两个不同的包具备相同的最后一段的可能,例如:
import (
"crypto/rand"
"math/rand"
)此时,就需要重命名导入,声明代码中通过重命名声明来使用相应的包而不是最后一段:
import (
"crypto/rand"
mrand "math/rand"
)这样,在接下来的代码中,如果使用 rand 则使用的是包 "crypto/rand",而 mrand 则使用的是 "math/rand"
GoLang 中,所有导入的包都必须在当前程序文件中引用,否则就会产生编译错误,但有时,我们仅希望导入一个包并执行其初始化操作,而不进行任何引用,这就需要进行“空导入”。 空导入指的是在 import 语句中通过在包名前添加下划线,声明这个包只执行其初始化操作,而不引用到程序中:
import _ "image/png"此前,我们介绍了 go 环境的搭建,go 安装成功后需要配置两个环境变量:
通过 go env 命令可以查看所有 go 所依赖的环境变量及取值:

GoLang 拥有两个编译命令 — go build 与 go install,他们的区别常常让初学者迷惑。 他们的区别其实很简单:
通过 go get 命令,可以从网络上获取指定的包:
go get github.com/urfave/cli
go 工具会将获取到的包源码放置到 GOPATH/src 目录下,但问题在于,这样的方式没有维护包的版本,这就意味着如果远程的代码出现了更新,例如修复了隐含的 bug,或者你需要引用只有旧版本才提供的函数或字段,那你就只能到 GOPATH/src 目录下手动删除相应的包,然后手动进行拉取,这是极不方便的。
由于早期原生的 go get 工具的限制,很多项目使用了第三方的包管理工具 — godep 进行包管理。 godep 将项目中使用到的第三方库复制到项目的 Godeps 目录下,因此 go build、go install 等工具显然因为无法找到包而不能工作了,所以 godep 提供了一系列工具来代替原生的 go 工具。
go get github.com/tools/godep
godep save 命令将项目中使用到的第三方库复制到项目的 Godeps 目录下。 在 Godeps/Godeps.json 文件中维护了各个依赖包的版本信息。 同时,执行命令后,包源码会被拷贝到 Godeps/_workspace/src 目录下用于后续进行的 godep go build 等命令的执行。
godep restore 命令执行后会按照 Godeps/Godeps.json 列表,依次执行 go get -d -v 来下载对应依赖包到GOPATH路径下。
godep go run main.go godep go build godep go install godep go test
可以看到,虽然 godep 实现了一套自己的打包工具,但从用法上,仍然与 go 原有工具十分接近。
govendor 是另一个 GoLang 常用的第三方包管理工具。 在 Golang1.5 版本后,Go 提供了 GO15VENDOREXPERIMENT 环境变量,并从 GoLang1.6 版本开始默认开启该环境变量。 一旦开启 GO15VENDOREXPERIMENT,go build 等工具除了搜索 $GOPATH/src 路径外,还会搜索当前项目的 vendor 路径,govendor 正是利用了这一特性来实现的。
go get -u -v github.com/kardianos/govendor
进入项目根目录,执行:
govendor init
初始化执行完成后,会在项目根目录生成 vendor 目录和 vendor.json 文件,在 vendor.json 中,维护了项目所依赖的包和版本信息。
下面的命令一次性将 $GOPATH 中所有依赖包都同步到 vendor 目录中:
govendor add +external
如果你只想添加某个包,执行:
govendor add gopkg.in/yaml.v2
govendor list
govendor fetch golang.org/x/net/context
以下方式指定了要拉取的包版本:
govendor fetch golang.org/x/net/context@a4bbce9fcae005b22ae5443f6af064d80a6f5a55 govendor fetch golang.org/x/net/context@v1 govendor fetch golang.org/x/net/context@=v1
本文详细介绍了 GoLang 中依赖包的使用和管理,我们看到了原生包管理工具与两个常见的第三方包管理工具的使用。 但从 GoLang 1.11 版本开始,官方推出了一个崭新的包管理工具 — go module,随着 GoLang 1.13 版本的发布,go module 默认开启,官方开始强推 go module,使用 go module 作为包管理工具已经成为了官方倡导的趋势。 那么,go module 是什么,应该如何使用,他又是如何解决了包管理中的诸多问题的呢?敬请期待主页君下一篇文章。
《Go 语言程序设计》。 https://studygolang.com/articles/4385。 http://github.com/tools/godep。 github.com/kardianos/govendor。