第十四章 Makefile的約定 -------------------------------------------------------------------------------- 本章主要講述,在為GNU make書寫Makefile時(shí)需要遵循的約定。使用工具“Automake”將會(huì)幫助我們創(chuàng)建一個(gè)遵循這些約定的Makefile。所有GNU發(fā)布的軟 件包中的Makefile都是按照這些標(biāo)準(zhǔn)的約定來書寫的。因此理解本章的內(nèi)容,可幫助我們能夠很快的熟悉那些開源代碼的結(jié)構(gòu)。對(duì)于我們,在為一個(gè)工程書 寫Makefile時(shí),也盡量遵循這些約定。雖然并沒有強(qiáng)求你這么做,但是建議你還是按照已約定的規(guī)則來書寫自己的Makefile。 14.1 基本的約定 本節(jié)討論的是書寫Makefile中需要的一些基本約定,由于不同版本make之間的一些差異。可能在GNU make環(huán)境中正常工作的Makefile,換成其它版本的make執(zhí)行時(shí)會(huì)發(fā)生錯(cuò)誤。為了最大可能的兼容不同版本的make,這里給出了一些基本的約定。 1. 所有的Makefile中應(yīng)該包含這樣一行: SHELL = /bin/sh 這樣做的目的是為了避免變量“SHELL”在有些系統(tǒng)上可能繼承同名的系統(tǒng)環(huán)境變量而導(dǎo)致錯(cuò)誤。雖然在GNU版本的make中不會(huì)出現(xiàn)這種問題(GNU make中變量“SHELL”的默認(rèn)值是“/bin/sh”,它不同于系統(tǒng)環(huán)境變量“SHELL”)。 2. 小心處理后綴和隱含規(guī)則。不同make的可識(shí)別后綴和隱含規(guī)則存在不兼容,它可能會(huì)導(dǎo)致混亂或者錯(cuò)誤。因此在特定Makefile中明確限定可識(shí)別的后綴是一個(gè)不錯(cuò)的注意。Makefile中應(yīng)該這樣做: .SUFFIXES: .SUFFIXES: .c .o 第一行首先取消掉make默認(rèn)的可識(shí)別后綴列表,第二行通過特殊目標(biāo)“.SUFFIXES”重新指定可識(shí)別的 后綴規(guī)則。 3. 小心處理規(guī)則中的路徑。當(dāng)需要處理指定目錄的的文件時(shí),需要明確指定路徑。如“./”代表當(dāng)前目錄,“$(srcdir)”代表源代碼目錄。當(dāng)沒有指定明確路徑時(shí),意味著是當(dāng)前目錄。 目錄“./”(當(dāng)前目錄,GNU的發(fā)布軟件包中的“build”目錄)和“$(srcdir)”的區(qū)別和重要,我們可以通過“configure”腳本的 選項(xiàng)“--srcdir”指定源代碼所在的目錄(可參考GNU發(fā)布的軟件包中的configure腳本)。當(dāng)源代碼目錄和build目錄不同時(shí),規(guī)則: foo.1 : foo.man sedscript sed –e sedscript foo.man > $@ 將執(zhí)行失敗,是因?yàn)?#8220;foo.man”和“sedscript”并不在當(dāng)前目錄(當(dāng)然,處理這種錯(cuò)誤的手段可能有很多,諸如使用變量“VPATH”等)。當(dāng)前目錄只是build目錄,并不是軟件包目錄。 4. 使用GNU make的變量“VPATH”指定搜索目錄。當(dāng)規(guī)則只有一個(gè)依賴文件時(shí)。我們應(yīng)該使用自動(dòng)化變量“$<”和“$@”代替出現(xiàn)在命令的依賴文件和目標(biāo) 文件(其它版本的make,只在隱含規(guī)則中設(shè)置自動(dòng)化變量“$<”)。對(duì)于一個(gè)這樣的目標(biāo): foo.o : bar.c $(CC) –I. –I$(srcdir) $(CFLAGS) –c bar.o –o foo.o 我們?cè)贛akefile中應(yīng)該是用這種方式來書寫: foo.o : bar.c $(CC) –I. –I$(srcdir) $(CFLAGS) –c $< –o $@ 另外,對(duì)于有多個(gè)依賴的規(guī)則,為了規(guī)則能被正確執(zhí)行。應(yīng)該在規(guī)則的命令行中明確的指定文件的完整路徑名。例如第一個(gè)例子就可以這樣寫(需要在規(guī)則之前使用“VPATH”指定搜索目錄): foo.1 : foo.man sedscript sed –e $(srcdir)/sedscript $(srcdir)/foo.man > $@ 在GNU的發(fā)布軟件包中,包括了很多非源代碼的文件。諸如:“info”文件、“Autoconf”的輸出文件、“Automake”、“Bison”或 者“Flex”等文件。這些文件在發(fā)布時(shí)就在源代碼的目錄中。因此Makefile中對(duì)它們的重建也應(yīng)該是在源代碼目錄,而不應(yīng)該在build目錄。 相反的,對(duì)于那些本來就不存在于源代碼目錄下的文件,也不應(yīng)該將它們創(chuàng)建在源代碼的目錄下。要記住,make的過程不應(yīng)該以任何方式修改源代碼,或者改變?cè)创a目錄的結(jié)構(gòu)。 14.2 規(guī)則命令行的約定 本節(jié)將討論書寫規(guī)則命令的一些約定,在書寫多系統(tǒng)兼容的Makefile時(shí),特別需要注意不同系統(tǒng)之間的命令的不兼容。這里對(duì)規(guī)則命令行做出了一些基本約定: 1. 書寫Makefile時(shí),規(guī)則的命令(包括其他的腳本文件,如:configure)應(yīng)該是“sh”而不因該是“csh”所支持的。 2. 用于創(chuàng)建和安裝的“configure”腳本和Makefile中的命令除下面所列出的意外,避免使用其它命令: cat cmp cp diff echo egrep expr false grep install-info ln ls mkdir mv pwd rm rmdir sed sleep sort tar test touch true 3. 在目標(biāo)“dist”的命令行中可以使用壓縮工具“gzip”。 4. 對(duì)于可使用的這些工具命令,盡量使用它的通用選項(xiàng)。不要使用那些只在特定系統(tǒng)上有效的選項(xiàng)。如:“mkdir -p”這個(gè)命令在Linux系統(tǒng)上能夠很好的工作,但是其它很多系統(tǒng)卻并不支持“mkdir”的“-p”選項(xiàng)。 5. 盡量不要在規(guī)則的命令行重創(chuàng)建符號(hào)連接文件(使用“ln”命令)。因?yàn)橛行┫到y(tǒng)不支持(對(duì)于類Unix的系統(tǒng)我們基本上沒有問題,可能這里所說的是MS- DOS系統(tǒng)的系統(tǒng)。我想大家也沒有興趣或者說沒有必要在MS-DOS下寫Makefile,所以這個(gè)限制基本可以不考慮)。 6. 重建或者安裝目標(biāo)(一般是偽目標(biāo))的命令行可使用編譯器或者相關(guān)工具程序,應(yīng)該使用一個(gè)變量來表示所要執(zhí)行的命令。這樣會(huì)比較方便,需要修改一個(gè)命令時(shí),只需要更改變量的值就可以了。對(duì)于以下的這些命令程序: ar bison cc flex install ld ldconfig lex make makeinfo ranlib texi2dvi yacc Makefile規(guī)則中的命令行中,使用以下這些變量來代替它們: $(AR) $(BISON) $(CC) $(FLEX) $(INSTALL) $(LD) $(LDCONFIG) $(LEX) $(MAKE) $(MAKEINFO) $(RANLIB) $(TEXI2DVI) $(YACC) 如果在規(guī)則的命令行需要使用“ranlib”或者“ldconfig”這些工具時(shí),需要考慮它們是否在其它系統(tǒng)上被支持。當(dāng)在不支持它的系統(tǒng)中執(zhí)行包含此命令的規(guī)則時(shí),要能夠給出提示信息(提示原因是告訴用戶系統(tǒng)不支持此命令,但不應(yīng)該出現(xiàn)錯(cuò)誤而退出執(zhí)行)。 對(duì)另外一些命令組件的使用,應(yīng)該都是通過變量的形式來實(shí)現(xiàn)。例如如下的這些命令: chgrp chmod chown mknod 我們可以在Makefile中為這些命令組件定義一個(gè)代表它的變量(如:CHRP、CHMOD等,在命令行中就可以使用$(CHMOD)來引用)。 書寫多系統(tǒng)兼容Makefile時(shí)需要遵循以上的約定。如果只考慮一種系統(tǒng)時(shí),以上的這些約定中有一部分可以靈活處理,比如:在命令組件的使用上,我們就 可以使用這個(gè)系統(tǒng)獨(dú)具的那些命令組件;系統(tǒng)支持符號(hào)鏈接時(shí),我們就可以在命令行重創(chuàng)建一個(gè)符號(hào)鏈接文件。對(duì)于上邊的第6個(gè)約定,強(qiáng)烈建議所有的 Makefile都應(yīng)該遵循。 14.3 代表命令變量 Makefile應(yīng)該為重設(shè)所有的命令、選項(xiàng)等提供變量。就是說用戶可以通過修改一個(gè)變量值來重新指定所要執(zhí)行的命令,或者來控制命令執(zhí)行的選項(xiàng)、參數(shù)等。 Makefile中,所有的命令都應(yīng)該使用變量定義。在規(guī)則中需要使用此命令時(shí),通過對(duì)相應(yīng)的變量的引用來實(shí)現(xiàn)命令的調(diào)用。例如:定義變量“CC = gcc”,規(guī)則中可使用“$(CC)”來引用“gcc”。 對(duì)于一些件管理器工具如“ln”,“rm”“mv”等,可以不為它們定義變量,而直接使用。 為所有命令執(zhí)行的選項(xiàng)參數(shù)也應(yīng)該定義一個(gè)變量(可稱為命令的選項(xiàng)變量)。在命令變量(代表一個(gè)命令的變量)后添加“FLAGS”來命名這個(gè)選項(xiàng)變量。例 如:變量“CFLAGS”是c編譯器(命令變量為“CC”)的命令行選項(xiàng)變量;變量YFLAGS時(shí)命令“yacc”(命令變量為“YACC”)選項(xiàng)變量; 變量“LDFLAGS”是命令“ld”(命令變量為“LD”)的選項(xiàng)變量等。規(guī)則中c預(yù)處理的命令使用變量“CCFLAGS”來替代;同樣任何需要執(zhí)行鏈 接的命令行中使用“LDFLAGS”作為命令行選項(xiàng)。 c編譯器的編譯選項(xiàng)變量“CFLAGS”在Makefile中通常是為編譯所有的源文件提供選項(xiàng)的變量。對(duì)一個(gè)特定文件增加的選項(xiàng),不應(yīng)包含在變量 “CFLAGS”中。編譯特定的文件(或者一類特定文件)時(shí),如果需要使用特定的選項(xiàng)參數(shù),可以將這些選項(xiàng)寫在編譯它所執(zhí)行的規(guī)則的命令行中(也可以使用 目標(biāo)指定變量或者模式指定變量)。例如: CFLAGS = -g ALL_CFLAGS = -I $(CFLAGS) .c.o: $(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) $< 例子中,變量“CFLAGS”指定了編譯選項(xiàng)為“-g”,本例中它作為缺省的選項(xiàng)。對(duì)于其它所有源文件的編譯使用“-g”選項(xiàng)。雙后綴規(guī)則的命令行中為編譯生成“.o”文件指定了另外的選項(xiàng)“-I. -g ” 在所有編譯命令行中,變量“CFLAGS”應(yīng)該放在選項(xiàng)的最后。這樣可以保證命令行選項(xiàng)參數(shù)在重復(fù)時(shí),“CFLAGS”是有效的。在任何調(diào)用c編譯器的命令行中應(yīng)該使用選項(xiàng)變量“CFLAGS”,無(wú)論是進(jìn)行編譯還是連接。 如果需要使用make實(shí)現(xiàn)文件的安裝,則在Makefile中需要定義變量“INSTALL”。此變量代表安裝命令(install)。同時(shí)在 Makefile中需要定義變量“INSTALL_PROGRAM”和“INSTALL_DATA”(“INSTALL_PROGRAM”的缺省值都是 “$(INSTALL)”;“INSTALL_DATA”的缺省值是“${INSTALL} –m 644”)??梢允怯眠@些變量,來安裝可執(zhí)行程序或者非可執(zhí)行程序到指定位置。例如: $(INSTALL_PROGRAM) foo $(bindir)/foo $(INSTALL_DATA) libfoo.a $(libdir)/libfoo.a 另外,也可以使用變量“DESTDIR”來指定目標(biāo)需要安裝的目錄。通常在Makefile中不需要定義變量“DESTDIR”,可以通過命令行或者參數(shù)的形式指定。例如:“make DESTDIR=exec/ install”。因此上邊的命令就可以這樣實(shí)現(xiàn): $(INSTALL_PROGRAM) foo $(DESTDIR)$(bindir)/foo $(INSTALL_DATA) libfoo.a $(DESTDIR)$(libdir)/libfoo.a 安裝命令中文件名作為作為第二個(gè)參數(shù)。每一個(gè)需要安裝的文件使用單獨(dú)的命令(包括安裝一個(gè)目錄)。 14.4 安裝目錄變量 在Makefile中,安裝目錄同樣需要使用變量指定,這樣就可以很方便的修改文件的安裝路徑。安裝目錄的標(biāo)準(zhǔn)命名下邊將一一介紹。這些變量基于標(biāo)準(zhǔn)的文 件系統(tǒng)結(jié)構(gòu),這些變量的變種在SVR4、4.4BSD、Linux、Ultrix v4以及其它現(xiàn)代操作系統(tǒng)中都有使用。 Ø 以下所羅列的兩個(gè)變量是指定安裝文件的根目錄。所有其它安裝目錄都使它們的子目錄。文件不能直接安裝在這兩個(gè)目錄下。 prefix 這個(gè)前綴用于構(gòu)造下列(除這兩個(gè)安裝根目錄以外的其它目錄變量)變量的缺省值。變量“prefix”缺省值是“/usr/local”。創(chuàng)建完整的GNU 系統(tǒng)時(shí),變量prefix的缺省值是空值,“/usr”是“/”的符號(hào)連接符文件。(如果使用“Autoconf”工具,它應(yīng)該寫成 “@prefix@”)。注意:當(dāng)更改了變量“PREFIX”以后執(zhí)行“make install”,不會(huì)重建可執(zhí)行程序(終極目標(biāo))。 exec_prefix 這個(gè)前綴用于構(gòu)造下列變量的缺省值。變量“exec_prefix”缺省值是“$(prefix)”(如果使用“Autoconf”工具,它應(yīng)該寫為 “@exec_prefix@”)。通常,“$(exec_prefix)”目錄中的子目錄中存放和機(jī)器相關(guān)文件(例如可執(zhí)行文件和例程庫(kù))。“$ (prefix)”目錄的子目錄存放通用的一般文件。同樣:改變“exec_prefix”的值之后執(zhí)行“make install”,不會(huì)重建可執(zhí)行程序(終極目標(biāo))。 Ø 文件(包括可執(zhí)行程序、說明文檔等)的安裝目錄: bindir 用于安裝一般用戶可運(yùn)行的可執(zhí)行程序。通常它的值為:“/usr/local/bin”,使用時(shí)應(yīng)寫為:“$(exec_prefix)/bin”。 (使用“Autoconf”工具時(shí),應(yīng)該為“@bindir@”) sbindir 安裝可在shell中直接調(diào)用執(zhí)行的程序。這些命令僅對(duì)系統(tǒng)管理員有用(系統(tǒng)管理工具)。通常它的值為:“/usr/local/sbin”,要求在使用 時(shí)應(yīng)寫為:“$(exec_prefix)/sbin”。(使用“Autoconf”工具時(shí),應(yīng)該為“@sbindir@”) libexecdir 用于安裝那些通常不是由用戶直接使用,而是由其它程序調(diào)用的可執(zhí)行程序。通常它的值為:“/usr/local/libexec”,要求在使用時(shí)應(yīng)寫為: “$(exec_prefix)/libexec”。(使用“Autoconf”工具時(shí),應(yīng)該為“@libexecdir@”) Ø 程序執(zhí)行時(shí)使用的數(shù)據(jù)文件可從以下兩個(gè)方面來分類: 1. 是否可由程序更改。程序可更改或者不能更改的文件(雖然用戶可編輯其中某些文件)。 2. 是否和體系結(jié)構(gòu)相關(guān)。和體系結(jié)構(gòu)無(wú)關(guān)的文件,可被所有類型的機(jī)器共享;體系相關(guān)文件,僅可被相同類型機(jī)器、操作系統(tǒng)共享;其它的就是那些不能被任何兩個(gè)機(jī)器共享的文件。 這樣就存在六種不同的可能。除編譯生成的目標(biāo)文件(.o文件)和庫(kù)文件以外,不推薦使用那些和特定機(jī)器體系結(jié)構(gòu)相關(guān)的文件,使用和體系無(wú)關(guān)的數(shù)據(jù)文件更加簡(jiǎn)潔,而且,它的實(shí)現(xiàn)也并不非常困難。 在Makefile中應(yīng)該使用以下變量為不同類型的文件指定對(duì)應(yīng)的安裝目錄: datadir 用于安裝和機(jī)器體系結(jié)構(gòu)無(wú)關(guān)的只讀數(shù)據(jù)文件。通常它的值為:“/usr/local/share”,使用時(shí)應(yīng)寫為:“$(prefix)/share”。 (使用“Autoconf”工具時(shí),應(yīng)該為“@datadir@”)。“$(infodir)”和“$(includedir)”作為例外情況,參考后續(xù) 對(duì)它們的詳細(xì)描述。 sysconfdir 用于安裝從屬于特定機(jī)器的只讀數(shù)據(jù)文件,包括:主機(jī)配置文件、郵件服務(wù)、網(wǎng)絡(luò)配置文件、“/etc/passwd”文件等。所有該目錄下的文件都應(yīng)該是普 通文本文件(可識(shí)別的“ASCII”碼文本文件)。通常它的值為:“/usr/local/etc”,在使用時(shí)應(yīng)寫為:“$(prefix)/etc”。 (使用“Autoconf”工具時(shí),應(yīng)該為“@sysconfdir@”)。 不要將可執(zhí)行文件安裝在這個(gè)目錄下(可執(zhí)行文件的安裝目錄應(yīng)該是“$(libexecdir)”或者“$(sbindir)”)。也不要在這個(gè)目錄下安裝那些需要更改的文件(系統(tǒng)的配置文件等)。這些文件應(yīng)該安裝在目錄“$(localstatedir)”下。 sharedstatedir 用于安裝那些可由程序運(yùn)行時(shí)修改的文件,這些文件與體系結(jié)構(gòu)無(wú)關(guān)。通常它的值為:“/usr/local/com”,要求在使用時(shí)應(yīng)寫為:“$(prefix)/com”。 (使用“Autoconf”工具時(shí),應(yīng)該為“@sharedstatedir@”) localstatedir 用于安裝那些可由程序運(yùn)行時(shí)修改的文件,但這些文件和體系結(jié)構(gòu)相關(guān)。用戶沒有必要通過直接修改這些文件來配置軟件包,對(duì)于不同的配置文件,將它們放在“$ (datadir)”或者“$(sysconfdir)”目錄中。“$(localstatedir)”值通常為:“/usr/local/var”,在 使用時(shí)應(yīng)寫為:“$(prefix)/var”。(使用“Autoconf”工具時(shí),應(yīng)該為“@localstatedir@”) libdir 用于存放編譯后的目標(biāo)文件(.o)文件庫(kù)文件(文檔文件或者執(zhí)行的共享庫(kù)文件)。不要在此目錄下安裝可執(zhí)行文件(可執(zhí)行文件應(yīng)該安裝在目錄“$ (libexecdir)”下)。變量libdir值通常為:“/usr/local/lib”,使用時(shí)應(yīng)寫為:“$(exec_prefix) /lib”。(使用“Autoconf”工具時(shí),應(yīng)該為“@libdir@”) infodir 用于安裝軟件包的 Info 文件。它的缺省值為:“/usr/local/info”,使用時(shí)應(yīng)寫為:“$(prefix)/info”。(使用“Autoconf”工具時(shí),應(yīng)該為“@infodir@”) lispdir 用于安裝軟件包的Emacs Lisp 文件的目錄。它的缺省值為:“/usr/local/share/emacs/site-lisp”,使用時(shí)應(yīng)寫為:“$(prefix) /share/emacs/site-lisp”。當(dāng)使用Autoconf工具時(shí),應(yīng)將寫為“@lispdir@”。為了保證“@lispdir@”能夠 正常工作,需要在“configure.in”文件中包含如下部分: lispdir=‘${datadir}/emacs/site-lisp‘ AC_SUBST(lispdir) includedir 用于安裝用戶程序源代碼使用“#include”包含的頭文件。它的缺省值為:“/usr/local/include”,使用時(shí)應(yīng)寫為:“$(prefix)/include”。 (使用“Autoconf”工具時(shí),應(yīng)該為“@includedir@”)。 除GCC外的大多數(shù)編譯器不會(huì)在目錄“/usr/local/include”中搜尋頭文件,因此這種方式只適用GCC編譯器。這一點(diǎn)應(yīng)該不是一個(gè)問題, 因?yàn)楹芏嗲闆r下一些庫(kù)需要GCC才能工作。對(duì)那些依靠其它編譯器的庫(kù)文件,需要將頭文件安裝在兩個(gè)地方,一個(gè)由變量 “includedir”指定,另一個(gè)由變量“oldincludedir”指定。 oldincludedir 它所指定的目錄也同樣用于安裝頭文件,這些頭文件用于非GCC的編譯器。它的缺省值為:“/usr/include”。(使用“Autoconf”工具時(shí),應(yīng)該為“@oldincludedir@”)。 Makefile在安裝頭文件時(shí),需要判斷變量“oldincludedir”的值是否為空。如果是空值,就不使用它進(jìn)行頭文件的安裝(一般是安裝完成“/usr/local/include”下的頭文件之后才安裝此目錄下的頭文件)。 一個(gè)軟件包的安裝不能替換該目錄下已經(jīng)存在的頭文件,除非是同一個(gè)軟件包(重新使用相同的軟件包在此目錄下安裝頭文件)。例如,軟件包“Foo”需要在 “oldincludedir”指定的目錄下安裝一個(gè)頭文件“foo.h”時(shí),可安裝的條件為:1. 目錄“$(oldincludedir)”目錄下不存在頭文件“foo.h”;2. 已經(jīng)存在頭文件“foo.h”,存在的頭文件“foo.h”是之前軟件包“Foo”安裝的。 檢查頭文件“foo.h”是否來自于軟件包Foo,需要在頭文件的注釋中包含一個(gè)“magic”字符串,使用命令“grep”來在該文件中查找這個(gè)magic。 Ø Unix風(fēng)格的幫助文件需要安裝在以下目錄中: mandir 安裝該軟件包的幫助文檔(如果有)的頂層目錄。它的缺省值為:“/usr/local/man”,要求在使用時(shí)應(yīng)寫為:“$(prefix)/man”。 (使用“Autoconf”工具時(shí),應(yīng)該為“@@mandir@@”) man1dir 用于安裝幫助文檔的第一節(jié)(man 1)。它的缺省值為:“$(mandir)/man1”。 man2dir 用于安裝幫助文檔的第二節(jié)(man 2)。它的缺省值為:“$(mandir)/man2”。 ... 不要將GNU 軟件的原始文檔作為幫助頁(yè)。應(yīng)該編寫使用手冊(cè)。幫助頁(yè)僅僅是為了幫助用戶在Unix上方便運(yùn)行GNU軟件,它是附屬的運(yùn)行程序。 manext 文件名擴(kuò)展字,它是對(duì)安裝手冊(cè)的擴(kuò)展。以點(diǎn)號(hào)(.)開始的十進(jìn)制數(shù)。缺省值為:“.1”。 man1ext 幫助文檔的第一節(jié)(man 1)的文件名擴(kuò)展字。 man2ext 幫助文檔的第二節(jié)(man 2)的文件名擴(kuò)展字。 ... 當(dāng)一個(gè)軟件包的幫助手冊(cè)有多個(gè)章節(jié)時(shí),使用這些變量代替“manext”。(第一節(jié)“man1ext”,第二節(jié)“man2ext”,第三節(jié)“man3ext”……) Ø 而且下列這些變量也應(yīng)該在Makefile中定義: srcdir 此變量指定的目錄是需要編譯的源文件所在的目錄。該變量的值在使用“configure”腳本對(duì)軟件包進(jìn)行配置時(shí)產(chǎn)生的。(使用“Autoconf”工具,應(yīng)該書寫為“srcdir = @srcdir@”) 例如: # 安裝的普通目錄路徑前綴。 # 注意:該目錄在開始安裝前必須存在 prefix = /usr/local exec_prefix = $(prefix) # 放置“gcc”命令使用的可執(zhí)行程序 bindir = $(exec_prefix)/bin # 編譯器需要的目錄 libexecdir = $(exec_prefix)/libexec # 軟件包的Info文件所在目錄 infodir = $(prefix)/info 在用戶標(biāo)準(zhǔn)指定的目錄下安裝大量文件時(shí),可以將這些文件分類安裝在指定目錄的多個(gè)子目錄下??梢栽贛akefile中實(shí)現(xiàn)一個(gè)“install”偽目標(biāo)來描述安裝這些文件的命令(包括創(chuàng)建子目錄,安裝文件到對(duì)應(yīng)的子目錄中)。 在發(fā)布的軟件包中,不能強(qiáng)制要求用戶必須指定這些安裝目錄的變量。使用一套標(biāo)準(zhǔn)的安裝目錄變量來指定安裝目錄,當(dāng)用戶需要指定安裝目錄時(shí),通過修改變量定義來指定具體的目錄,在用戶沒有指定的情況下,使用默認(rèn)的目錄。 14.5 Makefile的標(biāo)準(zhǔn)目標(biāo)名 所有GNU發(fā)布的軟件包的Makefile中,必須包含以下這些目標(biāo): all 此目標(biāo)的動(dòng)作是編譯整個(gè)軟件包。“all”應(yīng)該為Makefile的終極目標(biāo)。該目標(biāo)的動(dòng)作不重建任何文檔文件(只編譯所有的源代碼,生成可執(zhí)行程序);Info文件應(yīng)該作為發(fā)布文件的一部分,DVI文件只在明確指定的時(shí)候才應(yīng)該被重建。 缺省情況下,對(duì)所有源程序的編譯和連接應(yīng)該使用選項(xiàng)“-g”,是最終的可執(zhí)行程序中包含調(diào)試信息。當(dāng)最終的可執(zhí)行程序不需要包含調(diào)試信息時(shí),可使用“strip”去掉可執(zhí)行程序中的調(diào)試符號(hào)已縮減最終的程序大小。 install 此目標(biāo)的動(dòng)作是完成程序的編譯并將最終的可執(zhí)行程序、庫(kù)文件等拷貝到安裝的目錄。如果只是驗(yàn)證這些程序是否可被正確安裝,它的動(dòng)作應(yīng)該是一個(gè)測(cè)試安裝動(dòng)作。 安裝時(shí)一般不要對(duì)可執(zhí)行程序進(jìn)行strip(去掉可執(zhí)行程序內(nèi)部的調(diào)試信息)。存在另外一個(gè)目標(biāo)“install-strip”,它實(shí)現(xiàn)安裝時(shí)strip。 盡可能保證目標(biāo)“install”的動(dòng)作不更改程序創(chuàng)建目錄(builid目錄)下的任何文件,對(duì)這個(gè)目錄下文件的修改(重建或者更新)是目標(biāo)“all”所要定義的動(dòng)作。 “install”目標(biāo)定義的動(dòng)作在安裝目錄不存在時(shí),能夠創(chuàng)建這些不存在的安裝目錄。這些目錄包括:變量“prefix”和“exec_prefix”指定的目錄和所有必要的子目錄。完成此任務(wù)的方式可以使用下邊介紹的“installdirs”目標(biāo)。 在安裝man文檔的命令前使用“-”忽略這安裝命令的錯(cuò)誤,這樣可以避免在沒有Unix man文檔的系統(tǒng)上執(zhí)行安裝時(shí)出現(xiàn)錯(cuò)誤。 安裝Info文檔的方法是使用變量“INSTALL_DATA”將Info文檔拷貝到“$(infodir)”目錄下去(參考 14.4安裝目錄的變量 一節(jié)),如果存在“install-info”命令則執(zhí)行它。“install-info”是一個(gè)編輯Info“dir”文件的程序,更新或者修改 “info”文檔的入口和目錄;它是Texinfo軟件包的一部分。這里有一個(gè)安裝Info文檔的例子: $(DESTDIR)$(infodir)/foo.info: foo.info $(POST_INSTALL) # 可能在“.”(當(dāng)前目錄)存在一個(gè)新的文檔,而不是“srcdir”。 -if test -f foo.info; then d=.; \ else d=$(srcdir); fi; \ $(INSTALL_DATA) $$d/foo.info $(DESTDIR)$@; \ #如果install-info命令存在則運(yùn)行它 # 使用“if”代替在命令行前的“-” # 這樣,就可以看到運(yùn)行install-info產(chǎn)生的真正錯(cuò)誤 # 我們使用“$(SHELL) -c”是因?yàn)樵谝恍﹕hell中 # 遇到未知的命令不會(huì)運(yùn)行失敗 if $(SHELL) -c ‘install-info --version‘ \ >/dev/null 2>&1; then \ install-info --dir-file=$(DESTDIR)$(infodir)/dir \ $(DESTDIR)$(infodir)/foo.info; \ else true; fi 目標(biāo)install的命令需要分為三類:正常命令、預(yù)安裝命令和安裝后命令。參考 14.6 安裝命令分類 一節(jié) uninstall 刪除所有已安裝文件——由install創(chuàng)建的文件拷貝。規(guī)則所定義的命令不能修改編譯目錄下的文件,僅僅是刪除安裝目錄下的文件。像install目標(biāo)的命令一樣,uninstall目標(biāo)的命令也分為三類。參考 14.6 安裝命令分類 一節(jié) install-strip 和目標(biāo)install的動(dòng)作類似,但是install-strip指定的命令在安裝時(shí)對(duì)可執(zhí)行文件進(jìn)行strip(去掉程序內(nèi)部的調(diào)試信息)。它的定義如下: install-strip: $(MAKE) INSTALL_PROGRAM=‘$(INSTALL_PROGRAM) -s‘ \ install 如果軟件包的存在安裝腳本時(shí),目標(biāo)install-strip所定義的命令就不能是對(duì)目標(biāo)“install”的引用,它所完成的僅僅是對(duì)可執(zhí)行文件strip。 “install-strip”不應(yīng)該直接在build目錄下對(duì)可執(zhí)行文件進(jìn)行strip,應(yīng)該是對(duì)安裝目錄下的可執(zhí)行文件進(jìn)行strip。就是說“install-strip”所的命令不能build目錄下的文件產(chǎn)生影響。 一般不建議安裝時(shí)對(duì)可執(zhí)行文件進(jìn)行strip,因?yàn)槿サ艨蓤?zhí)行文件的調(diào)試信息后,在出現(xiàn)bug時(shí),就不能對(duì)可執(zhí)行程序進(jìn)行跟蹤調(diào)試。 clean 清除當(dāng)前目錄下編譯生成的所有文件,這些文件由make程序創(chuàng)建。記住,不要?jiǎng)h除軟件包的配置文件,同時(shí)需要保留build時(shí)創(chuàng)建的那些文件(諸如:創(chuàng)建的目錄、build生成的信息記錄文件等)。因?yàn)檫@些文件都是發(fā)布版本的一部分。 對(duì)于.dvi文件,當(dāng)它不作為發(fā)布版本的一部分時(shí),可以刪除。 distclean 刪除當(dāng)前目錄下的的配置過程產(chǎn)生的文件、build過程產(chǎn)生的文件。目標(biāo)“distclean”指定的刪除命令應(yīng)該刪除軟件包中所有非發(fā)布文件。 mostlyclean 類似于目標(biāo)“clean”,但是可保留一些編譯生成的文件,避免在下次編譯時(shí)對(duì)這些文件重建。例如,用于GCC的目標(biāo)“mostlyclean”不刪除文件“libgcc.a”,因?yàn)樵诮^大多數(shù)情況下它都不需要重新編譯。 maintainer-clean 此目標(biāo)所定義的命令幾乎會(huì)刪除所有當(dāng)前目錄下能夠由Makefile重建的文件。典型的,包括目標(biāo)“distclean”刪除的文件、由Bison生成 的.c源文件、tags記錄文件、Ifon文件等。但是有一個(gè)例外,就是執(zhí)行“make maintainer-clean”不能刪除“configure”這個(gè)配置腳本文件,即使“configure”可以由Makefile生成。因?yàn)?“configure”是軟件包的配置腳本。 目標(biāo)“maintainer-clean”應(yīng)該只能由維護(hù)軟件包的用戶使用,而不能被普通用戶使用。因?yàn)樗鼤?huì)刪除一些軟件包的發(fā)布文件,而重建這些文件可能需要專門的工具。因此我們?cè)谑褂么四繕?biāo)是需要小心。 為了讓用戶能夠在執(zhí)行前得到提示,通常目標(biāo)“maintainer-clean”的命令以下兩行為開始: @echo“該命令用于維護(hù)此軟件包的用戶使用”; @echo“它刪除的文件可能需要使用特殊的工具來重建。” TAGS 此目標(biāo)所定義的命令完成對(duì)該程序的tags記錄文件的更新。 info 產(chǎn)生必要的Info文檔。此目標(biāo)應(yīng)該按照如下書寫: info: foo.info foo.info: foo.texi chap1.texi chap2.texi $(MAKEINFO) $(srcdir)/foo.texi 必須在Makefile定義變量“MAKEINFO”,代表命令工具makeinfo,該工具是發(fā)布軟件Texinfo的一部分。 通常,GNU的發(fā)布程序會(huì)和Info文檔會(huì)被一同創(chuàng)建,這意味著Info文檔是在源文件的目錄下。用戶在創(chuàng)建發(fā)布軟件時(shí),一般情況下,make不更新Info文檔,因?yàn)樗鼈円呀?jīng)更新到最新了。 dvi 為所有的Texinfo文件創(chuàng)建對(duì)應(yīng)的DVI文件。例如: dvi: foo.dvi foo.dvi: foo.texi chap1.texi chap2.texi $(TEXI2DVI) $(srcdir)/foo.texi 必須在Makefile中定義變量“TEXI2DVI”。它代表命令工具texi2dvi,該工具是發(fā)布軟件Texinfo一部分。 規(guī)則中也可以沒有命令行,這樣make程序會(huì)自動(dòng)為它推導(dǎo)對(duì)應(yīng)的命令。 dist 此目標(biāo)指定的命令創(chuàng)建發(fā)布程序的tar文件。創(chuàng)建的tar文件應(yīng)該是這個(gè)軟件包的目錄,文件名中也可以包含版本號(hào)(就是說創(chuàng)建的tar文件在解包之后應(yīng)該是一個(gè)目錄)。例如,發(fā)布的GCC 1.40版的tar文件解包的目錄為“gcc-1.40”。 通常的做法是是創(chuàng)建一個(gè)空目錄,如使用ln或cp將所需要的文件加入到這個(gè)目錄中,之后對(duì)這個(gè)目錄使用tar進(jìn)行打包。打包之后的tar文件使用gzip壓縮。例如,實(shí)際的GCC 1.40版的發(fā)布文件叫“gcc-1.40.tar.gz”。 目標(biāo)“dist”的依賴文件為軟件包中所有的非源代碼的文件,因此在使用目標(biāo)進(jìn)行發(fā)布軟件打包壓縮之前必須保證這些文件是最新的。 check 此目標(biāo)指定的命令完成所有的自檢功能。在執(zhí)行檢查之前,應(yīng)確保所有程序已經(jīng)被創(chuàng)建,可以不安裝。為了對(duì)它們進(jìn)行測(cè)試,需要實(shí)現(xiàn)在程序沒有安裝的情況下被執(zhí)行的規(guī)則命令。 的目標(biāo),對(duì)于各種程序它們很有用: installcheck 執(zhí)行安裝檢查。在執(zhí)行安裝檢查之前,確保所有程序已經(jīng)被創(chuàng)建并且被安裝。需要注意的是:安裝目錄“$(bindir)”是否在搜索路徑中。 installdirs 使用目標(biāo)“installdirs”創(chuàng)建安裝目錄以及它的子目錄在很多場(chǎng)合是非常有用的。腳本“mkinstalldirs”就是為了實(shí)現(xiàn)這個(gè)目的而編寫的;發(fā)布的Texinfo軟件包中就包含了這個(gè)腳本文件。Makefile中的規(guī)則可以這樣書寫: # 確保所有安裝目錄(例如 $(bindir))存在,如有必要?jiǎng)t創(chuàng)建這些目錄 installdirs: mkinstalldirs $(srcdir)/mkinstalldirs $(bindir) $(datadir) \ $(libdir) $(infodir) \ $(mandir) 或者可以使用變量“DESTDIR”: # 確保所有安裝目錄(例如 $(bindir))存在,如有必要?jiǎng)t創(chuàng)建這些目錄 installdirs: mkinstalldirs $(srcdir)/mkinstalldirs \ $(DESTDIR)$(bindir) $(DESTDIR)$(datadir) \ $(DESTDIR)$(libdir) $(DESTDIR)$(infodir) \ $(DESTDIR)$(mandir) 該規(guī)則不能更改軟件的編譯目錄,僅僅是創(chuàng)建程序的安裝目錄。 14.6 安裝命令分類 在為Makefile書寫“install”目標(biāo)時(shí),需要將其命令分為三類:正常命令、安裝前命令和安裝后命令。 正常命令是把文件移動(dòng)到合適的地方,并設(shè)置它們的模式。這個(gè)過程不修改任何文件,僅僅是把需要安裝的文件從軟件包中拷貝到安裝目錄。 安裝前命令和安裝后命令可能修改某些文件;通常,修改一些配置文件和系統(tǒng)的數(shù)據(jù)庫(kù)文件。典型地,安裝前命令在正常命令之前執(zhí)行,安裝后命令在正常命令執(zhí)行后執(zhí)行。 大多數(shù)情況是,安裝后命令是運(yùn)行“install-info”程序。它所完成的工作不能由正常命令完成,因?yàn)樗铝艘粋€(gè)文件(Info的目錄),該文件不能單獨(dú)的或者完整的從軟件包來進(jìn)行安裝,因?yàn)樗荒茉谡0惭b命令完成安裝軟件包的info文檔之后才可正確執(zhí)行。 大多數(shù)程序不需要安裝前命令,但應(yīng)該在Makefile中提供。 將“install”規(guī)則的命令分為這三類時(shí),應(yīng)該在命令之間插入分類行(category lines)。分類行說明了后續(xù)需要執(zhí)行的命令的類別。 分類行是由一個(gè)[Tab]字符開始的make的特殊變量的引用,行尾是可選的注釋內(nèi)容。可以使用三個(gè)特殊的變量,每一個(gè)代表一種類別。分類行不能出現(xiàn)在普通的執(zhí)行文件中,因?yàn)檫@三個(gè)特殊的make變量沒有定義(也不應(yīng)該在Makefile中定義它們)。 以下是三種可能的分類行,并對(duì)它們進(jìn)行了注釋: $(PRE_INSTALL) # 以下是安裝前命令 $(POST_INSTALL) # 以下是安裝后命令 $(NORMAL_INSTALL) # 以下是正常命令 如果安裝規(guī)則(install所在的規(guī)則)的命令行沒有使用分類行,那么在第一個(gè)出現(xiàn)的分類行之前的所有的命令行都被認(rèn)為是正常命令。如果命令行中沒有分類行,那么規(guī)則的所有命令行都都被認(rèn)為是正常命令行。 對(duì)應(yīng)的,一下這三個(gè)是“uninstall”命令的分類行: $(PRE_UNINSTALL) #以下是卸載前命令 $(POST_UNINSTALL) #以下是卸載后命令 $(NORMAL_UNINSTALL) #以下是正常命令 卸載前命令應(yīng)該是取消軟件包Info文檔的入口。 如果目標(biāo)install或 uninstall存在依賴,其作為安裝的子例程,那么就應(yīng)該在每一個(gè)以來目標(biāo)的命令行開始使用分類行,同時(shí)目標(biāo)insall和uninstall的命令行開始也需要使用分類行。這樣就可以確保任何調(diào)用方式時(shí)每一條命令都被正確的分類。 除下列命令外,安裝前命令和安裝后命令不應(yīng)該使用其它命令: [ basename bash cat chgrp chmod chown cmp cp dd diff echo egrep expand expr false fgrep find getopt grep gunzip gzip hostname install install-info kill ldconfig ln ls md5sum mkdir mkfifo mknod mv printenv pwd rm rmdir sed sort tee test touch true uname xargs yes 按照這種方式分類命令的原因是為了創(chuàng)建二進(jìn)制的軟件包。典型的二進(jìn)制軟件包包括可執(zhí)行文件、必須安裝的其它文件以及它自己的安裝文件,因此二進(jìn)制軟件包就不需要任何正常命令、只需要安裝前命令和安裝后命令。 創(chuàng)建二進(jìn)制軟件包的程序通過提取安裝前命令和安裝后命令工作。這里有一個(gè)抽取安裝前命令的方法: make -n install -o all \ PRE_INSTALL=pre-install \ POST_INSTALL=post-install \ NORMAL_INSTALL=normal-install \ | gawk -f pre-install.awk 文件“pre-install.awk”可能包括以下內(nèi)容: $0 ~ /^\t[ \t]*(normal_install|post_install)[ \t]*$/ {on = 0} on {print $0} $0 ~ /^\t[ \t]*pre_install[ \t]*$/ {on = 1} 安裝前命令的執(zhí)行結(jié)果和軟件包的安裝shell腳本的結(jié)果一樣。 |
聯(lián)系客服
微信登錄中...
請(qǐng)勿關(guān)閉此頁(yè)面