robot
最新文章(10)
這並不是一個創新
Tossug 2/9 SVG 加 XBL 分享
SVG and XBL No Widget
OpenSource 與嵌入式系統
Internet 的工人智慧
GCC Spec Files
Android Native code 的繪圖方法
Android Native code 不用 NDK
基於隱私的一種 DRM
Build Android SDK on FreeBSD
首頁
新編
最新留言
Entries RSS
重要關鍵字(10)
coding (120)
Python (93)
FreeBSD (71)
WEB (61)
雜記 (48)
URL (48)
hardware (46)
javascript (36)
Linux (31)
blog (30)
所有關鍵字
新增 URL
拆解 make world of FreeBSD - 2006 11/06 更新
by thinker
2 Columns
關鍵字:
FreeBSD
$FreeBSD$ 和大部分 $Linux$ disto 最大兮差別,就是 $ports$ 和 make world。透過 make world,簡單兮命令,就可以對 source code 開始,make 出整個系統。透過 make world,update 汝兮系統。 == 六個階段 == 這是 src/Makefile.inc1 裡面兮一段文字 {{{ # # Building a world goes through the following stages # # 1. legacy stage [BMAKE] # This stage is responsible for creating compatibility # shims that are needed by the bootstrap-tools, # build-tools and cross-tools stages. # 2. bootstrap-tools stage [BMAKE] # This stage is responsible for creating programs that # are needed for backward compatibility reasons. They # are not built as cross-tools. # 3. build-tools stage [TMAKE] # This stage is responsible for creating the object # tree and building any tools that are needed during # the build process. # 4. cross-tools stage [XMAKE] # This stage is responsible for creating any tools that # are needed for cross-builds. A cross-compiler is one # of them. # 5. world stage [WMAKE] # This stage actually builds the world. # 6. install stage (optional) [IMAKE] # This stage installs a previously built world. # }}} 描述著 make world 這個簡單動作,所包括兮 6 個階段。 $FreeBSD$ 透過 WMAKE_TGTS 定義這 6 個階段兮內容和順序: {{{ WMAKE_TGTS= .if !defined(SUBDIR_OVERRIDE) WMAKE_TGTS+= _worldtmp _legacy _bootstrap-tools .endif WMAKE_TGTS+= _cleanobj _obj _build-tools .if !defined(SUBDIR_OVERRIDE) WMAKE_TGTS+= _cross-tools .endif WMAKE_TGTS+= _includes _libraries _depend everything .if $${TARGET_ARCH} == "amd64" && !defined(NO_LIB32) WMAKE_TGTS+= build32 .endif buildworld: $${WMAKE_TGTS} # 控制了內容 .ORDER: $${WMAKE_TGTS} # 控制了次序 }}} buildworld 這個 target,依倭著 WMAKE_TGTS 定義兮 6 個階段。 .ORDER 這行,定義 target 執行兮順序,也就是依照 WMAKE_TGTS 所定義兮順序,依順序執行。 == 用自己產生自己 == 整個 buildworld 兮過程,基本上是利用 $FreeBSD$ 本身所提供兮工具,來編譯自己。但是這寡工具,一開始攏還是 source code 而已,還無法直接使用。所以就要先利用現有兮舊系統頂頭,所提兮舊版工具來 make source code 所提供兮新版本工具。然後,才使用 make 出來兮新版本,來 make 新系統,乎新系統得著新版工具所能提供兮利,以較好兮效能和功能執行。利用舊系統提供兮舊版工具所 make 出來兮工具,只是作為 make 新系統所用兮臨時工具,然後被丟掉。因為尹是用舊工具所 make 出兮,在執行上並沒得到最新版本所能提供兮利,所以並昧直接用在新系統。而是重新用臨時工具,重新 make 出新環境所用兮 binary 版本。由於臨時版本兮工具,是用新版本兮 source code 所編譯兮,所以用尹所 make 出來兮新系統,也有著新版本所提供兮利。由於 buildworld,會使用著自己本身兮 source code ,編出工具,然後才用這寡工具來 make 整個系統,產生最後兮目的碼。因此,必需先利用舊系統所提供兮工具,來編譯 make 新系統所需要兮臨時工具。 == 只有一份 source 和 makefile == 由於編譯臨時工具時,新環境還未建立,以使用舊環境兮工具來編譯。所以 make 臨時工具時,Makefile.inc1 透過設定環境變數和參數,以無同兮設定執行 make,使得臨時工具能夠使用舊系統所提供兮舊版本兮工具來編譯。透過這種改變設定兮方法,可以利用同一份 makefile,但是實際上卻是使用無同版本兮工具和使用無同兮參數和環境變數,來 make 臨時工具。下面就是佇 legacy 這個階段,透過設定環境變數和參數兮方式,用舊環境兮工具編工具兮例: {{{ BMAKE= MAKEOBJDIRPREFIX=$${WORLDTMP} \ $${BMAKEENV} $${MAKE} -f Makefile.inc1 \ DESTDIR= \ BOOTSTRAPPING=$${OSRELDATE} \ -DNO_HTML -DNO_INFO -DNO_LINT -DNO_MAN -DNO_NLS -DNO_PIC \ -DNO_PROFILE -DNO_SHARED -DNO_CPU_CFLAGS -DNO_WARNS _legacy: [...CUT....] $${_+_}cd $${.CURDIR}; $${BMAKE} legacy }}} buildworld 透過依倭 _legacy 這個 target,以著無同兮設定,重新執行 make,以 make legacy 這個 target。在 legacy 兮階段,由於產生兮工具只是用來編譯最後兮目的碼兮臨時工具,所以只產生必要的部分。其它如 HTML 兮說明文件,在 buildworld 過程並用昧著。所以透過定義 NO_HTML (-DNO_HTML)跳過,以避免沒必要兮動作。 下面就是 legacy 兮內容,也就是 make src/tools/build 這個 sub-tree,而且安裝佇 /usr/obj/legacy/ 下面。DESTDIR=$${MAKEOBJDIRPREFIX}/legacy 就是設定安裝兮目錄。 {{{ legacy: .if $${BOOTSTRAPPING} < 503000 @echo "ERROR: Source upgrades from versions prior to 5.3 not supported."; false .endif .for _tool in tools/build $${_+_}@$${ECHODIR} "===> $${_tool} (obj,includes,depend,all,install)"; \ cd $${.CURDIR}/$${_tool}; \ $${MAKE} DIRPRFX=$${_tool}/ obj; \ $${MAKE} DIRPRFX=$${_tool}/ DESTDIR=$${MAKEOBJDIRPREFIX}/legacy includes; \ $${MAKE} DIRPRFX=$${_tool}/ depend; \ $${MAKE} DIRPRFX=$${_tool}/ all; \ $${MAKE} DIRPRFX=$${_tool}/ DESTDIR=$${MAKEOBJDIRPREFIX}/legacy install .endfor }}} 透過 .for loop,編譯所需要兮子目錄,產生 binary。$FreeBSD$ 透過這個方式,為每一個步驟設定不同兮編譯參數,編譯每一個子目錄。透過無同兮設定,這寡子目錄只需要一份 makefile 或是 script,就可以應付無同階段兮需求。避免因為環境兮無同,為每一階段編寫無同兮 makefile 和 script。例如說,只要改變環境變數 PATH,就可以執行無同版本兮 gcc,以選擇所需要兮環境。 == 新系統提供最新兮效能和功能 == 除了 everything: 這個 target 是完全用內附兮 source code,所產生兮臨時工具來編譯新系統。前它兮之前階段,攏是用舊環境所提供兮工具,來產生 everything: 所需要兮臨時工具。雖然本身提供尚新兮功能和品質,但是本身執行時,並無最新版本兮效能和功能。這寡臨時工具,大部分也是 buildworld 所 build 出兮新環境兮一部分,所以最後也會佇 everything: 重新用臨時工具,重新編譯一次,以得到最好兮結果。這之前所產生兮臨版本,攏會被丟掉,昧直拿來作為新環境兮一部分。 這個過程中,有可能用臨時工具來產生新環境所用兮 binary 版本,也就是用自己來重新產生自己。這聽起來稍寡奇怪,但是在軟體系統常常看著。尤其是系統工具,常會發生用自己來編譯自己兮情形。通常是用舊版來 make 新版兮 source code,產生 bootstrap 版本兮目的碼。然後才用 bootstrap 版本,重新 make 新版 source code。兩階段 make source code,其目的就是希望工具本身,能成為工具本身兮加惠者。乎新工具兮執行,也能得著本身所提供兮利,使用最新兮效能和功能。 buildworld 透過數個步驟,每次攏透過設定 PATH 或參數,利用之前所產生兮工具,來產生新兮工具,最後才完成準備,正式執行 everything: 這個 target,產生最後兮結果。 == everything: == buildworld 兮最後一個階段,就是利用之前階段所 make 出兮臨時工具,make 出新系統。像下面所示: {{{ CROSSENV= MAKEOBJDIRPREFIX=$${OBJTREE} \ MACHINE_ARCH=$${TARGET_ARCH} \ MACHINE=$${TARGET} \ CPUTYPE=$${TARGET_CPUTYPE} \ GROFF_BIN_PATH=$${WORLDTMP}/legacy/usr/bin \ GROFF_FONT_PATH=$${WORLDTMP}/legacy/usr/share/groff_font \ GROFF_TMAC_PATH=$${WORLDTMP}/legacy/usr/share/tmac WMAKEENV= $${CROSSENV} \ _SHLIBDIRPREFIX=$${WORLDTMP} \ INSTALL="sh $${.CURDIR}/tools/install.sh" \ PATH=$${TMPPATH} WMAKE= $${WMAKEENV} $${MAKE} -f Makefile.inc1 DESTDIR=$${WORLDTMP} everything: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 4.4: building everything" @echo "--------------------------------------------------------------" $${_+_}cd $${.CURDIR}; $${WMAKE} par-all }}} everything: 重新執行 make 指令,以進行 par-all 這個 target。WMAKE 定義各種環境變數和 make 參數,以尚新兮臨時工具,make 新系統。譬如,在 WMAKEENV 重新定義了 PATH,使得執行各種工具來產生新系統時,可以先走找放臨時工具兮路徑,如果若是找無,才用其它路徑內,舊系統兮工具。 {{{ .for __target in all clean cleandepend cleandir depend includes obj .for entry in $${SUBDIR} $${entry}.$${__target}__D: .PHONY $${_+_}@if $test$ -d $${.CURDIR}/$${entry}.$${MACHINE_ARCH}; then \ $${ECHODIR} "===> $${DIRPRFX}$${entry}.$${MACHINE_ARCH} ($${__target$$ edir=$${entry}.$${MACHINE_ARCH}; \ cd $${.CURDIR}/$$$${edir}; \ else \ $${ECHODIR} "===> $${DIRPRFX}$${entry} ($${__target})"; \ edir=$${entry}; \ cd $${.CURDIR}/$$$${edir}; \ fi; \ $${MAKE} $${__target} DIRPRFX=$${DIRPRFX}$$$${edir}/ .endfor par-$${__target}: $${SUBDIR:S/$$/.$${__target}__D/} .endfor }}} 這一段 script 定義了 par-all par-clean ... par-obj 等 target。利用外層 for loop 將 __target 代入 all clean cleandepend ... obj 等值,以產生 par-all par-clean ... 等 target。這些 target 的動作,就是進入每一個子目錄,執行 all clean ... 等 target。譬如 par-all 就是進入每一個子目錄執行 make all。 == Makefile 的功能 == 2006 10/24 更新 前面都是在談 Makefile.inc1 的內容,難道 Makefile 都沒功能。那還要 Makefile 做什麼?其實 Makefile 的主要的功能是從 source 作出新的 make,然後再以新的 make 執行 Makefile.inc1 的內容。所以你下 make XXX 指令時,大部分的 target XXX 都是在 Makefile 執行新的 make ,並指定執行 Makefile.inc1 裡的 target XXX 。很清楚的, Makefile 的功能就檢查 make 的版本,並產生新版的 make , 以使用最新的 make 來執行 makefile 。 {{{ PATH= /sbin:/bin:/usr/sbin:/usr/bin MAKEOBJDIRPREFIX?= /usr/obj _MAKEOBJDIRPREFIX!= /usr/bin/env -i PATH=$${PATH} $${MAKE} \ $${.MAKEFLAGS:MMAKEOBJDIRPREFIX=*} __MAKE_CONF=$${__MAKE_CONF} \ -f /dev/null -V MAKEOBJDIRPREFIX dummy .if !empty(_MAKEOBJDIRPREFIX) .error MAKEOBJDIRPREFIX can only be set in environment, not as a global\ (in make.conf(5)) or command-line variable. .endif MAKEPATH= $${MAKEOBJDIRPREFIX}$${.CURDIR}/make.$${MACHINE} BINMAKE= \ `if [ -x $${MAKEPATH}/make ]; then echo $${MAKEPATH}/make; else echo $${MA -m $${.CURDIR}/share/mk _MAKE= PATH=$${PATH} $${BINMAKE} -f Makefile.inc1 }}} Makefile 使用 _MAKE 執行 Makefile.inc1 裡的 target。例如 buildworld 這個 target 的 rule 是 $${_MAKE} target ,這指令會使用新的 make 執行 Makefile.inc1 定義的 buildworld 。 {{{ TGTS= all all-man buildenv buildenvvars buildkernel buildworld check-old \ checkdpadd clean cleandepend cleandir delete-old delete-old-libs \ depend distribute distributeworld distrib-dirs distribution doxygen \ everything hierarchy install installcheck installkernel \ installkernel.debug reinstallkernel reinstallkernel.debug \ installworld kernel-toolchain libraries lint maninstall \ obj objlink regress rerelease showconfig tags toolchain update \ _worldtmp _legacy _bootstrap-tools _cleanobj _obj \ _build-tools _cross-tools _includes _libraries _depend \ build32 distribute32 install32 ........................ $${TGTS}: $${_+_}@cd $${.CURDIR}; \ $${_MAKE} $${.TARGET} }}} == Cross-build == 2006 10/06 更新 為了要 porting 到不同的平臺, $FreeBSD$ 在 Makefile.inc1 裡,檢查使用者指定之平臺是否和目前機器的平臺相同。 {{{ # Guess machine architecture from machine type, and vice versa. .if !defined(TARGET_ARCH) && defined(TARGET) TARGET_ARCH= $${TARGET:S/pc98/i386/:S/sun4v/sparc64/} .elif !defined(TARGET) && defined(TARGET_ARCH) && \ $${TARGET_ARCH} != $${MACHINE_ARCH} TARGET= $${TARGET_ARCH} .endif # Otherwise, default to current machine type and architecture. TARGET?= $${MACHINE} TARGET_ARCH?= $${MACHINE_ARCH} }}} 首先,檢查使用者是否指定了 TARGET 和 TARGET_ARCH 這兩個變數。若只指定其一,則該變數去猜測另一個變數的應該有的內容。TARGET 是指定目標平臺,例如 i386 、 pc98 或 sun4v 之類。而 TARGET_ARCH 則指定目標平臺所使用的 CPU 架構,如 i386 、 sparc64 或 powerpc。不同平臺,可能使用一樣的 CPU 架構。若使用者沒指定 TARGET 或 TARGET_ARCH 時,直接以目前執行的平臺(MACHINE/MACHINE_ARCH),做為目標機器。 {{{ KNOWN_ARCHES?= amd64 arm i386 i386/pc98 ia64 powerpc sparc64 sparc64/sun4v .if $${TARGET} == $${TARGET_ARCH} _t= $${TARGET} .else _t= $${TARGET_ARCH}/$${TARGET} .endif .for _t in $${_t} .if empty(KNOWN_ARCHES:M$${_t}) .error Unknown target $${TARGET_ARCH}:$${TARGET}. .endif .endfor }}} 這一段指含,檢查指定的目標平臺是否為 $FreeBSD$ 所支援。KNOWN_ARCHES 例出所有目前支援的架構。empty(KNOWN_ARCHES:M$$$${_t}) 檢查指定的平臺,是否例在 KNOWN_ARCHES 裡。 {{{ .if $${TARGET} == $${MACHINE} TARGET_CPUTYPE?=$${CPUTYPE} .else TARGET_CPUTYPE?= .endif .if !empty(TARGET_CPUTYPE) _TARGET_CPUTYPE=$${TARGET_CPUTYPE} .else _TARGET_CPUTYPE=dummy .endif _CPUTYPE!= MAKEFLAGS= CPUTYPE=$${_TARGET_CPUTYPE} $${MAKE} \ -f /dev/null -m $${.CURDIR}/share/mk -V CPUTYPE .if $${_CPUTYPE} != $${_TARGET_CPUTYPE} .error CPUTYPE global should be set with ?=. .endif }}} 這一段檢查 CPUTYPE 是否被定死在 /etc/make.conf 之類的地方,使的目前的指定值,無法生效。因為如果設定在 /etc/make.conf 之類的地方,又不是使用 ?= 指定,則 $command line$ 指定的值將無法生效。 透過指定 TARGET 和 TARGET_ARCH, makefile 依其內容做平臺的選擇。 == 結論 == everything: 透過改變各種環境變數和參數兮方式,重新執行 make par-all,將每一個子目錄下兮 source 全部重新 make。在重新 make 之後,所產生兮新系統,不但提供最新版兮能力,也有新版本本身所提供兮執行表現。$FreeBSD$ 完全利用兩階段 make 兮優勢,只用 makefile 就可以產生一代又一代兮新系統。
最後更新時間: 2006-11-06 23:28:47 CST |
引用
查詢:
COMMENTS: