Golang Build Process
Attention
这里的构建过程没有考虑
Golang Build Cache
的影响Golang 的构建过程是典型的静态编译语言的编译过程,分为编译(compile)和链接(link)两个阶段。
包源码被编译为
.o
文件之后,会被pack
工具打包成为.a
文件,生成的.a
会被放入临时构建文件夹,之后被链接成为可执行文件。标准库的构建过程
标准库的源码存放在
$GOROOT/src
下,标准库编译的.a
文件存放在$GOROOT/pkg/
下,构建时如果已经存在.a
文件,则会直接链接.a
文件,否则会从源文件中进行编译。如果想强制从标准库的源码进行重新编译,可以通过两种办法:
- 删除相应的
.a
的文件- 使用
go build -a
参数,这会将所有的源码重新编译一边,所以构建时间可能很长非标准库的构建过程
同标准库的构建过程不同,非标准库在构建的时候都会从源码进行构建。使用
go install
命令可以将包的.a
文件安装到$GOPATH/pkg/
下,但在构建过程中会重新编译源码到.a
,重新编译的文件会被放入构建的临时目录,临时目录的下的.a
优先级会高于$GOPATH/pkg/
中的.a
。程序构建过程
以下面结构的项目的为例,编译是使用命令
go build -x -v
输出详细构建日志:$tree -F demo1 demo1 ├── cmd/ │ └── app1/ │ └── main.go └── pkg/ └── pkg1/ └── pkg1.go
程序
app1
的构建过程可分为下面几步:指向原始笔记的链接
- 建立临时工作目录,放入环境变量
WORK
,之后的工作都已$WORK
为当前目录- 编译
app1
的依赖包pkg1
,将目标文件打包放入$WORK/<demo1_pkg_path>/pkg/pkg1.a
- 编译
app1
的 main 包, 将目标文件打包后放入$WORK/<demo1_pkg_path>/cmd/app1.a
- 链接器将
app1.a
,pkg1.a
链接成为$WORK/<demo1_pkg_path>/cmd/app1/_obj/exe/a.out
- 将
a.out
复制到go build
命令执行目录,并改名为app1
Golang Package Search Path
Golang 中有两部分的包搜索路径由两部分主成:
基础的搜索路径首先都包括
$GOROOT/src
。在此基础上,按照GO111MODULE
的设置不同还会增加不同的路径。
GO111MODULE=off
使用传统的gopath
模式,包基础搜索路径包含$GOPATH/src/
。GO111MODULE=on
使用gomod
模式,包基础搜索路径包含$GOPATH/pkg/mod/
。包导入路径就是每个源文件头部包导入部分的路径。基础路径组合包导入路径,就构成了源文件所有依赖包的源码搜索路径。以下面的例子为例:
package p1 import ( "fmt" "time" "github.com/bigwhite/effective-go-book" "golang.org/x/text" "a/b/c" "./e/f/g" )
该源码包的搜索路径集合在使用
gomod
模式时可以展开为下面这样:- $GOROOT/src/fmt/ - $GOROOT/src/time/ - $GOROOT/src/github.com/bigwhite/effective-go-book/ - $GOROOT/src/golang.org/x/text/ - $GOROOT/src/a/b/c/ - $GOPATH/pkg/mod/github.com/bigwhite/effective-go-book/ - $GOPATH/pkg/mod/golang.org/x/text/ - $GOPATH/pkg/mod/a/b/c/ - $CWD/e/f/g
这里也可以看出代码中
指向原始笔记的链接import
中的其实是路径名,而不是包名,包名由路径中源文件头部的package
关键字指定,其可能和路径名不同,只是 Golang 惯例是和路径名保持一致。
如果一个源码文件中导入两个来源不同包名相同的依赖时,通过设置包的别名来解决冲突。
package main
import (
"fmt"
myfmt "github.com/q3yi/go/fmt"
)