1. 程式人生 > >Go 編譯過程分析(一) -- 編譯指令碼

Go 編譯過程分析(一) -- 編譯指令碼

http://blog.csdn.net/free2o/article/details/38417293

go 語言最近很火,與時俱進,我也看了看go 的語法。

    看起來 go 還是不錯的,有很多新的feature。 就下載了程式碼研究了一下。

    go 的 src 目錄下面存在三套編譯檔案:

  1. window 平臺, 所有 bat 檔案
  2. plan9 平臺,所有 rc 檔案
  3. unix 類平臺,所有bash 檔案

     以 unix 編譯檔案為例, go 的編譯入口時在 src/all.bash , 這是一個bash 腳步, 這個腳步只是簡單的呼叫了 make.bash 在腳步結束之後,呼叫 dist banner 輸出編譯的資訊。 

  1. set -e  
  2. if [ ! -f make.bash ]; then  
  3.         echo 'all.bash must be run from $GOROOT/src' 1>&2  
  4.         exit 1  
  5. fi  
  6. OLDPATH="$PATH"  
  7. . ./make.bash "[email protected]" --no-banner  
  8. bash run.bash --no-rebuild  
  9. PATH="$OLDPATH"  
  10. $GOTOOLDIR/dist banner  # print build info  

    dist 是在 make.bash 中生成的一個可執行檔案,go 的所有編譯都是在這個檔案的控制下完成的。 個人認為這並不是一個好的設計,導致維護編譯系統的成功過高,如果要修改一下編譯選項,往往要修改 dist 原始碼。dist 的程式碼在目錄: /src/cmd/dist 下。

  dist 這個命令列程式支援如下幾個引數:   

  1. "banner         print installation banner\n" ; 列印安裝的一些資訊  
  2. "bootstrap      rebuild everything\n"        ; 重新編譯所有的 go 程式碼  
  3. "clean          deletes all built files\n"   ; 清楚編譯的 go 程式碼  
  4. "env [-p]       print environment (-p: include $PATH)\n"  ; 列印編譯的環境  
  5. "install [dir]  install individual directory\n"  ;安裝某一個目錄。會編譯目錄下程式碼,安裝生成檔案  
  6. "version        print Go version\n"              ;大約go版本資訊  

  想要研究編譯細節一定要看看這個程式的程式碼,後續詳細分析。

  make.bash, 同樣是一個 bash 腳步,開啟這個腳步,可以看到這個腳步主要做了如下幾件事情:

  1. 根據不同的系統,以及引數進行一些初始化的工作
  2. 編譯生成 dist,呼叫dist 完成整個go 的編譯. dist bootstrap
  3. 用編譯生成的 go_bootstrap 完成整個安裝過程

    很遺憾,這個script 不支援 window。 window 下呼叫 make.bat 去完成編譯。  go 的編譯系統不能很好的支援cygwin, 這是讓人覺得很不爽的地方。其實整個go 的編譯應該建立在Makefile 機制上,而修改go 的編譯指令碼,讓整個原始碼不依賴於dist 去完成整個編譯的過程,是讓go 很好的支援各種不同平臺的好的入手點。

    有一些環境變數和 make.bash 結合的很緊密,也控制了編譯的一些選項:

  1. GOROOT_FINAL :  這個變數用來表明 go 最終安裝的路徑,如果不設定,預設值為當前原始碼的路徑
  2. GOHOSTARCH :   設定編譯 go 語音的電腦的 ARCH(架構) , 386 or amd64 or arm
  3. GOARCH : 編譯生成的 go 所執行的 ARCH。
  4. GOOS : 編譯生成的 go 所允許的作業系統
  5. GO_GCFLAGS: 編譯 5g/6g/8g 時,額外指定的引數
  6. GO_LDFLAGS : 編譯 5l/6l/8l 時, 額外指定的引數
  7. GO_CCFLAGS  :  編譯 5c/6c/8c 時,額外指定的引數
  8. CGO_ENABLED:   是否支援 cgo,設定為1 的話,cgo 相關檔案會被編譯,設定為0 的話,則不會編譯
  9. GO_EXTLINK_ENABLED : 是否使用Host 環境的 link。設定1的話,則會使用編譯環境中帶的聯結器,0,在不會
  10. CC : 設定C編譯器名字, gcc 還是 clang , 這個設定的是 host 環境的編譯器
  11. CC_FOR_TARGET : 設定C編譯器名字,這個設定的是 能夠生成目標環境程式碼的編譯器
  12. CXX_FOR_TARGET: 設定CXX編譯器名字, g++ or clang++這個設定的是 能夠生成目標環境程式碼的編譯器
  13. GO_DISTFLAGS :  為 dist bootstrap 提供額外的引數.

   make.bash 的一些分析:

    判斷 run.bash 是否存在,不存在,則提示,退出

  1. if [ ! -f run.bash ]; then  
  2.         echo 'make.bash must be run from $GOROOT/src' 1>&2  
  3.         exit 1  
  4. fi  

     判斷當前是否在cygwin 或者 mingw ,或者其他window 環境下執行。 這裡吧 cygwin 簡單的劃到window 的環境,是不合適的

  1. case "$(uname)" in  
  2. *MINGW* | *WIN32* | *CYGWIN*)  
  3.        echo 'ERROR: Do not use make.bash to build on Windows.'  
  4.        echo 'Use make.bat instead.'  
  5.        echo  
  6.        exit 1  
  7.        ;;  
  8. esac  
  如果當前是 Darwin 系統,則在編譯選項中加入設定最小 macos 版本的條件
  1. if [ "$(uname)" == "Darwin" ]; then  
  2.         # golang.org/issue/5261  
  3.         mflag="$mflag -mmacosx-version-min=10.6"
  4. fi  
如果CC 沒有設定,並且 gcc 在host 環境上沒有, clang 確是在host 環境上存在,則設定編譯器 為 clang
  1. # if gcc does not exist and $CC is not set, try clang if available.  
  2. if [ -z "$CC" -a -z "$(type -t gcc)" -a -n "$(type -t clang)" ]; then  
  3.         export CC=clangCXX=clang++  
  4. fi  

編譯生成 dist 程式,判斷是否編譯成功

  1. ${CC:-gcc} $mflag -O2 -Wall -o cmd/dist/dist -Icmd/dist "$DEFGOROOT" cmd/dist/*.c  
  2. # -e doesn't propagate out of eval, so check success by hand.  
  3. eval $(./cmd/dist/dist env -p || echo FAIL=true)  
  4. if [ "$FAIL" = true ]; then  
  5.         exit 1  
  6. fi  
如果呼叫指令碼的時候,傳遞如引數 "--dist--tool" ,那麼意味著僅僅編譯dist,那麼生成disk 之後,安裝dist,然後退出
  1. if [ "$1" = "--dist-tool" ]; then  
  2.         # Stop after building dist tool.  
  3.         mkdir -p "$GOTOOLDIR"  
  4.         if [ "$2" != "" ]; then  
  5.                 cp cmd/dist/dist "$2"  
  6.         fi  
  7.         mv cmd/dist/dist "$GOTOOLDIR"/dist  
  8.         exit 0  
  9. fi  
否則,就重新編譯所以的程式碼,編譯 go_bootstrap,通過執行命令 dist bootstrap
  1. echo "# Building compilers and Go bootstrap tool for host, $GOHOSTOS/$GOHOSTARCH."  
  2. buildall="-a"
  3. if [ "$1" = "--no-clean" ]; then  
  4.         buildall=""
  5.         shift  
  6. fi  
  7. ./cmd/dist/dist bootstrap $buildall $GO_DISTFLAGS -v # builds go_bootstrap  

用 go_bootstrap 完成整個編譯過程

  1. if [ "$GOHOSTARCH" != "$GOARCH" -o "$GOHOSTOS" != "$GOOS" ]; then  
  2.         echo "# Building packages and commands for host, $GOHOSTOS/$GOHOSTARCH."  
  3.         # CC_FOR_TARGET is recorded as the default compiler for the go tool. When building for the host, however,  
  4.         # use the host compiler, CC, from `cmd/dist/dist env` instead.  
  5.         CC=$CC GOOS=$GOHOSTOS GOARCH=$GOHOSTARCH \  
  6.                 "$GOTOOLDIR"/go_bootstrap install -ccflags "$GO_CCFLAGS" -gcflags "$GO_GCFLAGS" -ldflags "$GO_LDFLAGS" -v std  
  7.         echo  
  8. fi  
  9. echo "# Building packages and commands for $GOOS/$GOARCH."  
  10. CC=$CC_FOR_TARGET "$GOTOOLDIR"/go_bootstrap install $GO_FLAGS -ccflags "$GO_CCFLAGS" -gcflags "$GO_GCFLAGS" -ldflags "$GO_LDFLAGS" -v std  
  11. echo