go 语言最近很火,与时俱进,我也看了看go 的语法。
看起来 go 还是不错的,有很多新的feature。 就下载了代码研究了一下。
go 的 src 目录下面存在三套编译文件:
以 unix 编译文件为例, go 的编译入口时在 src/all.bash , 这是一个bash 脚步, 这个脚步只是简单的调用了 make.bash 在脚步结束之后,调用 dist banner 输出编译的信息。
set -e
if [ ! -f make.bash ]; then
echo 'all.bash must be run from $GOROOT/src' 1>&2
exit 1
fi
OLDPATH="$PATH"
. ./make.bash "$@" --no-banner
bash run.bash --no-rebuild
PATH="$OLDPATH"
$GOTOOLDIR/dist banner # print build info
dist 是在 make.bash 中生成的一个可执行文件,go 的所有编译都是在这个文件的控制下完成的。 个人认为这并不是一个好的设计,导致维护编译系统的成功过高,如果要修改一下编译选项,往往要修改 dist 源代码。dist 的代码在目录: /src/cmd/dist 下。
dist 这个命令行程序支持如下几个参数:
"banner print installation banner\n" ; 打印安装的一些信息
"bootstrap rebuild everything\n" ; 重新编译所有的 go 代码
"clean deletes all built files\n" ; 清楚编译的 go 代码
"env [-p] print environment (-p: include $PATH)\n" ; 打印编译的环境
"install [dir] install individual directory\n" ;安装某一个目录。会编译目录下代码,安装生成文件
"version print Go version\n" ;大约go版本信息
想要研究编译细节一定要看看这个程序的代码,后续详细分析。
make.bash, 同样是一个 bash 脚步,打开这个脚步,可以看到这个脚步主要做了如下几件事情:
很遗憾,这个script 不支持 window。 window 下调用 make.bat 去完成编译。 go 的编译系统不能很好的支持cygwin, 这是让人觉得很不爽的地方。其实整个go 的编译应该建立在Makefile 机制上,而修改go 的编译脚本,让整个源代码不依赖于dist 去完成整个编译的过程,是让go 很好的支持各种不同平台的好的入手点。
有一些环境变量和 make.bash 结合的很紧密,也控制了编译的一些选项:
make.bash 的一些分析:
判断 run.bash 是否存在,不存在,则提示,退出
if [ ! -f run.bash ]; then
echo 'make.bash must be run from $GOROOT/src' 1>&2
exit 1
fi
判断当前是否在cygwin 或者 mingw ,或者其他window 环境下运行。 这里吧 cygwin 简单的划到window 的环境,是不合适的
case "$(uname)" in
*MINGW* | *WIN32* | *CYGWIN*)
echo 'ERROR: Do not use make.bash to build on Windows.'
echo 'Use make.bat instead.'
echo
exit 1
;;
esac
如果当前是 Darwin 系统,则在编译选项中加入设定最小 macos 版本的条件
if [ "$(uname)" == "Darwin" ]; then
# golang.org/issue/5261
mflag="$mflag -mmacosx-version-min=10.6"
fi
如果CC 没有设置,并且 gcc 在host 环境上没有, clang 确是在host 环境上存在,则设置编译器 为 clang
# if gcc does not exist and $CC is not set, try clang if available.
if [ -z "$CC" -a -z "$(type -t gcc)" -a -n "$(type -t clang)" ]; then
export CC=clang CXX=clang++
fi
编译生成 dist 程序,判断是否编译成功
${CC:-gcc} $mflag -O2 -Wall -o cmd/dist/dist -Icmd/dist "$DEFGOROOT" cmd/dist/*.c
# -e doesn't propagate out of eval, so check success by hand.
eval $(./cmd/dist/dist env -p || echo FAIL=true)
if [ "$FAIL" = true ]; then
exit 1
fi
如果调用脚本的时候,传递如参数 "--dist--tool" ,那么意味着仅仅编译dist,那么生成disk 之后,安装dist,然后退出
if [ "$1" = "--dist-tool" ]; then
# Stop after building dist tool.
mkdir -p "$GOTOOLDIR"
if [ "$2" != "" ]; then
cp cmd/dist/dist "$2"
fi
mv cmd/dist/dist "$GOTOOLDIR"/dist
exit 0
fi
否则,就重新编译所以的代码,编译 go_bootstrap,通过执行命令 dist bootstrap
echo "# Building compilers and Go bootstrap tool for host, $GOHOSTOS/$GOHOSTARCH."
buildall="-a"
if [ "$1" = "--no-clean" ]; then
buildall=""
shift
fi
./cmd/dist/dist bootstrap $buildall $GO_DISTFLAGS -v # builds go_bootstrap
用 go_bootstrap 完成整个编译过程
if [ "$GOHOSTARCH" != "$GOARCH" -o "$GOHOSTOS" != "$GOOS" ]; then
echo "# Building packages and commands for host, $GOHOSTOS/$GOHOSTARCH."
# CC_FOR_TARGET is recorded as the default compiler for the go tool. When building for the host, however,
# use the host compiler, CC, from `cmd/dist/dist env` instead.
CC=$CC GOOS=$GOHOSTOS GOARCH=$GOHOSTARCH \
"$GOTOOLDIR"/go_bootstrap install -ccflags "$GO_CCFLAGS" -gcflags "$GO_GCFLAGS" -ldflags "$GO_LDFLAGS" -v std
echo
fi
echo "# Building packages and commands for $GOOS/$GOARCH."
CC=$CC_FOR_TARGET "$GOTOOLDIR"/go_bootstrap install $GO_FLAGS -ccflags "$GO_CCFLAGS" -gcflags "$GO_GCFLAGS" -ldflags "$GO_LDFLAGS" -v std
echo