跳至內容

GNU make/簡介

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

手冊目錄

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如何執行一個Makefile的指令[編輯]

在默認情況下,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)。