使用golang已经相当一段时间,当被问到对于golang编译运行的过程时还是不能完全的讲出来还是之前C/C++的思路。所以今天就将这go的一过程搞清楚。
go源码目录结构
先看一下安装go的源码目录结构,在此是1.7.3版本2016-10-20update
|– AUTHORS — 文件,官方 Go语言作者列表 |– CONTRIBUTORS — 文件,第三方贡献者列表 |– LICENSE — 文件,Go语言发布授权协议 |– PATENTS — 文件,专利 |– README — 文件,README文件 |– VERSION — 文件,当前Go版本 |– api — 目录,包含所有版本API列表,方便IDE使用 |– doc — 目录,Go语言的各种文档 |– favicon.ico — 文件,官网logo |– lib — 目录,文档模板 |– misc — 目录,其他的一些工具,大部分是各种编辑器的Go语言支持,还有cgo的例子等 |– robots.txt — 文件,搜索引擎robots文件 |– src — 目录,Go语言源码:基本工具(编译器等)、标准库 |– test — 目录,包含很多测试程序包含main包的测试和一些fixbug测试 ps:其实在1.5之前还要一个include目录,主要是go的基本工具依赖的库的头文件,然而自举后已不再需要。
源码安装脚本
通过源码安装Go非常简单,它提供了方便的脚本。 脚本在src目录下
所以,通过源码安装Go,一般cd到src目录执行./all.bash。如果不想测试标准库,可以直接./make.bash,这样会比较快。可知,go 的编译入口时在 src/all.bash,它调用了 make.bash,结束之后,调用 dist banner 输出编译的信息。
dist 是在 make.bash 中生成的一个可执行文件,go的所有编译都是在这个文件的控制下完成的。 Make.dist 被其他Makefile文件引用,比如cmd下面的很多工具中的Makefile文件。这个文件的作用是:运行go tool dist去安装命令,同时在安装过程中会打印出执行了该文件的目录名。可见,在源码安装Go的过程中,打印出的大部分信息就是这个文件的作用。
下面我以注释的形式说明脚本都做了什么
// 当脚本中某个命令返回非零退出状态时,脚本退出 set -e // 限制文件路径,要求all.bash必须在make.bash所在的目录运行 if [ ! -f make.bash ]; then echo 'all.bash must be run from $GOROOT/src' 1>&2 exit 1 fi // 保存当前PATH OLDPATH="$PATH" // 执行make.bash并传递–no-banner参数 . ./make.bash "$@" --no-banner // 执行run.bash并传递–no-rebuild参数 bash run.bash --no-rebuild PATH="$OLDPATH" // 执行dist工具并传递 banner参数,打印build信息,原来所有的编译信息都是由dist产生的 $GOTOOLDIR/dist banner # print build info
dist很重要,这里详细说下参数说明
ok,all.bash其实只是外皮,真正做事情的是脚本make.bash和run.bash.
make.bash 主要做了几件事:
// 当脚本中某个命令返回非零退出状态时,脚本退出 set -e // GOBIN失效 unset GOBIN # Issue 14340 // 检查run.bash路径 if [ ! -f run.bash ]; then echo 'make.bash must be run from $GOROOT/src' 1>&2 exit 1 fi // 判断当前是否在cygwin 或者 mingw ,或者其他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 // 下面是对工具和平台的检查 // Test for bad ld. if ld --version 2>&1 | grep 'gold.* 2\.20' >/dev/null; then echo 'ERROR: Your system has gold 2.20 installed.' echo 'This version is shipped by Ubuntu even though' echo 'it is known not to work on Ubuntu.' echo 'Binaries built with this linker are likely to fail in mysterious ways.' echo echo 'Run sudo apt-get remove binutils-gold.' echo exit 1 fi // Test for bad SELinux. # On Fedora 16 the selinux filesystem is mounted at /sys/fs/selinux, # so loop through the possible selinux mount points. for se_mount in /selinux /sys/fs/selinux do if [ -d $se_mount -a -f $se_mount/booleans/allow_execstack -a -x /usr/sbin/selinuxenabled ] && /usr/sbin/selinuxenabled; then if ! cat $se_mount/booleans/allow_execstack | grep -c '^1 1$' >> /dev/null ; then echo "WARNING: the default SELinux policy on, at least, Fedora 12 breaks " echo "Go. You can enable the features that Go needs via the following " echo "command (as root):" echo " # setsebool -P allow_execstack 1" echo echo "Note that this affects your system globally! " echo echo "The build will continue in five seconds in case we " echo "misdiagnosed the issue..." sleep 5 fi fi done # Test for debian/kFreeBSD. # cmd/dist will detect kFreeBSD as freebsd/$GOARCH, but we need to # disable cgo manually. if [ "$(uname -s)" == "GNU/kFreeBSD" ]; then export CGO_ENABLED=0 fi # Clean old generated file that will cause problems in the build. rm -f ./runtime/runtime_defs.go # Finally! Run the build. echo '##### Building Go bootstrap tool.' echo cmd/dist // 将当前源码目录设置为GOROOT export GOROOT="$(cd .. && pwd)" // GOROOT_BOOTSTRAP路径 GOROOT_BOOTSTRAP=${GOROOT_BOOTSTRAP:-$HOME/go1.4} if [ ! -x "$GOROOT_BOOTSTRAP/bin/go" ]; then echo "ERROR: Cannot find $GOROOT_BOOTSTRAP/bin/go." >&2 echo "Set \$GOROOT_BOOTSTRAP to a working Go tree >= Go 1.4." >&2 exit 1 fi if [ "$GOROOT_BOOTSTRAP" == "$GOROOT" ]; then echo "ERROR: \$GOROOT_BOOTSTRAP must not be set to \$GOROOT" >&2 echo "Set \$GOROOT_BOOTSTRAP to a working Go tree >= Go 1.4." >&2 exit 1 fi // 删除上次残留dist rm -f cmd/dist/dist // 编译生成dist GOROOT="$GOROOT_BOOTSTRAP" GOOS="" GOARCH="" "$GOROOT_BOOTSTRAP/bin/go" build -o cmd/dist/dist ./cmd/dist // 检查是否成功,设置相关环境变量 eval $(./cmd/dist/dist env -p || echo FAIL=true) if [ "$FAIL" = true ]; then exit 1 fi echo // 运行make.bash时传递--dist-tool参数可以仅编译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 // 重新构建 buildall="-a" // 传递--no-clean 表示不重新构建 if [ "$1" = "--no-clean" ]; then buildall="" shift fi // 构建go的引导工具 ./cmd/dist/dist bootstrap $buildall $GO_DISTFLAGS -v # builds go_bootstrap // 将dist工具移动到工具目录下,bootstrap可能清空tool目录 mv cmd/dist/dist "$GOTOOLDIR"/dist echo // 交叉编译的环境需要 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 -gcflags "$GO_GCFLAGS" -ldflags "$GO_LDFLAGS" -v std cmd echo fi echo "##### Building packages and commands for $GOOS/$GOARCH." CC=$CC_FOR_TARGET "$GOTOOLDIR"/go_bootstrap install $GO_FLAGS -gcflags "$GO_GCFLAGS" -ldflags "$GO_LDFLAGS" -v std cmd echo rm -f "$GOTOOLDIR"/go_bootstrap // 是否打印成功的提示信息 if [ "$1" != "--no-banner" ]; then "$GOTOOLDIR"/dist banner fi
run.bash 工作是编译和测试标准库,运行时以及语言测试套件。
// 当脚本中某个命令返回非零退出状态时,脚本退出 set -e // 测试go环境 eval $(go env) // 设置GOROOT export GOROOT # the api test requires GOROOT to be set. unset CDPATH # in case user has it set unset GOPATH # we disallow local import for non-local packages, if $GOROOT happens # to be under $GOPATH, then some tests below will fail unset GOBIN # Issue 14340 export GOHOSTOS export CC # no core files, please ulimit -c 0 [ "$(ulimit -H -n)" == "unlimited" ] || ulimit -S -n $(ulimit -H -n) [ "$(ulimit -H -d)" == "unlimited" ] || ulimit -S -d $(ulimit -H -d) // thread 限制 if ulimit -T &> /dev/null; then [ "$(ulimit -H -T)" == "unlimited" ] || ulimit -S -T $(ulimit -H -T) fi // go tool 测试 exec go tool dist test -rebuild "$@"
以上基本上算是了解了安装go都需要做些什么工作,go所依赖的东西是什么。后面继续研究go的编译链接过程。。。go go。。