跳至內容

GNU make/列印版

維基教科書,自由的教學讀本

聲明,本文翻譯的是GNU make 3.8.1版用戶手冊;本手冊的原作者是司徒文(Richard M. Stallman),麥家府(Roland McGrath),史密斯(Paul D. Smith)。原手冊發布於2006年4月。此為原文下載地址

make概述[編輯]

make工具能自動判斷一個大程序的哪些部分需要再編譯,並且能用命令來執行再編譯操作。本文檔討論的GNU make原來是由司徒文與麥家府開發製作的,從3.76版開始由史密斯接管開發工作。

GNU make遵從IEEE 1003.2-1992標準(POSIX.2)第6.2條。

在本文例子中,筆者用的是C語言程序,這是因為C語言最為流行,但讀者也可以用make來完成其他程式語言的編譯工作,前提是這種語言的編譯器能在 shell下運行。當然,make的應用不局限於程序;如果遇到一些任務,這些任務的文件必須隨著其他文件或數據的變化而更新,那麼讀者就可以用make 來應對這些任務。

在使用make之前,請讀者先寫好Makefile文件,該文件須描述清楚程序文件之間的關係,還要給出更新每個文件的命令。通常,一個程序的可執行文件由OBJ文件來更新,而這些OBJ文件則由原始碼編譯產生。

只要有合適的Makefile文件,在修改了一些原始碼後,只要在Shell命令行下輸入:

make

就足以完成所有必要的再編譯。make程序通過Makefile的資料庫以及諸文件的最後修改時間來判斷哪些文件需要更新。對於每個需要更新的文件, make會用資料庫中記錄的命令來對其操作。

讀者可以給make加上命令行參數,以此告訴make哪些文件需要再編譯、怎樣再編譯。參見第九章如何運行make

如何使用本文檔[編輯]

如果讀者是個make新手,或者想閱讀大體介紹,那麼在閱讀每章時只需少量的看前幾節,後面幾節可以略過。每章的前幾節會有該章介紹以及該章的大體信息,而後幾節則會包括一些特殊的或技術性的信息。但第二章 make簡介是個例外,全章都是介紹性的內容。

如果讀者對其他make程序比較熟悉的話,請參閱第十二章 GNU make的特色功能,該章列出了GNU make的增強功能;另外請參閱第十三章 GNU make與標準make不兼容的部分,該章舉出GNU make在少數功能上遜色於其他make的例子。

如果需要閱讀摘要,請看第九章第七節 選項摘要附錄 A 快速參考以及第四章第八節所講的特殊目標

問題與漏洞[編輯]

如果讀者發現了GNU make的一些問題或者認為發現了漏洞,那麼請與開發者聯繫;我們不作任何承諾,但我們會盡力解決問題。

在提交漏洞報告之前,請核實漏洞的真實性。另外請認真反覆閱讀文檔,確定文檔介紹的應用方法在實際操作中有效,如果文檔對功能上的一些問題沒有闡述清楚,那麼也請提交報告,這些都是文檔的「漏洞」!

在提交漏洞報告或者自己解決漏洞之前,請將Makefile文件簡化至最小且能體現漏洞問題的程度。然後把Makefile文件連同出錯和警告信息一齊發給我們。請勿改動原信息,最好將其剪切複製到漏洞報告中。請確保用以生成最簡Makefile文件的命令中不使用非自由軟體或者不常用的程序(其實對於這種工具,讀者隨時都可以用簡單的幾句Shell命令來測試)。最後,請詳述Makefile本應有的預期結果,以便於我們判斷問題是否出在文檔上。

如果讀者發現了一個如假包換的錯誤,那麼可以通過以下兩種途徑提交報告:一、通過發送電子郵件到:

bug-make@gnu.org

與我們聯繫;
二、使用我們的在線專案管理來提交報告,網址是:

http://savannah.gnu.org/projects/make/


除了上述的信息以外,請將使用的make完整的版本號一併發給我們;讀者可以通過使用命令「make --version」來獲取版本信息。請確保報告中包含make所運行機器的硬體信息與作業系統信息;順便提一句,獲取這些信息的一種方法是使用命令 「make --help」,一切盡在命令的最後一行輸出中。

make簡介[編輯]

首先要有一個名為「Makefile」的文件告訴make需要作什麼。絕大多數情況下,Makefile主要讓make完成編譯與連結程序的工作。

本章我們討論一個簡單的Makefile,這個Makefile會編譯與連結一個由八個C原始碼文件和三個頭文件組成的文本編輯器程序。該 Makefile還會告訴make如何在有明確指示的時候運行各種各樣的命令(比如說接到清除指示時,make運行刪除某些文件的命令)。要閱讀更複雜的 Makefile代碼,翻至附錄C 一個複雜的Makefile實例

當make重新編譯該文本編輯器時,所有的C原始碼文件都會被重新編譯。如果一個頭文件被修改,那麼為了保證整體程序無損,包含該頭文件的所有C原始碼文件都必須要重新編譯。原始碼文件每經過一次編譯都會有一個與自己對應的OBJ文件生成。最後,若有原始碼文件被重新編譯,所有OBJ文件不管是以前生成的還是新生成的,都會被用來連結生成新的可執行文本編輯器程序。

規則大概是什麼樣的[編輯]

一個簡單的Makefile所包含的「規則」形式如下

目标 ...:先决条件
     命令
     ...
     ...

目標的名稱一般與生成的文件名稱一致(像可執行程序和OBJ文件)。目標的名稱 也可以是要進行的動作,比如說「clean」(參見第四章第五節 偽目標)。

先決條件是用以生成目標的輸入文件,而一個目標往往依賴於多個文件。

命令是make要執行的動作,而一個規則往往要多行命令。請注意:在每個命令前都必須輸入一個Tab!一般粗心大意的人容易在這個問題上犯錯。

通常有先決條件規則的命令會在先決條件被改動之後執行。但也有一些執行特殊命令的規則沒有先決條件,比如說目標「clean」所在的規則就沒有先決條件,該規則含有刪除命令。

接下來規則會說明與特定規則關聯的文件應該何時被操作,如何被操作。然後make執行先決條件中的命令,用以生成或更新目標。此外,規則還可以對何時與如何進行一個動作下令。參見第四章 編寫規則

除了規則之外,Makefile文件也可以有其他內容,但一個簡單的Makefile只需有規則即可。規則寫出來會比模板中所寫的略顯複雜,但或多或少含有與其一致的部分。

一份簡單的Makefile文件[編輯]

下面有一份簡單易懂的Makefile文件,該文件描述了各種依賴關係:可執行程序edit依賴於八個OBJ文件,八個OBJ文件依賴於八個C原始碼文件和三個頭文件。

在本例中,所有C原始碼文件都包含了「defs.h」頭文件,只有定義了編輯命令的C原始碼文件才包含「command.h」頭文件,而只有只有能改變編輯器緩衝區的低層C原始碼文件才包含「buffer.h」頭文件。

     edit : main.o kbd.o command.o display.o \
            insert.o search.o files.o utils.o
             cc -o edit main.o kbd.o command.o display.o \
                        insert.o search.o files.o utils.o
     
     main.o : main.c defs.h
             cc -c main.c
     kbd.o : kbd.c defs.h command.h
             cc -c kbd.c
     command.o : command.c defs.h command.h
             cc -c command.c
     display.o : display.c defs.h buffer.h
             cc -c display.c
     insert.o : insert.c defs.h buffer.h
             cc -c insert.c
     search.o : search.c defs.h buffer.h
             cc -c search.c
     files.o : files.c defs.h buffer.h command.h
             cc -c files.c
     utils.o : utils.c defs.h
             cc -c utils.c
     clean :
             rm edit main.o kbd.o command.o display.o \
                insert.o search.o files.o utils.o


本例用反斜線「\」將每個長行都分成兩行,如此功能上與長行相同,而且減少了閱讀長行的難度。

要用此Makefile生成可執行程序edit,請鍵入:

make

要用此Makefile刪除目錄中所有OBJ文件和可執行程序「edit」,請鍵入:

make clean

在本例中,目標包括,可執行程序「edit」以及OBJ文件「main.o」與「kdb.o」;先決條件也有許多,如「main.c」與「defs.h」;而每個「.o」文件都既為目標,又是先決條件;命令包括「cc -c main.c」與「cc -c kbd.c」。

若目標為文件,則該文件會在其先決條件被改動後重編譯與連結。另外,會自動生成的先決條件首先更新。本例中,「edit」依賴於八個OBJ文件;OBJ文件「main.o」依賴於原始碼文件「main.c」與頭文件「defs.h」。

Shell命令跟隨在目標與先決條件的下一行,該命令控制更新目標文件。每個命令行首必須要有Tab鍵入,以此區分命令行與Makefile中的其他行。(想想看,make全然不知命令是如何工作的,必須由程式設計師提供命令來更新目標。make所能幹的只有執行程式設計師給出命令,然後再根據命令判斷目標文件是否需要更新;如需要,更新之)。

目標「clean」不是一個文件,僅僅是動作的名稱而已。因為這個規則不包含其他動作,而且也不是其他任何規則的先決條件,所以,make不會執行此處操作,除非被特別指定。請注意,此規則既非其他規則之先決條件,亦無先決條件,故此規則唯一目的為運行特定命令。目標若只含命令而與其他文件不相關聯,則稱此目標為偽目標。可參閱第四章第五節 偽目標,了解詳情。另外,請參閱第五章第五節 命令中的報錯信息,了解如何忽略rm或者其他命令的報錯信息。

make如何執行一個Makefie的指令[編輯]

在默認情況下,make的工作始於第一個目標(只要這個目標不是以「。」開頭的)。此目標稱為默認最終目標(如果不想使用這個默認規定,可以在命令行中輸入「make 目標名」(參見第九章第二節 用以確定最終目標的參數)或者更改變量(參見第三章第六節 其它的特殊變量)「.DEFAULT_GOAL」以改變默認最終目標)。

上一節簡單示例中,默認最終目標是更新可執行程序「edit」,故將其列入規則首位。

因此,輸入以下命令:

make

make首先讀取本目錄的Makefile,而後開始運行第一個規則。在此例中,規則用以重連結「edit」;但在執行這條規則之前,make必須完成所有「edit」依賴的規則,這些規則就是那些OBJ文件;而每個OBJ文件都會運行自己的一套規則,這些規則會通過編譯各自原始碼的方式來更新每個「.o」文件。如果頭文件或者原始碼文件的修改時間比依賴其的OBJ文件要晚,或者OBJ文件不存在,那麼重新編譯就必須要進行。

那些非最終目標之所以會被運行,是因為最終目標與之相關。而與最終目標無關的規則就不會被執行,除非被特指(如「make clean」)。

在重編譯一個OBJ文件之前,make會先更新其先決條件,先決條件包括原始碼文件與頭文件。此例中的Makefile並沒有針對原始碼文件與頭文件的操作,另外這些「.c」與「.h」文件也不是規則的名稱,故make在此不做任何事情。但make會自動更新自動生成的C程序,比如說由Bison或Yacc生成的,由此次規則生成的。

在編譯完所有需要的OBJ文件後,make會決定是否重連結「edit」。如果「edit」文件不存在或者OBJ文件比「edit」文件要新,則重連結工作一定要作。如果OBJ文件是剛編譯出的,其時間晚於「edit」文件,故此時「edit」文件要被重連結。

因此,如果更改了「insert.c」文件再運行make;make會先編譯「insert.c」文件生成「insert.o」文件,再連結生成「edit」文件。若更改的是「command.h」,再運行make;make會編譯生成「kdb.o」、「command.o」、「files.o」再連結生成「edit」。

用變量簡化Makefile[編輯]

在上例中,生成「edit」的OBJ文件組被列舉了兩次(這裡在重複一次):

     edit : main.o kbd.o command.o display.o \
                   insert.o search.o files.o utils.o
             cc -o edit main.o kbd.o command.o display.o \
                        insert.o search.o files.o utils.o


如此重複書寫,容易造成錯誤;另外,若有新OBJ文件加入系統,本應可以只添加新文件而不考慮原有的OBJ文件。若採用變量協助工作,則既可減少出錯機率,亦能簡化Makefile。變量允許先對一段文本字符串定義,以後需要書寫諸OBJ文件時,可以只輸入變量而不需要把所有OBJ文件列出(參見第六章 如何使用變量)。

此處,一個習慣性做法是,在每個Makefile中加入名為「objects」、「OBJECTS」、「objs」、「OBJS」、「obj」或者「OBJ」的變量,用以列出全部OBJ文件的名稱。在Makefile中,可以用如下方式定義變量「objects」:

 objects = main.o kbd.o command.o display.o \
               insert.o search.o files.o utils.o


定義之後,文件時需要列出諸OBJ文件處可用「$(objects)」代替(參見第六章 如何使用變量)。

下面就是用變量來描述諸OBJ文件的Makefile,簡單而完整:

objects = main.o kbd.o command.o display.o \
               insert.o search.o files.o utils.o
     
     edit : $(objects)
             cc -o edit $(objects)
     main.o : main.c defs.h
             cc -c main.c
     kbd.o : kbd.c defs.h command.h
             cc -c kbd.c
     command.o : command.c defs.h command.h
             cc -c command.c
     display.o : display.c defs.h buffer.h
             cc -c display.c
     insert.o : insert.c defs.h buffer.h
             cc -c insert.c
     search.o : search.c defs.h buffer.h
             cc -c search.c
     files.o : files.c defs.h buffer.h command.h
             cc -c files.c
     utils.o : utils.c defs.h
             cc -c utils.c
     clean :
             rm edit $(objects)

讓make自己推算出要執行的命令[編輯]

不必將每個編譯C原始碼文件的命令全部寫出,因為make可以構造出這些命令:make有一個隱規則可通過命令「cc -c」生成與「.c」文件前綴名一致的「.o」文件的命令,比如將「main.c」編譯成「main.o」的命令「cc -c main.c -o main.o」。此處可以把命令中寫「.o」的部分省略。參閱第十章 使用隱規則

當「.c」文件以下例方法自動被使用時,「.c」文件會自動加入到先決條件中。在先決條件中省略「.c」文件的同時,編譯命令也被省略了。

下面是個完整的例子。例子中包括了上面兩種省略方法,而且也使用了上一節所說的「objects」變量。

  objects = main.o kbd.o command.o display.o \
               insert.o search.o files.o utils.o
     
     edit : $(objects)
             cc -o edit $(objects)
     
     main.o : defs.h
     kbd.o : defs.h command.h
     command.o : defs.h command.h
     display.o : defs.h buffer.h
     insert.o : defs.h buffer.h
     search.o : defs.h buffer.h
     files.o : defs.h buffer.h command.h
     utils.o : defs.h
     
     .PHONY : clean
     clean :
             rm edit $(objects)


本例與實際應用中Makefile相差無幾(複雜化的「clean」規則在其他地方有所討論。參見第四章第五節 偽目標第五章第五節 命令中的報錯信息)。

隱規則使用簡便,故尤為重要。讀者會頻繁見到隱規則的使用。

另一種格式的Makefile[編輯]

當組成Makefile的全為規則時,可以用另一種形式的Makefile代替前面的。下面這種形式的Makefile全部以先決條件代替,此為全文:

     objects = main.o kbd.o command.o display.o \
               insert.o search.o files.o utils.o
     
     edit : $(objects)
             cc -o edit $(objects)
     
     $(objects) : defs.h
     kbd.o command.o files.o : command.h
     display.o insert.o search.o files.o : buffer.h


這裡,「defs.h」為所有OBJ文件的先決條件;「command.h」與「buffer.h」為特定OBJ文件的先決條件。

不知這樣緊湊的寫法是否為一些人的愛好,但還是有一些人不喜歡這種寫法,認為將一個目標跟與之對應的信息寫在一起顯得比較清楚。

清理目錄的規則[編輯]

編寫Makefile所要寫的不僅僅編譯程序。Makefile還能做一些編譯外的事情,比如說,如何將一個目錄里的OBJ文件與可執行文件清理乾淨。

下面就是一例,演示如何用make以完成清除工作:

     clean:
             rm edit $(objects)


在實際應用中Makefile可能會寫得更複雜,以應對突發事件,下為例:

.PHONY : clean
     clean :
             -rm edit $(objects)


本例的Makefile,可在有文件名為「clean」時避免混淆,還可在rm報錯時仍舊執行。(參見第四章第五節 偽目標第五章第五節 命令中的報錯信息

此類規則不可置於Makefile文首,這是由於一般不願讓其作為默認規則執行!故在前幾節的完整Makefile示例中,用「edit」為默認規則,以便編譯與重編譯。

因為「clean」並非「edit」的先決條件,故此規則在make沒有參數時不會運行。若要運行此規則,鍵入命令「make clean」(參見第九章 如何運行make)。

編寫Makefile[編輯]

make重編譯系統的信息源於Makefile構建的資料庫。

Makefile裡包含的是什麼[編輯]

「Makefile」文件應該取什麼樣的名字[編輯]

包含其它的Makefile文件[編輯]

「MAKEFILES」變量[編輯]

「MAKEFILE_LIST」變量[編輯]

其它的特殊變量[編輯]

Makefile如可被make再執行[編輯]

調用其它Makefile的重要技巧[編輯]

make如何理解Makefile的內容[編輯]

編寫規則[編輯]

規則的語法結構[編輯]

先決條件的種類[編輯]

在文件名中使用通配符[編輯]

通配符示例[編輯]

使用通配符的陷阱[編輯]

「wildcard」函數[編輯]

為先決條件搜索目錄[編輯]

「VPATH」:為所有先決條件搜索路徑[編輯]

「vpath」指令[編輯]

目錄搜索是如何進行的[編輯]

編寫帶目錄搜索功能的Shell命令[編輯]

目錄搜索與隱規則[編輯]

為連接庫而做的目錄搜索[編輯]

偽目標[編輯]

無先決條件與命令的規則[編輯]

用目標對應的空文件記錄事件[編輯]

特殊內建目標的名稱[編輯]

單規則使用多目標[編輯]

單目標對應多規則[編輯]

靜態模式規則[編輯]

靜態模式規則的語法結構[編輯]

靜態模式規則與隱規則的比較[編輯]

帶雙冒號的規則[編輯]

自動生成先決條件[編輯]

編寫規則中的命令[編輯]

命令的語法結構[編輯]

將命令分為多行的方法[編輯]

在命令中使用變量[編輯]

輸出信息命令[編輯]

命令的執行[編輯]

選擇Shell[編輯]

並行執行[編輯]

命令中的報錯信息[編輯]

中斷或強制結束make[編輯]

遞歸使用make[編輯]

make的變量是怎樣工作的[編輯]

與低層的make互傳變量[編輯]

與低層的make互傳選項[編輯]

「--print-directory」選項[編輯]

定義封裝的命令組[編輯]

使用空命令[編輯]

如何使用變量[編輯]

變量的基本參考[編輯]

變量的兩種風格[編輯]

變量高級功能參考[編輯]

替換功能參考[編輯]

可運算的變量名[編輯]

變量如何獲取其值[編輯]

設置變量[編輯]

向變量追加文本內容[編輯]

「override」指令[編輯]

逐字定義變量[編輯]

來自環境變量的變量[編輯]

目標特定變量的賦值[編輯]

模式特定變量的賦值[編輯]

Makefile的條件控制部分[編輯]

條件示例[編輯]

條件的語法結構[編輯]

測試標誌位的條件[編輯]

文本操控函數[編輯]

函數調用的語法結構[編輯]

對字符串做替換與分解的函數[編輯]

對文件名操作的函數[編輯]

條件控制函數[編輯]

「foreach」函數[編輯]

「call」函數[編輯]

「value」函數[編輯]

「eval」函數[編輯]

「shell」函數[編輯]

控制make的函數[編輯]

如何運行make[編輯]

用以確定Makefile文件的參數[編輯]

用以確定最終目標的參數[編輯]

用其它功能來取代執行命令的參數[編輯]

避免重新編譯某些文件[編輯]

初始變量[編輯]

測試編譯程序[編輯]

選項摘要[編輯]

使用隱規則[編輯]

使用隱規則[編輯]

隱規則章目[編輯]

隱規則使用的變量[編輯]

隱規則鏈[編輯]

定義與重定義模式的規則[編輯]

模式的規則介紹[編輯]

模式的規則示例[編輯]

自變變量[編輯]

模式是如何匹配的[編輯]

匹配一切的模式規則[編輯]

取消隱規則[編輯]

定義去除依賴的默認規則[編輯]

過時的附加規則[編輯]

隱規則的搜索算法[編輯]

使用make更新歸檔文件[編輯]

歸檔成員作為目標[編輯]

為歸檔成員目標而設的隱規則[編輯]

更新歸檔字符目錄[編輯]

使用歸檔的風險[編輯]

歸檔文件的附加規則[編輯]

GNU make的特色功能[編輯]

GNU make與標準make不兼容的部分[編輯]

GNU make的一些不成文規定[編輯]

Makefile的通用習慣[編輯]

Makefile的工具[編輯]

指定變量的命令[編輯]

安裝目錄的變量[編輯]

為用戶設計的標準目標[編輯]

安裝命令的條目[編輯]

附錄[編輯]

A 快速參考[編輯]

B 常見錯誤[編輯]

C 一個複雜的Makefile實例[編輯]

下面是一段為GNU tar程序設計的Makefile代碼,相當複雜。

由於「all」是第一個目標,所以它也是默認目標。下面這個程序段有一個非常有趣的特色,這個特色就是原始碼程序中的「testpad.h」文件是一個由名叫testpad程序自動生成的,而這個testpad又是又是由「testpad.c」這個程序經過編譯產生的。

如果您輸入「make」或者「make all」,則make會讓名為「tar」的目標運行從而開始編譯指令,會讓名為「rmt」的目標運行從而生成一個遠程磁帶數據存取的守護程序,會讓名為 「tar.info」的目標運行從而生成INFO文檔。

如果您輸入「make install」,那麼make就執行的就不僅僅只是「tar」、「rmt」和「tar.info」這三個目標了,還會執行相應的安裝操作。< /br>
如果您輸入「make clean」,那麼make會清除所有的「.o」文件,以及「tar」、「rmt」、「testpad」、「testpad.h」和「core」這些文件。

如果您輸入「make distclean」,那麼make除了會清除「make clean」會清除的文件外,還會清除「TAGS」、「Makefile」和「config.status」這些文件(雖然不明顯,但下面這個 makefile和「config.status」都是用戶通過tar發行版中的「configure」文件生成的,這個「configure」文件這裡沒有列出)。

如果您輸入「make realclean」,那麼make除了清除「make distclean」會清除的文件外,還會清除由「tar.info」生成的INFO文檔。

# Generated automatically from Makefile.in by configure.
     # Un*x Makefile for GNU tar program.
     # Copyright (C) 1991 Free Software Foundation, Inc.
     
     # This program is free software; you can redistribute
     # it and/or modify it under the terms of the GNU
     # General Public License ...
     ...
     ...
     
     SHELL = /bin/sh
     
     #### Start of system configuration section. ####
     
     srcdir = .
     
     # If you use gcc, you should either run the
     # fixincludes script that comes with it or else use
     # gcc with the -traditional option.  Otherwise ioctl
     # calls will be compiled incorrectly on some systems.
     CC = gcc -O
     YACC = bison -y
     INSTALL = /usr/local/bin/install -c
     INSTALLDATA = /usr/local/bin/install -c -m 644
     
     # Things you might add to DEFS:
     # -DSTDC_HEADERS        If you have ANSI C headers and
     #                       libraries.
     # -DPOSIX               If you have POSIX.1 headers and
     #                       libraries.
     # -DBSD42               If you have sys/dir.h (unless
     #                       you use -DPOSIX), sys/file.h,
     #                       and st_blocks in `struct stat'.
     # -DUSG                 If you have System V/ANSI C
     #                       string and memory functions
     #                       and headers, sys/sysmacros.h,
     #                       fcntl.h, getcwd, no valloc,
     #                       and ndir.h (unless
     #                       you use -DDIRENT).
     # -DNO_MEMORY_H         If USG or STDC_HEADERS but do not
     #                       include memory.h.
     # -DDIRENT              If USG and you have dirent.h
     #                       instead of ndir.h.
     # -DSIGTYPE=int         If your signal handlers
     #                       return int, not void.
     # -DNO_MTIO             If you lack sys/mtio.h
     #                       (magtape ioctls).
     # -DNO_REMOTE           If you do not have a remote shell
     #                       or rexec.
     # -DUSE_REXEC           To use rexec for remote tape
     #                       operations instead of
     #                       forking rsh or remsh.
     # -DVPRINTF_MISSING     If you lack vprintf function
     #                       (but have _doprnt).
     # -DDOPRNT_MISSING      If you lack _doprnt function.
     #                       Also need to define
     #                       -DVPRINTF_MISSING.
     # -DFTIME_MISSING       If you lack ftime system call.
     # -DSTRSTR_MISSING      If you lack strstr function.
     # -DVALLOC_MISSING      If you lack valloc function.
     # -DMKDIR_MISSING       If you lack mkdir and
     #                       rmdir system calls.
     # -DRENAME_MISSING      If you lack rename system call.
     # -DFTRUNCATE_MISSING   If you lack ftruncate
     #                       system call.
     # -DV7                  On Version 7 Unix (not
     #                       tested in a long time).
     # -DEMUL_OPEN3          If you lack a 3-argument version
     #                       of open, and want to emulate it
     #                       with system calls you do have.
     # -DNO_OPEN3            If you lack the 3-argument open
     #                       and want to disable the tar -k
     #                       option instead of emulating open.
     # -DXENIX               If you have sys/inode.h
     #                       and need it 94 to be included.
     
     DEFS =  -DSIGTYPE=int -DDIRENT -DSTRSTR_MISSING \
             -DVPRINTF_MISSING -DBSD42
     # Set this to rtapelib.o unless you defined NO_REMOTE,
     # in which case make it empty.
     RTAPELIB = rtapelib.o
     LIBS =
     DEF_AR_FILE = /dev/rmt8
     DEFBLOCKING = 20
     
     CDEBUG = -g
     CFLAGS = $(CDEBUG) -I. -I$(srcdir) $(DEFS) \
             -DDEF_AR_FILE=\"$(DEF_AR_FILE)\" \
             -DDEFBLOCKING=$(DEFBLOCKING)
     LDFLAGS = -g
     
     prefix = /usr/local
     # Prefix for each installed program,
     # normally empty or `g'.
     binprefix =
     
     # The directory to install tar in.
     bindir = $(prefix)/bin
     
     # The directory to install the info files in.
     infodir = $(prefix)/info
     
     #### End of system configuration section. ####
     
     SRC1 =  tar.c create.c extract.c buffer.c \
             getoldopt.c update.c gnu.c mangle.c
     SRC2 =  version.c list.c names.c diffarch.c \
             port.c wildmat.c getopt.c
     SRC3 =  getopt1.c regex.c getdate.y
     SRCS =  $(SRC1) $(SRC2) $(SRC3)
     OBJ1 =  tar.o create.o extract.o buffer.o \
             getoldopt.o update.o gnu.o mangle.o
     OBJ2 =  version.o list.o names.o diffarch.o \
             port.o wildmat.o getopt.o
     OBJ3 =  getopt1.o regex.o getdate.o $(RTAPELIB)
     OBJS =  $(OBJ1) $(OBJ2) $(OBJ3)
     AUX =   README COPYING ChangeLog Makefile.in  \
             makefile.pc configure configure.in \
             tar.texinfo tar.info* texinfo.tex \
             tar.h port.h open3.h getopt.h regex.h \
             rmt.h rmt.c rtapelib.c alloca.c \
             msd_dir.h msd_dir.c tcexparg.c \
             level-0 level-1 backup-specs testpad.c
     
     .PHONY: all
     all:    tar rmt tar.info
     
     .PHONY: tar
     tar:    $(OBJS)
             $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
     
     rmt:    rmt.c
             $(CC) $(CFLAGS) $(LDFLAGS) -o $@ rmt.c
     
     tar.info: tar.texinfo
             makeinfo tar.texinfo
     
     .PHONY: install
     install: all
             $(INSTALL) tar $(bindir)/$(binprefix)tar
             -test ! -f rmt || $(INSTALL) rmt /etc/rmt
             $(INSTALLDATA) $(srcdir)/tar.info* $(infodir)
     
     $(OBJS): tar.h port.h testpad.h
     regex.o buffer.o tar.o: regex.h
     # getdate.y has 8 shift/reduce conflicts.
     
     testpad.h: testpad
             ./testpad
     
     testpad: testpad.o
             $(CC) -o $@ testpad.o
     
     TAGS:   $(SRCS)
             etags $(SRCS)
     
     .PHONY: clean
     clean:
             rm -f *.o tar rmt testpad testpad.h core
     
     .PHONY: distclean
     distclean: clean
             rm -f TAGS Makefile config.status
     
     .PHONY: realclean
     realclean: distclean
             rm -f tar.info*
     
     .PHONY: shar
     shar: $(SRCS) $(AUX)
             shar $(SRCS) $(AUX) | compress \
               > tar-`sed -e '/version_string/!d' \
                          -e 's/[^0-9.]*\([0-9.]*\).*/\1/' \
                          -e q
                          version.c`.shar.Z
     
     .PHONY: dist
     dist: $(SRCS) $(AUX)
             echo tar-`sed \
                  -e '/version_string/!d' \
                  -e 's/[^0-9.]*\([0-9.]*\).*/\1/' \
                  -e q
                  version.c` > .fname
             -rm -rf `cat .fname`
             mkdir `cat .fname`
             ln $(SRCS) $(AUX) `cat .fname`
             tar chZf `cat .fname`.tar.Z `cat .fname`
             -rm -rf `cat .fname` .fname
     
     tar.zoo: $(SRCS) $(AUX)
             -rm -rf tmp.dir
             -mkdir tmp.dir
             -rm tar.zoo
             for X in $(SRCS) $(AUX) ; do \
                 echo $$X ; \
                 sed 's/$$/^M/' $$X \
                 > tmp.dir/$$X ; done
             cd tmp.dir ; zoo aM ../tar.zoo *
             -rm -rf tmp.dir

D GNU 自由文檔許可證[編輯]

GNU 自由文檔許可證