万字长文详解 Go 程序是怎样跑起来的?( 六 )

我们也可以指定生成的可执行文件的名称:
go build -o bin/hello这样,在 bin 目录下会生成一个可执行文件,运行结果和上面的 src 一样 。
其实,util 包可以单独被编译 。我们可以在项目根目录下执行:
go build util编译程序会去 $GoPath/src 路径找 util 包(其实是找文件夹) 。还可以在 ./src/util 目录下直接执行 go build 编译 。
当然,直接编译库源码文件不会生成 .a 文件,因为:
go build 命令在编译只包含库源码文件的代码包(或者同时编译多个代码包)时,只会做检查性的编译,而不会输出任何结果文件 。
为了展示整个编译链接的运行过程,我们在项目根目录执行如下的命令:
go build -v -x -work -o bin/hello src/main.go-v 会打印所编译过的包名字, -x 打印编译期间所执行的命令, -work 打印编译期间生成的临时文件路径,并且编译完成之后不会被删除 。
执行结果:

万字长文详解 Go 程序是怎样跑起来的?

文章插图
从结果来看,图中用箭头标注了本次编译过程涉及 2 个包:util,command-line-arguments 。第二个包比较诡异,源码里根本就没有这个名字好吗?其实这是 go build 命令检测到 [packages] 处填的是一个 .go 文件,因此创建了一个虚拟的包:command-line-arguments 。
同时,用红框圈出了 compile, link,也就是先编译了 util 包和 main.go 文件,分别得到 .a 文件,之后将两者进行链接,最终生成可执行文件,并且移动到 bin 目录下,改名为 hello 。
另外,第一行显示了编译过程中的工作目录,此目录的文件结构是:
万字长文详解 Go 程序是怎样跑起来的?

文章插图
可以看到,和 hello-world 目录的层级基本一致 。command-line-arguments 就是虚拟的 main.go 文件所处的包 。exe 目录下的可执行文件在最后一步被移动到了 bin 目录下,所以这里是空的 。
整体来看, go build 在执行时,会先递归寻找 main.go 所依赖的包,以及依赖的依赖,直至最底层的包 。这里可以是深度优先遍历也可以是宽度优先遍历 。如果发现有循环依赖,就会直接退出,这也是经常会发生的循环引用编译错误 。
正常情况下,这些依赖关系会形成一棵倒着生长的树,树根在最上面,就是 main.go 文件,最下面是没有任何其他依赖的包 。编译器会从最左的节点所代表的包开始挨个编译,完成之后,再去编译上一层的包 。
这里,引用郝林老师几年前在 github 上发表的 go 命令教程,可以从参考资料找到原文地址 。
从代码包编译的角度来说,如果代码包 A 依赖代码包 B,则称代码包 B 是代码包 A 的依赖代码包(以下简称依赖包),代码包 A 是代码包 B 的触发代码包(以下简称触发包) 。
执行 go build 命令的计算机如果拥有多个逻辑 CPU 核心,那么编译代码包的顺序可能会存在一些不确定性 。但是,它一定会满足这样的约束条件:依赖代码包 -> 当前代码包 -> 触发代码包 。
顺便推荐一个浏览器插件 Octotree,在看 github 项目的时候,此插件可以在浏览器里直接展示整个项目的文件结构,非常方便:
万字长文详解 Go 程序是怎样跑起来的?

文章插图
到这里,你一定会发现,对于 hello-wrold 文件夹下的 pkg 目录好像一直没有涉及到 。
其实,pkg 目录下面应该存放的是涉及到的库文件编译后的包,也就是一些 .a 文件 。但是 go build 执行过程中,这些 .a 文件放在临时文件夹中,编译完成后会被直接删掉,因此一般不会用到 。
前面我们提到过,在 go build 命令里加上 -i 参数会安装这些库文件编译的包,也就是这些 .a 文件会放到 pkg 目录下 。
在项目根目录执行 go build-i src/main.go 后,pkg 目录里增加了 util.a 文件:
万字长文详解 Go 程序是怎样跑起来的?

文章插图
darwin_amd64 表示的是:
  • GOOS 和 GOARCH 。这两个环境变量不用我们设置,系统默认的 。
  • GOOS 是 Go 所在的操作系统类型,GOARCH 是 Go 所在的计算架构 。
  • Mac 平台上这个目录名就是 darwin_amd64
生成了 util.a 文件后,再次编译的时候,就不会再重新编译 util.go 文件,加快了编译速度 。
同时,在根目录下生成了名称为 main 的可执行文件,这是以 main.go 的文件名命令的 。
hello-world 这个项目的代码已经上传到了 github 项目 Go-Questions,这个项目由问题导入,企图串连 Go 的所有知识点,正在完善,期待你的 star 。地址见参考资料【Go-Questions hello-world项目】 。


推荐阅读