本章主要講述,在爲GNU make書寫Makefile時須要遵循的約定。使用工具「Automake」將會幫助咱們建立一個遵循這些約定的Makefile。全部GNU發佈的軟件包中的Makefile都是按照這些標準的約定來書寫的。所以理解本章的內容,可幫助咱們可以很快的熟悉那些開源代碼的結構。對於咱們,在爲一個工程書寫Makefile時,也儘可能遵循這些約定。雖然並無強求你這麼作,可是建議你仍是按照已約定的規則來書寫本身的Makefile。 php
本節討論的是書寫Makefile中須要的一些基本約定,因爲不一樣版本make之間的一些差別。可能在GNU make環境中正常工做的Makefile,換成其它版本的make執行時會發生錯誤。爲了最大可能的兼容不一樣版本的make,這裏給出了一些基本的約定。 shell
1. 全部的Makefile中應該包含這樣一行: 數據庫
SHELL = /bin/sh bash
這樣作的目的是爲了不變量「SHELL」在有些系統上可能繼承同名的系統環境變量而致使錯誤。雖然在GNU版本的make中不會出現這種問題(GNU make中變量「SHELL」的默認值是「/bin/sh」,它不一樣於系統環境變量「SHELL」)。 網絡
2. 當心處理後綴和隱含規則。不一樣make的可識別後綴和隱含規則存在不兼容,它可能會致使混亂或者錯誤。所以在特定Makefile中明確限定可識別的後綴是一個不錯的注意。Makefile中應該這樣作: 工具
.SUFFIXES: post
.SUFFIXES: .c .o 測試
第一行首先取消掉make默認的可識別後綴列表,第二行經過特殊目標「.SUFFIXES」從新指定可識別的 flex
後綴規則。 ui
3. 當心處理規則中的路徑。當須要處理指定目錄的的文件時,須要明確指定路徑。如「./」表明當前目錄,「$(srcdir)」表明源代碼目錄。當沒有指定明確路徑時,意味着是當前目錄。
目錄「./」(當前目錄,GNU的發佈軟件包中的「build」目錄)和「$(srcdir)」的區別和重要,咱們能夠經過「configure」腳本的選項「--srcdir」指定源代碼所在的目錄(可參考GNU發佈的軟件包中的configure腳本)。當源代碼目錄和build目錄不一樣時,規則:
foo.1 : foo.man sedscript
sed –e sedscript foo.man > $@
將執行失敗,是由於「foo.man」和「sedscript」並不在當前目錄(固然,處理這種錯誤的手段可能有不少,諸如使用變量「VPATH」等)。當前目錄只是build目錄,並非軟件包目錄。
4. 使用GNU make的變量「VPATH」指定搜索目錄。當規則只有一個依賴文件時。咱們應該使用自動化變量「$<」和「$@」代替出如今命令的依賴文件和目標文件(其它版本的make,只在隱含規則中設置自動化變量「$<」)。對於一個這樣的目標:
foo.o : bar.c
$(CC) –I. –I$(srcdir) $(CFLAGS) –c bar.o –o foo.o
咱們在Makefile中應該是用這種方式來書寫:
foo.o : bar.c
$(CC) –I. –I$(srcdir) $(CFLAGS) –c $< –o $@
另外,對於有多個依賴的規則,爲了規則能被正確執行。應該在規則的命令行中明確的指定文件的完整路徑名。例如第一個例子就能夠這樣寫(須要在規則以前使用「VPATH」指定搜索目錄):
foo.1 : foo.man sedscript
sed –e $(srcdir)/sedscript $(srcdir)/foo.man > $@
在GNU的發佈軟件包中,包括了不少非源代碼的文件。諸如:「info」文件、「Autoconf」的輸出文件、「Automake」、「Bison」或者「Flex」等文件。這些文件在發佈時就在源代碼的目錄中。所以Makefile中對它們的重建也應該是在源代碼目錄,而不該該在build目錄。
相反的,對於那些原本就不存在於源代碼目錄下的文件,也不該該將它們建立在源代碼的目錄下。要記住,make的過程不該該以任何方式修改源代碼,或者改變源代碼目錄的結構。
本節將討論書寫規則命令的一些約定,在書寫多系統兼容的Makefile時,特別須要注意不一樣系統之間的命令的不兼容。這裏對規則命令行作出了一些基本約定:
1. 書寫Makefile時,規則的命令(包括其餘的腳本文件,如:configure)應該是「sh」而不因該是「csh」所支持的。
2. 用於建立和安裝的「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. 在目標「dist」的命令行中可使用壓縮工具「gzip」。
4. 對於可以使用的這些工具命令,儘可能使用它的通用選項。不要使用那些只在特定系統上有效的選項。如:「mkdir -p」這個命令在Linux系統上可以很好的工做,可是其它不少系統卻並不支持「mkdir」的「-p」選項。
5. 儘可能不要在規則的命令行重建立符號鏈接文件(使用「ln」命令)。由於有些系統不支持(對於類Unix的系統咱們基本上沒有問題,可能這裏所說的是MS-DOS系統的系統。我想你們也沒有興趣或者說沒有必要在MS-DOS下寫Makefile,因此這個限制基本能夠不考慮)。
6. 重建或者安裝目標(通常是僞目標)的命令行可以使用編譯器或者相關工具程序,應該使用一個變量來表示所要執行的命令。這樣會比較方便,須要修改一個命令時,只須要更改變量的值就能夠了。對於如下的這些命令程序:
ar bison cc flex install ld ldconfig lex
make makeinfo ranlib texi2dvi yacc
Makefile規則中的命令行中,使用如下這些變量來代替它們:
$(AR) $(BISON) $(CC) $(FLEX) $(INSTALL) $(LD) $(LDCONFIG) $(LEX)
$(MAKE) $(MAKEINFO) $(RANLIB) $(TEXI2DVI) $(YACC)
若是在規則的命令行須要使用「ranlib」或者「ldconfig」這些工具時,須要考慮它們是否在其它系統上被支持。當在不支持它的系統中執行包含此命令的規則時,要可以給出提示信息(提示緣由是告訴用戶系統不支持此命令,但不該該出現錯誤而退出執行)。
對另一些命令組件的使用,應該都是經過變量的形式來實現。例如以下的這些命令:
chgrp chmod chown mknod
咱們能夠在Makefile中爲這些命令組件定義一個表明它的變量(如:CHRP、CHMOD等,在命令行中就可使用$(CHMOD)來引用)。
書寫多系統兼容Makefile時須要遵循以上的約定。若是隻考慮一種系統時,以上的這些約定中有一部分能夠靈活處理,好比:在命令組件的使用上,咱們就可使用這個系統獨具的那些命令組件;系統支持符號連接時,咱們就能夠在命令行重建立一個符號連接文件。對於上邊的第6個約定,強烈建議全部的Makefile都應該遵循。
Makefile應該爲重設全部的命令、選項等提供變量。就是說用戶能夠經過修改一個變量值來從新指定所要執行的命令,或者來控制命令執行的選項、參數等。
Makefile中,全部的命令都應該使用變量定義。在規則中須要使用此命令時,經過對相應的變量的引用來實現命令的調用。例如:定義變量「CC = gcc」,規則中可以使用「$(CC)」來引用「gcc」。
對於一些件管理器工具如「ln」,「rm」「mv」等,能夠不爲它們定義變量,而直接使用。
爲全部命令執行的選項參數也應該定義一個變量(可稱爲命令的選項變量)。在命令變量(表明一個命令的變量)後添加「FLAGS」來命名這個選項變量。例如:變量「CFLAGS」是c編譯器(命令變量爲「CC」)的命令行選項變量;變量YFLAGS時命令「yacc」(命令變量爲「YACC」)選項變量;變量「LDFLAGS」是命令「ld」(命令變量爲「LD」)的選項變量等。規則中c預處理的命令使用變量「CCFLAGS」來替代;一樣任何須要執行連接的命令行中使用「LDFLAGS」做爲命令行選項。
c編譯器的編譯選項變量「CFLAGS」在Makefile中一般是爲編譯全部的源文件提供選項的變量。對一個特定文件增長的選項,不該包含在變量「CFLAGS」中。編譯特定的文件(或者一類特定文件)時,若是須要使用特定的選項參數,能夠將這些選項寫在編譯它所執行的規則的命令行中(也可使用目標指定變量或者模式指定變量)。例如:
CFLAGS = -g
ALL_CFLAGS = -I $(CFLAGS)
.c.o:
$(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) $<
例子中,變量「CFLAGS」指定了編譯選項爲「-g」,本例中它做爲缺省的選項。對於其它全部源文件的編譯使用「-g」選項。雙後綴規則的命令行中爲編譯生成「.o」文件指定了另外的選項「-I. -g 」
在全部編譯命令行中,變量「CFLAGS」應該放在選項的最後。這樣能夠保證命令行選項參數在重複時,「CFLAGS」是有效的。在任何調用c編譯器的命令行中應該使用選項變量「CFLAGS」,不管是進行編譯仍是鏈接。
若是須要使用make實現文件的安裝,則在Makefile中須要定義變量「INSTALL」。此變量表明安裝命令(install)。同時在Makefile中須要定義變量「INSTALL_PROGRAM」和「INSTALL_DATA」(「INSTALL_PROGRAM」的缺省值都是「$(INSTALL)」;「INSTALL_DATA」的缺省值是「${INSTALL} –m 644」)。能夠是用這些變量,來安裝可執行程序或者非可執行程序到指定位置。例如:
$(INSTALL_PROGRAM) foo $(bindir)/foo
$(INSTALL_DATA) libfoo.a $(libdir)/libfoo.a
另外,也可使用變量「DESTDIR」來指定目標須要安裝的目錄。一般在Makefile中不須要定義變量「DESTDIR」,能夠經過命令行或者參數的形式指定。例如:「make DESTDIR=exec/ install」。所以上邊的命令就能夠這樣實現:
$(INSTALL_PROGRAM) foo $(DESTDIR)$(bindir)/foo
$(INSTALL_DATA) libfoo.a $(DESTDIR)$(libdir)/libfoo.a
安裝命令中文件名做爲做爲第二個參數。每個須要安裝的文件使用單獨的命令(包括安裝一個目錄)。
在Makefile中,安裝目錄一樣須要使用變量指定,這樣就能夠很方便的修改文件的安裝路徑。安裝目錄的標準命名下邊將一一介紹。這些變量基於標準的文件系統結構,這些變量的變種在SVR4、4.4BSD、Linux、Ultrix v4以及其它現代操做系統中都有使用。
Ø 如下所羅列的兩個變量是指定安裝文件的根目錄。全部其它安裝目錄都使它們的子目錄。文件不能直接安裝在這兩個目錄下。
prefix
這個前綴用於構造下列(除這兩個安裝根目錄之外的其它目錄變量)變量的缺省值。變量「prefix」缺省值是「/usr/local」。建立完整的GNU系統時,變量prefix的缺省值是空值,「/usr」是「/」的符號鏈接符文件。(若是使用「Autoconf」工具,它應該寫成「@prefix@」)。注意:當更改了變量「PREFIX」之後執行「make install」,不會重建可執行程序(終極目標)。
exec_prefix
這個前綴用於構造下列變量的缺省值。變量「exec_prefix」缺省值是「$(prefix)」(若是使用「Autoconf」工具,它應該寫爲「@exec_prefix@」)。一般,「$(exec_prefix)」目錄中的子目錄中存放和機器相關文件(例如可執行文件和例程庫)。「$(prefix)」目錄的子目錄存放通用的通常文件。一樣:改變「exec_prefix」的值以後執行「make install」,不會重建可執行程序(終極目標)。
Ø 文件(包括可執行程序、說明文檔等)的安裝目錄:
bindir
用於安裝通常用戶可運行的可執行程序。一般它的值爲:「/usr/local/bin」,使用時應寫爲:「$(exec_prefix)/bin」。 (使用「Autoconf」工具時,應該爲「@bindir@」)
sbindir
安裝可在shell中直接調用執行的程序。這些命令僅對系統管理員有用(系統管理工具)。一般它的值爲:「/usr/local/sbin」,要求在使用時應寫爲:「$(exec_prefix)/sbin」。(使用「Autoconf」工具時,應該爲「@sbindir@」)
libexecdir
用於安裝那些一般不是由用戶直接使用,而是由其它程序調用的可執行程序。一般它的值爲:「/usr/local/libexec」,要求在使用時應寫爲:「$(exec_prefix)/libexec」。(使用「Autoconf」工具時,應該爲「@libexecdir@」)
Ø 程序執行時使用的數據文件可從如下兩個方面來分類:
1. 是否可由程序更改。程序可更改或者不能更改的文件(雖然用戶可編輯其中某些文件)。
2. 是否和體系結構相關。和體系結構無關的文件,可被全部類型的機器共享;體系相關文件,僅可被相同類型機器、操做系統共享;其它的就是那些不能被任何兩個機器共享的文件。
這樣就存在六種不一樣的可能。除編譯生成的目標文件(.o文件)和庫文件之外,不推薦使用那些和特定機器體系結構相關的文件,使用和體系無關的數據文件更加簡潔,並且,它的實現也並不很是困難。
在Makefile中應該使用如下變量爲不一樣類型的文件指定對應的安裝目錄:
datadir
用於安裝和機器體系結構無關的只讀數據文件。一般它的值爲:「/usr/local/share」,使用時應寫爲:「$(prefix)/share」。(使用「Autoconf」工具時,應該爲「@datadir@」)。「$(infodir)」和「$(includedir)」做爲例外狀況,參考後續對它們的詳細描述。
sysconfdir
用於安裝從屬於特定機器的只讀數據文件,包括:主機配置文件、郵件服務、網絡配置文件、「/etc/passwd」文件等。全部該目錄下的文件都應該是普通文本文件(可識別的「ASCII」碼文本文件)。一般它的值爲:「/usr/local/etc」,在使用時應寫爲:「$(prefix)/etc」。(使用「Autoconf」工具時,應該爲「@sysconfdir@」)。
不要將可執行文件安裝在這個目錄下(可執行文件的安裝目錄應該是「$(libexecdir)」或者「$(sbindir)」)。也不要在這個目錄下安裝那些須要更改的文件(系統的配置文件等)。這些文件應該安裝在目錄「$(localstatedir)」下。
sharedstatedir
用於安裝那些可由程序運行時修改的文件,這些文件與體系結構無關。一般它的值爲:「/usr/local/com」,要求在使用時應寫爲:「$(prefix)/com」。(使用「Autoconf」工具時,應該爲「@sharedstatedir@」)
localstatedir
用於安裝那些可由程序運行時修改的文件,但這些文件和體系結構相關。用戶沒有必要經過直接修改這些文件來配置軟件包,對於不一樣的配置文件,將它們放在「$(datadir)」或者「$(sysconfdir)」目錄中。「$(localstatedir)」值一般爲:「/usr/local/var」,在使用時應寫爲:「$(prefix)/var」。(使用「Autoconf」工具時,應該爲「@localstatedir@」)
libdir
用於存放編譯後的目標文件(.o)文件庫文件(文檔文件或者執行的共享庫文件)。不要在此目錄下安裝可執行文件(可執行文件應該安裝在目錄「$(libexecdir)」下)。變量libdir值一般爲:「/usr/local/lib」,使用時應寫爲:「$(exec_prefix)/lib」。(使用「Autoconf」工具時,應該爲「@libdir@」)
infodir
用於安裝軟件包的 Info 文件。它的缺省值爲:「/usr/local/info」,使用時應寫爲:「$(prefix)/info」。(使用「Autoconf」工具時,應該爲「@infodir@」)
lispdir
用於安裝軟件包的Emacs Lisp 文件的目錄。它的缺省值爲:「/usr/local/share/emacs/site-lisp」,使用時應寫爲:「$(prefix)/share/emacs/site-lisp」。當使用Autoconf工具時,應將寫爲「@lispdir@」。爲了保證「@lispdir@」可以正常工做,須要在「configure.in」文件中包含以下部分:
lispdir='${datadir}/emacs/site-lisp'
AC_SUBST(lispdir)
includedir
用於安裝用戶程序源代碼使用「#include」包含的頭文件。它的缺省值爲:「/usr/local/include」,使用時應寫爲:「$(prefix)/include」。 (使用「Autoconf」工具時,應該爲「@includedir@」)。
除GCC外的大多數編譯器不會在目錄「/usr/local/include」中搜尋頭文件,所以這種方式只適用GCC編譯器。這一點應該不是一個問題,由於不少狀況下一些庫須要GCC才能工做。對那些依靠其它編譯器的庫文件,須要將頭文件安裝在兩個地方,一個由變量 「includedir」指定,另外一個由變量「oldincludedir」指定。
oldincludedir
它所指定的目錄也一樣用於安裝頭文件,這些頭文件用於非GCC的編譯器。它的缺省值爲:「/usr/include」。(使用「Autoconf」工具時,應該爲「@oldincludedir@」)。
Makefile在安裝頭文件時,須要判斷變量「oldincludedir」的值是否爲空。若是是空值,就不使用它進行頭文件的安裝(通常是安裝完成「/usr/local/include」下的頭文件以後才安裝此目錄下的頭文件)。
一個軟件包的安裝不能替換該目錄下已經存在的頭文件,除非是同一個軟件包(從新使用相同的軟件包在此目錄下安裝頭文件)。例如,軟件包「Foo」須要在「oldincludedir」指定的目錄下安裝一個頭文件「foo.h」時,可安裝的條件爲:1. 目錄「$(oldincludedir)」目錄下不存在頭文件「foo.h」;2. 已經存在頭文件「foo.h」,存在的頭文件「foo.h」是以前軟件包「Foo」安裝的。
檢查頭文件「foo.h」是否來自於軟件包Foo,須要在頭文件的註釋中包含一個「magic」字符串,使用命令「grep」來在該文件中查找這個magic。
Ø Unix風格的幫助文件須要安裝在如下目錄中:
mandir
安裝該軟件包的幫助文檔(若是有)的頂層目錄。它的缺省值爲:「/usr/local/man」,要求在使用時應寫爲:「$(prefix)/man」。 (使用「Autoconf」工具時,應該爲「@@mandir@@」)
man1dir
用於安裝幫助文檔的第一節(man 1)。它的缺省值爲:「$(mandir)/man1」。
man2dir
用於安裝幫助文檔的第二節(man 2)。它的缺省值爲:「$(mandir)/man2」。
...
不要將GNU 軟件的原始文檔做爲幫助頁。應該編寫使用手冊。幫助頁僅僅是爲了幫助用戶在Unix上方便運行GNU軟件,它是附屬的運行程序。
manext
文件名擴展字,它是對安裝手冊的擴展。以點號(.)開始的十進制數。缺省值爲:「.1」。
man1ext
幫助文檔的第一節(man 1)的文件名擴展字。
man2ext
幫助文檔的第二節(man 2)的文件名擴展字。
...
當一個軟件包的幫助手冊有多個章節時,使用這些變量代替「manext」。(第一節「man1ext」,第二節「man2ext」,第三節「man3ext」……)
Ø 並且下列這些變量也應該在Makefile中定義:
srcdir
此變量指定的目錄是須要編譯的源文件所在的目錄。該變量的值在使用「configure」腳本對軟件包進行配置時產生的。(使用「Autoconf」工具,應該書寫爲「srcdir = @srcdir@」)
例如:
# 安裝的普通目錄路徑前綴。
# 注意:該目錄在開始安裝前必須存在
prefix = /usr/local
exec_prefix = $(prefix)
# 放置「gcc」命令使用的可執行程序
bindir = $(exec_prefix)/bin
# 編譯器須要的目錄
libexecdir = $(exec_prefix)/libexec
# 軟件包的Info文件所在目錄
infodir = $(prefix)/info
在用戶標準指定的目錄下安裝大量文件時,能夠將這些文件分類安裝在指定目錄的多個子目錄下。能夠在Makefile中實現一個「install」僞目標來描述安裝這些文件的命令(包括建立子目錄,安裝文件到對應的子目錄中)。
在發佈的軟件包中,不能強制要求用戶必須指定這些安裝目錄的變量。使用一套標準的安裝目錄變量來指定安裝目錄,當用戶須要指定安裝目錄時,經過修改變量定義來指定具體的目錄,在用戶沒有指定的狀況下,使用默認的目錄。
全部GNU發佈的軟件包的Makefile中,必須包含如下這些目標:
all
此目標的動做是編譯整個軟件包。「all」應該爲Makefile的終極目標。該目標的動做不重建任何文檔文件(只編譯全部的源代碼,生成可執行程序);Info文件應該做爲發佈文件的一部分,DVI文件只在明確指定的時候才應該被重建。
缺省狀況下,對全部源程序的編譯和鏈接應該使用選項「-g」,是最終的可執行程序中包含調試信息。當最終的可執行程序不須要包含調試信息時,可以使用「strip」去掉可執行程序中的調試符號已縮減最終的程序大小。
install
此目標的動做是完成程序的編譯並將最終的可執行程序、庫文件等拷貝到安裝的目錄。若是隻是驗證這些程序是否可被正確安裝,它的動做應該是一個測試安裝動做。
安裝時通常不要對可執行程序進行strip(去掉可執行程序內部的調試信息)。存在另一個目標「install-strip」,它實現安裝時strip。
儘量保證目標「install」的動做不更改程序建立目錄(builid目錄)下的任何文件,對這個目錄下文件的修改(重建或者更新)是目標「all」所要定義的動做。
「install」目標定義的動做在安裝目錄不存在時,可以建立這些不存在的安裝目錄。這些目錄包括:變量「prefix」和「exec_prefix」指定的目錄和全部必要的子目錄。完成此任務的方式可使用下邊介紹的「installdirs」目標。
在安裝man文檔的命令前使用「-」忽略這安裝命令的錯誤,這樣能夠避免在沒有Unix man文檔的系統上執行安裝時出現錯誤。
安裝Info文檔的方法是使用變量「INSTALL_DATA」將Info文檔拷貝到「$(infodir)」目錄下去(參考 13.4安裝目錄的變量 一節),若是存在「install-info」命令則執行它。「install-info」是一個編輯Info「dir」文件的程序,更新或者修改「info」文檔的入口和目錄;它是Texinfo軟件包的一部分。這裏有一個安裝Info文檔的例子:
$(DESTDIR)$(infodir)/foo.info: foo.info
$(POST_INSTALL)
# 可能在「.」(當前目錄)存在一個新的文檔,而不是「srcdir」。
-if test -f foo.info; then d=.; \
else d=$(srcdir); fi; \
$(INSTALL_DATA) $$d/foo.info $(DESTDIR)$@; \
#若是install-info命令存在則運行它
# 使用「if」代替在命令行前的「-」
# 這樣,就能夠看到運行install-info產生的真正錯誤
# 咱們使用「$(SHELL) -c」是由於在一些shell中
# 遇到未知的命令不會運行失敗
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
目標install的命令須要分爲三類:正常命令、預安裝命令和安裝後命令。參考 13.6 安裝命令分類 一節
uninstall
刪除全部已安裝文件——由install建立的文件拷貝。規則所定義的命令不能修改編譯目錄下的文件,僅僅是刪除安裝目錄下的文件。像install目標的命令同樣,uninstall目標的命令也分爲三類。參考 13.6 安裝命令分類 一節
install-strip
和目標install的動做相似,可是install-strip指定的命令在安裝時對可執行文件進行strip(去掉程序內部的調試信息)。它的定義以下:
install-strip:
$(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' \
install
若是軟件包的存在安裝腳本時,目標install-strip所定義的命令就不能是對目標「install」的引用,它所完成的僅僅是對可執行文件strip。
「install-strip」不該該直接在build目錄下對可執行文件進行strip,應該是對安裝目錄下的可執行文件進行strip。就是說「install-strip」所的命令不能build目錄下的文件產生影響。
通常不建議安裝時對可執行文件進行strip,由於去掉可執行文件的調試信息後,在出現bug時,就不能對可執行程序進行跟蹤調試。
clean
清除當前目錄下編譯生成的全部文件,這些文件由make程序建立。記住,不要刪除軟件包的配置文件,同時須要保留build時建立的那些文件(諸如:建立的目錄、build生成的信息記錄文件等)。由於這些文件都是發佈版本的一部分。
對於.dvi文件,當它不做爲發佈版本的一部分時,能夠刪除。
distclean
刪除當前目錄下的的配置過程產生的文件、build過程產生的文件。目標「distclean」指定的刪除命令應該刪除軟件包中全部非發佈文件。
mostlyclean
相似於目標「clean」,可是可保留一些編譯生成的文件,避免在下次編譯時對這些文件重建。例如,用於GCC的目標「mostlyclean」不刪除文件「libgcc.a」,由於在絕大多數狀況下它都不須要從新編譯。
maintainer-clean
此目標所定義的命令幾乎會刪除全部當前目錄下可以由Makefile重建的文件。典型的,包括目標「distclean」刪除的文件、由Bison生成的.c源文件、tags記錄文件、Ifon文件等。可是有一個例外,就是執行「make maintainer-clean」不能刪除「configure」這個配置腳本文件,即便「configure」能夠由Makefile生成。由於「configure」是軟件包的配置腳本。
目標「maintainer-clean」應該只能由維護軟件包的用戶使用,而不能被普通用戶使用。由於它會刪除一些軟件包的發佈文件,而重建這些文件可能須要專門的工具。所以咱們在使用此目標是須要當心。
爲了讓用戶可以在執行前獲得提示,一般目標「maintainer-clean」的命令如下兩行爲開始:
@echo 「該命令用於維護此軟件包的用戶使用」;
@echo 「它刪除的文件可能須要使用特殊的工具來重建。」
TAGS
此目標所定義的命令完成對該程序的tags記錄文件的更新。
info
產生必要的Info文檔。此目標應該按照以下書寫:
info: foo.info
foo.info: foo.texi chap1.texi chap2.texi
$(MAKEINFO) $(srcdir)/foo.texi
必須在Makefile定義變量「MAKEINFO」,表明命令工具makeinfo,該工具是發佈軟件Texinfo的一部分。
一般,GNU的發佈程序會和Info文檔會被一同建立,這意味着Info文檔是在源文件的目錄下。用戶在建立發佈軟件時,通常狀況下,make不更新Info文檔,由於它們已經更新到最新了。
dvi
爲全部的Texinfo文件建立對應的DVI文件。例如:
dvi: foo.dvi
foo.dvi: foo.texi chap1.texi chap2.texi
$(TEXI2DVI) $(srcdir)/foo.texi
必須在Makefile中定義變量「TEXI2DVI」。它表明命令工具texi2dvi,該工具是發佈軟件Texinfo一部分。
規則中也能夠沒有命令行,這樣make程序會自動爲它推導對應的命令。
dist
此目標指定的命令建立發佈程序的tar文件。建立的tar文件應該是這個軟件包的目錄,文件名中也能夠包含版本號(就是說建立的tar文件在解包以後應該是一個目錄)。例如,發佈的GCC 1.40版的tar文件解包的目錄爲「gcc-1.40」。
一般的作法是是建立一個空目錄,如使用ln或cp將所須要的文件加入到這個目錄中,以後對這個目錄使用tar進行打包。打包以後的tar文件使用gzip壓縮。例如,實際的GCC 1.40版的發佈文件叫「gcc-1.40.tar.gz」。
目標「dist」的依賴文件爲軟件包中全部的非源代碼的文件,所以在使用目標進行發佈軟件打包壓縮以前必須保證這些文件是最新的。
check
此目標指定的命令完成全部的自檢功能。在執行檢查以前,應確保全部程序已經被建立,能夠不安裝。爲了對它們進行測試,須要實如今程序沒有安裝的狀況下被執行的規則命令。
的目標,對於各類程序它們頗有用:
installcheck
執行安裝檢查。在執行安裝檢查以前,確保全部程序已經被建立而且被安裝。須要注意的是:安裝目錄「$(bindir)」是否在搜索路徑中。
installdirs
使用目標「installdirs」建立安裝目錄以及它的子目錄在不少場合是很是有用的。腳本「mkinstalldirs」就是爲了實現這個目的而編寫的;發佈的Texinfo軟件包中就包含了這個腳本文件。Makefile中的規則能夠這樣書寫:
# 確保全部安裝目錄(例如 $(bindir))存在,若有必要則建立這些目錄
installdirs: mkinstalldirs
$(srcdir)/mkinstalldirs $(bindir) $(datadir) \
$(libdir) $(infodir) \
$(mandir)
或者可使用變量「DESTDIR」:
# 確保全部安裝目錄(例如 $(bindir))存在,若有必要則建立這些目錄
installdirs: mkinstalldirs
$(srcdir)/mkinstalldirs \
$(DESTDIR)$(bindir) $(DESTDIR)$(datadir) \
$(DESTDIR)$(libdir) $(DESTDIR)$(infodir) \
$(DESTDIR)$(mandir)
該規則不能更改軟件的編譯目錄,僅僅是建立程序的安裝目錄。
在爲Makefile書寫「install」目標時,須要將其命令分爲三類:正常命令、安裝前命令和安裝後命令。
正常命令是把文件移動到合適的地方,並設置它們的模式。這個過程不修改任何文件,僅僅是把須要安裝的文件從軟件包中拷貝到安裝目錄。
安裝前命令和安裝後命令可能修改某些文件;一般,修改一些配置文件和系統的數據庫文件。典型地,安裝前命令在正常命令以前執行,安裝後命令在正常命令執行後執行。
大多數狀況是,安裝後命令是運行「install-info」程序。它所完成的工做不能由正常命令完成,由於它更新了一個文件(Info的目錄),該文件不能單獨的或者完整的從軟件包來進行安裝,由於它只能在正常安裝命令完成安裝軟件包的info文檔以後纔可正確執行。
大多數程序不須要安裝前命令,但應該在Makefile中提供。
將「install」規則的命令分爲這三類時,應該在命令之間插入分類行(category lines)。分類行說明了後續須要執行的命令的類別。
分類行是由一個[Tab]字符開始的make的特殊變量的引用,行尾是可選的註釋內容。可使用三個特殊的變量,每個表明一種類別。分類行不能出如今普通的執行文件中,由於這三個特殊的make變量沒有定義(也不該該在Makefile中定義它們)。
如下是三種可能的分類行,並對它們進行了註釋:
$(PRE_INSTALL) # 如下是安裝前命令
$(POST_INSTALL) # 如下是安裝後命令
$(NORMAL_INSTALL) # 如下是正常命令
若是安裝規則(install所在的規則)的命令行沒有使用分類行,那麼在第一個出現的分類行以前的全部的命令行都被認爲是正常命令。若是命令行中沒有分類行,那麼規則的全部命令行都都被認爲是正常命令行。
對應的,一下這三個是「uninstall」命令的分類行:
$(PRE_UNINSTALL) #如下是卸載前命令
$(POST_UNINSTALL) #如下是卸載後命令
$(NORMAL_UNINSTALL) #如下是正常命令
卸載前命令應該是取消軟件包Info文檔的入口。
若是目標install或 uninstall存在依賴,其做爲安裝的子例程,那麼就應該在每個以來目標的命令行開始使用分類行,同時目標insall和uninstall的命令行開始也須要使用分類行。這樣就能夠確保任何調用方式時每一條命令都被正確的分類。
除下列命令外,安裝前命令和安裝後命令不該該使用其它命令:
[ 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
按照這種方式分類命令的緣由是爲了建立二進制的軟件包。典型的二進制軟件包包括可執行文件、必須安裝的其它文件以及它本身的安裝文件,所以二進制軟件包就不須要任何正常命令、只須要安裝前命令和安裝後命令。
建立二進制軟件包的程序經過提取安裝前命令和安裝後命令工做。這裏有一個抽取安裝前命令的方法:
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」可能包括如下內容:
$0 ~ /^\t[ \t]*(normal_install|post_install)[ \t]*$/ {on = 0}
on {print $0}
$0 ~ /^\t[ \t]*pre_install[ \t]*$/ {on = 1}
安裝前命令的執行結果和軟件包的安裝shell腳本的結果同樣。