人生就是有那麼多的 but,就是會有專案跟你用的 building system 不一致,你被迫要 merge 兩個不同的 building system。
node.js 的 building system 沒有考慮過被當作 sub-project 的狀況,無法在 source code 以外的資料夾進行編譯。用比較精確的說法來說,就是 node.js 的 building system 沒有區分 build tree 和 source tree,無法進行 VPATH builds (又稱作 parallel builds,因為 parallel build 常會讓人誤以為是分散式編譯,所以大家才會又發明一個饒口的 VPATH builds)
這其實是滿不好的設計。以前遇到這類型的專案,大多都是為了政治因素, 不給其他專案用的反智現象。但是 node.js 應該只是開發者不了解這個需求,或者還沒有人嘗試過使用 node.js 成為子專案,所以也沒有人回報這個需求。
首先先看了這篇:Third-Party MakefileS
當中說到,利用 SUBDIRS 和 DIST_SUBDIRS 可以讓 make 進入 sub-project 執行 Makefile。這方法其實只解決了一半問題,在 node.js 以及大多數的 open source project都會面臨到另外一個問題。
大多數 open source project developers 通常會將 ${builddir} 和 ${srcdir} 分開,也就是說,程式碼和編譯的資料夾,是不同的地方。如果用 SUBDIRS 和 DIST_SUBDIRS,則 make 會在 ${builddir} 去找只存在 ${srcdir} 的 sub-project。
舉例來說,如果我的專案叫做 utils,然後我把 node.js 放到 utils/nodejs 資料夾下,然後在 utils/Makefile.am 這樣寫:
SUBDIRS = nodejs
接下來編譯的時候,建立一個 build-utils 資料夾,然後執行
cd build-utils
../utils/configure
make all
則在 make 的時候,make 會跟我說找不到 nodejs 資料夾。訊息如下:
Making all in nodejs
/bin/sh: line 0: cd: nodejs: No such file or directory
這是因為 nodejs 是在 utils 底下,而不是在 build-utils 底下,所以 make 自然找不到。這時候有兩種解法。
第一招 - Makefile Adapter
如果使用的是 GNU make 的話,一種方式是增加一個新的 Makefile,讓 make 優先讀取,而這個優先讀取的 Makefile,去 include 原來的 Makefile。這是一種 Adapter 的概念,用自己的 Makefile,去包裝他人的 Makefile。
如果不使用 -f 指定 makefile 的話,make 讀取 makefile 的順序是 GNUmakefile, makefile, 和 Makefile,所以大多數的專案都會使用大寫的 Makefile,好讓有需要時,小寫的 makefile 派上用場。
其實 Makefile 是大寫還是小寫,也可以看出一個 open source project 功力,通常很多人使用的專案會知道要用大寫的版本。
這時候要讓 VPATH builds 裡面生出能夠優先讀取的 Makefile,所以通常會在 source tree 當中增加一個 makefile.in 或 GNUmakefile.in,然後在 configure.ac 當中利用 AC_CONFIG_FILES 生成 VPATH builds 的 makefile/GNUmakefile。如果原生的 makefile 檔名是 Makefile,則 makefile.in/GNUmakefile.in 的內容應該如下:
# First, include the real Makefile
include Makefile
# Then, define the other targets needed by Automake Makefiles. (A rule-wrapper to original Makefile)
.PHONY: dvi pdf ps info html check
dvi pdf ps info html:
check: test
這個新的 Makefile.am,要把原生 Makefile 的 rule 轉接到 Automake 的 rule 上,做一個 rule adapter。
第二招 - Makefile Proxy
第二招原理也是很像,主要的目的是在避免 include Makefile。因為有些專案就是那麼白目,make file 的檔名會用 makefile 或者是 GNUmakefile,使得 Makefile Adapter 無法使用。Makefile proxy 的做法是將原來的 makefile 改名,然後利用 make -f 的方式,執行真正的 Makefile。舉例來說,可以將原來的 makefile 改名為 makefile.real,然後讓 Makefile.am 當中去執行 $(MAKE) -f makefile.real。
為了要保留原來 Automake 所提供的參數,所以通常會在 $(MAKE) -f makefile.real 之後,加上 `$(AM_MAKEFLAGS) target`
Makefile.am 會像這樣:
all-local:
cd subdir && $(MAKE) -f $(srcdir)/Makefile.real $(AM_MAKEFLAGS) all
不論是哪一招,都無法克服一個問題就是:如果這個專案不 support VPATH builds,則不管是哪一招都沒有漂亮的解決方案。如果該專案要支援 VPATH builds (而且通常你會需要支援),則需要重寫該專案的 Makefile
沒有留言:
張貼留言