5.1 Go项目的项目结构

我们先来看看第一个Go项目——Go语言自身——的项目结构是什么样的。

Go项目的项目结构自1.0版本发布以来一直十分稳定,直到现在Go项目的顶层结构基本没有大的改变。截至Go项目commit 1e3ffb0c(2019.5.14),Go项目结构如下:

$ tree -LF 1 ~/go/src/github.com/golang/go
./go
├── api/
├── AUTHORS
├── CONTRIBUTING.md
├── CONTRIBUTORS
├── doc/
├── favicon.ico
├── lib/
├── LICENSE
├── misc/
├── PATENTS
├── README.md
├── robots.txt
├── src/
└── test/

作为Go语言的创世项目,Go的项目结构的布局对后续的Go语言项目具有重要的参考意义,尤其是早期Go项目中src目录下面的结构,更是在后续被Go社区作为Go应用项目结构的模板广泛使用。以早期的Go 1.3版本的src目录下的结构为例:

$ tree -LF 1 ./src
./src
├── all.bash*
├── all.bat
├── all.rc*
├── clean.bash*
├── clean.bat
├── clean.rc*
├── cmd/
├── lib9/
├── libbio/
├── liblink/
├── make.bash*
├── make.bat
├── Make.dist
├── make.rc*
├── nacltest.bash*
├── pkg/
├── race.bash*
├── race.bat
├── run.bash*
├── run.bat
├── run.rc*
└── sudo.bash*

关于上面src目录下的结构,笔者总结了以下三个特点。

1)代码构建的脚本源文件放在src下面的顶层目录下。

2)src下的二级目录cmd下面存放着Go工具链相关的可执行文件(比如go、gofmt等)的主目录以及它们的main包源文件。

$ tree -LF 1 ./cmd
./cmd
...
├── 6a/
├── 6c/
├── 6g/
...
├── cc/
├── cgo/
├── dist/
├── fix/
├── gc/
├── go/
├── gofmt/
├── ld/
├── nm/
├── objdump/
├── pack/
└── yacc/

3)src下的二级目录pkg下面存放着上面cmd下各工具链程序依赖的包、Go运行时以及Go标准库的源文件。

$ tree -LF 1 ./pkg
./pkg
...
├── flag/
├── fmt/
├── go/
├── io/
├── log/
├── math/
...
├── syscall/
├── testing/
├── text/
├── time/
├── unicode/
└── unsafe/

在Go 1.3版本以后至今,Go项目下的src目录发生了几次结构上的变动。

  • Go 1.4版本删除了Go源码树中src/pkg/xxx中的pkg这一层级目录,改为直接使用src/xxx。
  • Go 1.4版本在src下面增加internal目录,用于存放无法被外部导入、仅Go项目自用的包。
  • Go 1.6版本在src下面增加vendor目录,但Go项目自身真正启用vendor机制是在Go 1.7版本中。vendor目录中存放了Go项目自身对外部项目的依赖,主要是golang.org/x下的各个包,包括net、text、crypto等。该目录下的包会在每次Go版本发布时更新。
  • Go 1.13版本在src下面增加了go.mod和go.num,实现了Go项目自身的go module迁移。Go项目内所有包被放到名为std的module下面,其依赖的包依然是golang.org/x下的各个包。
// Go 1.13版本Go项目src下面的go.mod
module std

go 1.12

require (
    golang.org/x/crypto v0.0.0-20200124225646-8b5121be2f68
    golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
    golang.org/x/sys v0.0.0-20190529130038-5219a1e1c5f8 // indirect
    golang.org/x/text v0.3.2 // indirect
)

下面是Go 1.16版本src目录下的完整布局:

├── Make.dist
├── README.vendor
├── all.bash*
├── all.bat
├── all.rc*
├── bootstrap.bash*
├── buildall.bash*
├── clean.bash*
├── clean.bat
├── clean.rc*
├── cmd/
├── cmp.bash
├── go.mod
├── go.sum
├── internal/
├── make.bash*
├── make.bat
├── make.rc*
├── race.bash*
├── race.bat
├── run.bash*
├── run.bat
├── run.rc*
├── testdata/
...
└── vendor/