2007年2月20日 星期二

autoconf手冊(六)

[本文轉載來源為:http://doc.sheup.com/linux/linux403.htm]

我如何解開死結?
如果Autoconf需要GNU m4並且GNU m4還有一個Autoconf configure腳本,
我如何解開這個死結?它好像是一個類似於雞和蛋的問題﹗
這實際上是一種誤解。雖然GNU m4帶有一個由Autoconf生成的configure腳本,但在營運腳本及安裝GNU m4的時候並不需要安裝Autoconf。只有在你需要修改m4的configure 腳本的時候,這只是少數幾個人(主要是它的維護者)必須去作的事,才需要Autoconf。

為什麼不使用Imake?
為什麼不用Imake來代替configure腳本?
有些人已經提出了這個問題,所以在改編之後,我把給他們的解釋寫在這裡。
下面是對Richard Pixley的問題的回答︰
由Autoconf生成的腳本經常地在它以前從未設定過的機器上工作。這就是說,它善於推斷新系統的配置。而Imake不能做到。
Imake使用含有主機特定數據的通用數據庫。對X11來說,這種方法具有意義是因為發布版本是由一個控制整個數據庫的總管機關管理的一組工具組成的。
GNU工具並不按這種模式發行。每個GNU工具都有一個維護者;這些維護者散佈在世界各地。使用統一的數據庫將使維護變成噩夢。 Autoconf可能成為這類數據庫,但實際上它沒有。不是列舉主機的倚賴性,它列舉的是程式的需求。
如果你把GNU套件看作一組本地工具,那麼問題就很相似了。但GNU開發工具可以作為交叉工具(cross tools)而在幾乎所有主機+目標機的組合中進行配置。所有的這些配置都可以同時(concurrency)安裝。它們甚至可以被配置成可以在不同主機上共享與主機獨立的訊息的形式。Imake不能處理這些問題。
Imake模板是標準的一種形式。GNU編碼標準在沒有強加相同的限制的情況下,解決了相同的問題。
下面是一些由Per Bothner撰寫的進一步的解釋︰
Imake的一個長處是它易於透過使用cpp的``#include''和宏機製生成大的Makefile。然而,cpp是不可編程的︰它含有有限的條件工具,而不含有循環。而且cpp不能檢查它的環境。
所有這些問題可以透過使用sh而不是cpp來解決。shell是完全可編程的、含有宏替換、可以執行(或者編製)其它的shell腳本,並且可以檢查它的環境。
Paul Eggert更詳細地闡述︰
使用Autoconf,安裝者不必假定Imake自身已經被安裝並且正常地工作了。這對於習慣使用Imake的人們來說,看起來不是突出的長處。但在許多主機上,並沒有安裝Imake或者缺省的安裝不能很好地工作,為此,要求安裝Imake就阻礙了在這些主機上使用由Imake配置的套裝軟件。例如,Imake模板和配置檔案可能不能適當地安裝在一個主機上,或者Imake創建過程可能會錯誤地假定所有的源代碼檔案都在一個大目錄樹中,或者Imake配置可能使用某個編譯器而包或者安裝器需要使用另一個編譯器,或者包需要的Imake的版本號與系統支援的版本號不匹配。這些問題在Autoconf中很少出現,這是因為包附帶屬於它自己的獨立配置處理器。
還有,Imake通常會在make和安裝者的C預處理器之間遇到難以預期的影響。這裡的基本問題是,C預處理器是為處理C程式而不是``Makefile''而設計的。這對Autoconf來說問題小得多,它使用通用目的預處理器m4,並且包的作者(而不是安裝者)以標準的模式進行預處理。
最後,Mark Eichin解釋道︰
Imake還不是完全可擴展的。為了把新特徵添加到Imake中,你需要提供你自己的項目模板,並且複製已經存在的特徵的主要部分。這意味著對於複雜的項目來說,使用由買主提供的(vendor-provided)Imake模板不能提供任何平衡作用--這是因為它們不包括你自己的項目的任何東西(除非它是一個X11程式)。

但是,另一方面︰

一個Imake勝過configure的長處是︰ ``Imakefile''總是趨向於比``Makefile.in''簡短(同樣地,冗餘較少)。但是,這兒有一個修正的方法--至少對於Kerberos V5樹來說,我們已經在整個樹中進行了修改以調用通用的 ``post.in''和``pre.in'' ``Makefile''片斷。這意味著大部分通用的東西,即使它們通常是在configure中設定的,也不必複製。

從版本1中升級
Autoconf第2版基本上與第1版是向後兼容的。但是,它給出了作某些事的更好方法,並且不再支援版本1中一些醜陋的東西。因此,根據你的``configure.in''檔案的複雜性,你可能必須作一些手工的工作以升級到版本2。本章指出了一些在升級的時候需要注意的問題。還有,可能你的configure腳本可以從版本2中的新特徵中獲得一些好處;在Autoconf發布包中的``NEWS''檔案概括了改變的部分。

首先,確認你安裝了1.1版或者更高版本的GNU m4,最好是1.3版或者更高版本。在1.1版之前的版本含有bug 以至於它不能與Autoconf版本2一同工作。版本1.3及其後的版本比早期的版本更快一些,這是因為1.3版的GNU m4 對轉換(diversions)進行了更有效的實現並且能夠在可以快速讀回的檔案中凍結(freeze)它的內部狀態。

改變了的檔案名
如果你隨Autoconf一起安裝了``aclocal.m4''(相對於特定套裝軟件的源代碼目錄中的``aclocal.m4''),你必須把它重命名為``acsite.m4''。參見用autoconf創建configure。

如果你與你的套裝軟件一同發布``install.sh'',就把它重命名為``install-sh''以便make的內置規則不會無意地從該檔案創建一個稱為``install''的檔案。AC_PROG_INSTALL將尋找這兩個名字的腳本,但最好使用新名字。

如果你使用``config.h.top''或者``config.h.bot'',你仍然可以使用它們,但如果你把它們混合到 ``acconfig.h''之中,將減少你的麻煩。參見用autoheader創建``config.h.in''。

改變了的Makefile
在你的``Makefile.in''檔案中添加``@CFLAGS@''、``@CPPFLAGS@''和``@LDFLAGS@'',以便它們可以在configure營運的時候利用環境中的這些變量的值。這樣做不是必須的,但對用戶來說比較方便。

對於AC_OUTPUT的每個非``Makefile''的輸入檔案,你還應該添加一條含有 ``@configure_input@''的註釋,以便輸出檔案將會包含一條註釋以說明它們是由configure生成的。自動地為每種人們在AC_OUTPUT中輸出的檔案選擇正確的註釋語法需要做太多的工作。

把``config.log''和``config.cache''添加到你要在distclean目標中刪除的檔案的清單中。

如果你的``Makefile.in''如下︰

prefix = /usr/local
exec_prefix = ${prefix}

你必須把它修改成︰

prefix = @prefix@
exec_prefix = @exec_prefix@

不使用``@''字符的老式的對這些變量的替換行為已經被刪除了。

改變了的宏
在Autoconf第2版中,重新命名了許多宏。你仍然可以使用舊名字,但新名字更清晰,並且易於找到相關文檔。關於為舊宏名提供新宏名的清單,參見陳舊的宏名。用autoupdate程式轉換你的``configure.in''以使用新的宏名。參見用autoupdate更新configure。

有些宏已經被能夠更好地完成工作的類似宏所代替,但在調用上並不兼容。如果你在營運autoconf時受到了關於調用過時宏的警告,你可以安全地忽略它們,但如果你按照列印的建議替換過時的宏,你的configure腳本通常可以工作的更好。特別地,報告測試結果的機製已經改變了。如果你使用了echo或者AC_VERBOSE(可能是透過AC_COMPILE_CHECK),如果你改用AC_MSG_CHECKING和AC_MSG_RESULT,你的configure腳本的輸出將更加美觀。參見列印消息。這些宏能夠更好地與緩存變量協同工作。參見緩存結果。

用autoupdate更新configure
程式autoupdate把使用Autoconf舊宏名的``configure.in''檔案更新為使用當前宏名的檔案。在Autoconf第2版中,大部分宏被重命名以使用一個更統一、更具有描述性的命名機製。關於對新的命名機製的描述,參見宏名。雖然舊宏名仍然可以工作(關於舊宏名和對應的新宏名的清單,參見陳舊的宏名),如果你更新它們以使用新的宏名,你可以使你的 ``configure.in''檔案更加可讀並且易於使用當前的Autoconf文檔。

如果沒有給出參數,autoupdate就更新``configure.in'',並且透過添加後綴``~'' (或者在設定了環境變量SIMPLE_BACKUP_SUFFIX的時候,使用該環境變量的值)以備份原始版本。如果你帶參數調用autoupdate,它就讀入那個檔案而不是讀入``configure.in'',並且把更新的檔案輸出到標準輸出。

autoupdate接受下列選項︰

--help
-h
列印命令行選項的概述並且退出。
--macrodir=dir
-m dir
在目錄dir中,而不是在缺省安裝目錄中尋找Autoconf宏檔案。你還可以把環境變量AC_MACRODIR設定成一個目錄;本選項覆蓋該環境變量。
--version
列印autoupdate的版本號並且退出。
改變了的結果
如果你透過檢驗shell變量DEFS來檢驗以前測試的結果,你需要把這些檢驗替換為對那些測試的緩存變量的檢查。在configure營運的時候,DEFS不再存在;它僅僅在生成輸出檔案的時候才被創建。這種與第1版的不同是因為正確地對變量實行引用(quoting)實在太麻煩而且在每次調用AC_DEFINE都要實行引用是低效的。參見緩存變量名。

例如,下面是為Autoconf第1版編寫的``configure.in''的片斷︰

AC_HAVE_FUNCS(syslog)
case ""$DEFS"" in
*-DHAVE_SYSLOG*) ;;
*) # syslog is not in the default libraries. See if it''s in some other.
saved_LIBS=""$LIBS""
for lib in bsd socket inet; do
AC_CHECKING(for syslog in -l$lib)
LIBS=""$saved_LIBS -l$lib""
AC_HAVE_FUNCS(syslog)
case ""$DEFS"" in
*-DHAVE_SYSLOG*) break ;;
*) ;;
esac
LIBS=""$saved_LIBS""
done ;;
esac

這裡是為版本2編寫的模式︰

AC_CHECK_FUNCS(syslog)
if test $ac_cv_func_syslog = no; then
# syslog is not in the default libraries. See if it''s in some other.
for lib in bsd socket inet; do
AC_CHECK_LIB($lib, syslog, [AC_DEFINE(HAVE_SYSLOG)
LIBS=""$LIBS $lib""; break])
done
fi

如果你透過在引號的後邊添加反斜線以處理AC_DEFINE_UNQUOTED中的bug,你需要刪除它們。它現下以可以預期的模式工作,並且不需要特別地處理引號(處理反斜線)。參見設定輸出變量。

所有由Autoconf宏設定的布爾shell變量現下用``yes''來表示真值。雖然為了向後兼容,有些宏使用空字元串表示假,大部分宏使用``no''來表示假。如果你倚賴於shell變量用諸如1或者``t''來表示真,你就需要改變你的測試。

改變了的宏的編寫
在定義你自己的宏時,你現下應該使用AC_DEFUN而不是define。 AC_DEFUN自動調用AC_PROVIDE並且確保透過AC_REQUIRE調用該宏不會被其他宏所打斷,從而防止在螢幕上出現巢狀的``checking...''消息。繼續按照老辦法行事沒有實際上的傷害,但它缺乏便利和吸引力。參見宏定義。

你可能把與Autoconf一同發行的宏作為如何解決問題的指南。看看它們的新版本將是一個好主意,因為風格已經有些改進並且它們利用了一些新的特徵。

如果你利用未公開的(undocumented)Autoconf內部元素(宏、變量、變換(diversions))作了微妙的工作,就要檢查你是否需要修改些什麼以適應已經發生的變化。可能你甚至能夠用版本2中公開(officially)支援的技術來代替你的拼裝(kludging)。但也可能不能。

為了加快你自行編寫的特徵測試,為它們添加緩存。看看你所有的測試是否足夠一般化,從而具有足夠的用途以把它們封裝到你可以共享的宏中去。

Autoconf的歷史
你可能會困惑,最初為什麼要編寫Autoconf?它是如何演變到今天的形式的?(為什麼它看起來就像大猩猩的吐沫?)如果你不困惑,那麼本章就不包含對你有用的訊息,你也可能會跳過它。如果你困惑,那就讓它明白些...

起源(Genesis)
在1991年六月,我為自由軟體基金會維護了許多GNU工具。由於它們被移植到更多的平台並且增加了更多的程式,用戶必須在``Makefile''中選擇的``-D''選項的數目(大約20個)變得難以承受。尤其是我-- 我不得不在許多不同的系統上對每個新的發布版本進行測試。所以我編寫了一個簡單的shell腳本為fileutils包猜測一些正確的設定,並且把它作為fileutils 2.0的一部分進行發布。這個configure能夠勝任工作,因此,我在下個月中,手工對其進行了修改以用於其他幾個GNU工具包,從而創建了相似的configure腳本。 Brian Berliner也修改了我的腳本以用與它的CVS修訂控制系統。

那個夏天以後,我得知Richard Stallman和Richard Pixley正在開發用於GNU編譯器工具的類似腳本;所以我對我的 configure進行了修改以支援它們的進化的界面︰把名為``Makefile.in''的檔案當作模板;添加``+srcdir'',作為許多選項的第一個選項;並創建``config.status''檔案。

出發(Exodus)
由於我從用戶那裡獲得了回饋,我組合了許多改進,使用Emacs進行搜索和替換、剪切(cut)和粘貼(paste),在每個腳本中進行類似的修改。隨著我修改更多的GNU工具包以使用configure腳本,完全用手工更新它們就不可能了。Rich Murphey,GNU圖形工具的維護者,在給我發送的郵件中說configure腳本很好,並問我是否有一個可以生成它們的工具可以發給他。沒有,我想,但我將會有﹗所以我開始考慮如何生成它們。這樣,從手工編寫configure腳本的苦力向功能強大而易於使用的Autoconf前進的旅程開始了。

Cygnus configure,它大約也在那個時候被開發,是表驅動的;這意味著用少量的大體上不可猜測的特徵來處理離散數量的系統類型(例如目標檔案格式的細節)。Brian Fox為Bash開發的自動配置系統採用了類似的方法。為了統一用法,我好像必須絕望地試圖為每個作業系統的變種的特徵維護一個及時更新的數據庫。更容易和更可靠的辦法是不檢查大多數特徵--特別是在那些人們已經在本地深入地研究或者安裝了買主提供的補丁的雜合的系統。

我考慮到使用與Cygnus configure相似的架構,就是提供一個單獨的configure腳本,在營運時讀入``configure.in''的片斷。但是我不想讓每個包都發布所有的特徵測試,所以我選擇了使用預處理器從每個``configure.in''中創建不同的configure。這個方法還提供了更多的控制和便利。

我簡要地察看了被Larry Wall、Harlan Stenn和Raphael Manfredi採用的Metaconfig包,但我為了幾個原因而不採用它。這種模式生成的Configure腳本是交互式的,我認為太不方便了;我不喜歡它測試某些特徵的模式(例如庫函數);我不知道它是否還有人維護,並且我所見到的Configure腳本在許多現代系統(例如System V R4和NeXT)中都不能工作;設定在支援某個特徵或者不支援該特徵時所進行的動作也不是很方便;我發現它難於學習;並且對於我的需要,它太大、太複雜了(我沒有意識到Autoconf最終將變得多么大)。

我考慮過使用Perl來生成我的風格的configure腳本,但顯然m4更加適合於簡單的文本替換工作︰由於輸出是隱含的,它的工作比較少。還有,每個人都已經擁有它了。(一開始,我並不倚賴於 GNU對m4的擴展。)我在Maryland大學的一些朋友最近用一些程式,包括tvtwm,製作了m4的前端,並且我也有興趣試試一種新語言。

上路(Leviticus)
因為我的configure在沒有與用戶進行交互的條件下自動地確定了系統的能力,我決定把生成它們的程式稱作Autoconfig。但附加了版本號之後,這個名字對於老式的UNIX檔案系統來說就太長了,所以我把它縮短成Autoconf。

在1991年秋天,我召集了一群期望獲得移植性的家伙(alpha測試者)以給我提供回饋從而使我可以壓縮(encapsulate)我用m4宏寫的腳本並且繼續添加特徵、改進檢查中採用的技術。測試者中的傑出人物有Pinard,他提出了創建一個``autoconf''來營運m4並且檢查找不到的宏調用的想法;還有Richard Pixley,他建議透過營運編譯器而不是在檔案系統中尋找引入檔案和符號,以獲得更精確的結果;還有Kerl Berry,他使得Autoconf可以配置 Tex並且把宏索引添加到文檔中;還有Ian Taylor,他增加了對創建C頭檔案的支援以代替在``Makefile''中添加 ``-D''選項的方法,以便他可以把Autoconf用於他的UUCP包。alpha測試者愉快地、一次又一次地隨著 Autoconf不同發布版本中的Autoconf名稱和宏調用慣例的改變而調整他們的檔案。他們都貢獻了許多特定的檢查、絕妙的想法,以及對bug的修正。

發展(Numbers)
在1992年七月,在alpha測試之後一個月,我發布了Autoconf 1.0,並且修改了許多GNU包以使用它。我對它帶來的正面作用感到很吃驚。很多人,包括那些編寫並不屬於GNU工程的軟體(例如TCL、FSP和Kerberos V5)的人們,開始使用它,以至於我無法跟蹤他們了。隨著很多使用configure腳本的人報告他們所遇到的問題,Autoconf繼續快速地得到改進,

Autoconf成為考驗m4實現的酷刑般的測試。由於Autoconf定義的宏的長度,UNIX m4開始失敗(dump core),同時也發現了GNU m4中的一些bug。最終,我們意識到我們需要使用一些只有 GNU m4才提供的特徵。特別的,4.3BSD m4含有一組增強了的內置宏;System V版本更好一些,但仍然不能提供我們所需要的所有東西。

隨著Autoconf得到人們越來越多的重視,對Autoconf進行了更多的開發(並且有了我不能預見的用途)。Karl Berry添加了對X11的檢查。david zuhn貢獻了對C++的支援。Pinard使Autoconf能夠診斷非法的參數。Jim Blandy勇敢地用它配置了GNU Emacs,並且為某些未來的改進打下了基礎。Roland McGrath用它配置了GNU C庫,編寫了autoheader 腳本以自動創建C頭檔案模板,並且為configure添加了一個``--verbose''選項。 Noah Friedman添加了``--macrodir''選項和環境變量AC_MACRODIR。(他還提出了術語 autoconfiscate,用來表示“調整套裝軟件以使用Autoconf”。)Roland和Noah改進了AC_DEFINE 中的引用保護並且修正了許多bug,特別是在1993年二月到六月間我們對處理移植性問題感到厭倦的時候。

現狀(Deuteronomy)
在累積了一個關於希望添加的主要特徵的長長的清單,並且在幾年之中各式各樣的人們提供的補丁殘留了古怪的效果之後。在1994年四月,處理對Cygnus的支援時,我開始對Autoconf進行一次主要的修訂。我添加了大部分Cygnus configure 有,而Autoconf缺少的特徵,主要是在david zuhn和Ken Raeburn的幫助下改編Cygnus configure的相關部分。這些特徵包括對使用``config.sub''、``config.guess''、``--host''和 ``--target''的支援;創建對檔案的連接;以及在次目錄中營運configure腳本。添加這些特徵使得Ken可以放棄GNU as,Rob Savoye可以放棄DejaGNU,而改用Autoconf。

作為對其他人的要求的回應,我添加了更多的特徵。許多人要求configure腳本能夠在不同的營運中共享檢查的結果,這是因為它們實在太慢了(尤其是像Cygnus那樣在配置一個大的源代碼樹的時候)。 Mike Haertel建議增加與位置有關的初始化腳本。發布必須在MS-DOS中解包(unpack)的軟體的人們要求提供一種覆蓋那些諸如``config.h.in''那樣的、含有兩個點的檔案名中的``.in''擴展名的方法。 Jim Avera透過AC_DEFINE和AC_SUBST中的引用擴展了對程式的檢測;他的洞察力帶來了重要的改進。Richard Stallman要求把編譯器的輸出送到``config.log''中,而不是送到``/dev/null''中,以幫助人們調試Emacs configure腳本。

由於我對程式質量的不滿,我進行了一些其他的修改。我減少了用於顯示檢查結果的消息的二義性,總是列印結果。我識別宏的名字並且消除編碼風格的不一致性。我添加了一些我所開發的附加工具以助於修改源代碼包以使用Autoconf。在Pinard的幫助下,我創建了不會在彼此的消息中導致衝突的宏。(這個特徵暴露了他草率地修正的、GNU m4 中的一些性能瓶頸﹗)我重新組織了人們需要解決的問題的文檔。並且我開始了一組測試(testsuite),這是因為經驗已經表明︰在我們修改Autoconf的時候,它有明顯的回歸傾向。

一些alpha測試者再次給出了難以估量的回饋,特別是 Pinard、Jim Meyering、Karl Berry、Rob Savoye、Ken Raeburn和Mark Eichin。

最後,2.0版本準備好了。而且我們也很高興。(我們又有閒暇時間了。我想。哇,很好。)

沒有留言: