Makefile概述
你需要 makefile 文件來告訴make
應該做些什麼。通常,makefile 會告訴make
如何編譯和鏈接一個程序。
make
在被明確要求時如何運行各種各樣不同的命令(例如,將某些文件進行清理操作移除掉)。要查看更複雜的 makefile 示例,請參閱[Complex Makefile]
。 當make
重新編譯編輯器時,必須重新編譯每個更改了的C源文件。如果頭文件已更改,則必須重新編譯每個包含該頭文件的C源文件以確保安全。每個編譯生成一個對應於源文件的目標文件。最後,如果任何源文件已經被重新編譯,所有的目標文件,無論是新建立的還是從先前的編譯保存的,都必須鏈接在一起以產生新的可執行的編輯器。
2.1 規則(rule)
一個簡單的makefile由下列形式的「規則(rule)」組成:
target ... : prerequisites ... recipe ... ...
target 通常是由程序所生成的文件的名稱;例如,target 是可執行文件或目標文件。target 也可以是所要執行的操作的名稱,例如「clean
」(請參閱[Phony Targets]
)。
prerequisites 作為輸入,用於創建 target 的文件。target 通常依賴於多個文件。
recipe 是make
所執行的操作。recipe 可能有多個命令,可以在同一行上,也可以分別在各自的行上。請注意:你需要在每個 recipe 行的輸入放置製表符!這是一個容易出錯的地方。如果你希望在 recipe 中添加製表符以外的其他字元,可以將變數.RECIPEPREFIX
設置為替代字元(請參見[Special Variables]
)。 通常情況下,recipe 需要具有 prerequisites ,並且在任何 prerequisites 發生更改時用於創建目標文件。但是,有些 rule 指定了 target 所要執行的 recipe,那麼這些 rule 就不需要 prerequisites 了。例如,包含刪除命令的 target「clean
」的 rule 就不具有 prerequisites。 然後,rule 解釋了如何以及何時重新製作特定文件,這個文件就是指這個 rule 的 target。make
執行 prerequisites 所對應的 recipe 來創建或者更新 target。rule 也可以解釋如何以及何時執行一個操作。參見[Writing Rules]
。 一個 makefile 可能包含除 rule 以外的其他文本,但一個簡單的 makefile 只需要包含 rule。rule 可能看起來比這個模板中所顯示的要複雜一些,但都或多或少地符合這個模式。
2.2 一個簡單的 Makefile
下面是一個簡單的 makefile,它描述了一個名為edit
的可執行文件依賴於八個目標文件的方式,而這些目標文件依次取決於八個C源文件和三個頭文件。
defs.h
,但只有那些定義編輯命令的文件包含command.h
,並且只有用於更改編輯器緩衝區的低級文件才包含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
我們使用反斜杠/換行符將每條長的命令分成兩行;雖然可以使用一個長的命令行,但短的命令行更容易閱讀。請參見[Splitting Long Lines]
。
edit
的可執行文件,請鍵入: make
要使用此 makefile 從文件夾中刪除可執行文件和所有目標文件,請鍵入:
make clean
在示例 makefile 中,target 包括可執行文件edit
以及目標文件main.o
和kbd.o
。prerequisites 是諸如main.c
和defs.h
之類的文件。實際上,每個.o
文件既是 target 又是 prerequisites。recipe 包括cc -c main.c
和cc -c kbd.c
。
edit
依賴於八個目標文件中的每一個;目標文件main.o
依賴於源文件main.c
和頭文件defs.h
。 recipe 緊隨著包含 target 和 prerequisites 的那一行。這些 recipe 說明了如何更新 target 文件。製表符(或任何由.RECIPEPREFIX
變數指定的字元;參見[Special Variables]
)必須位於 recipe 中每行的開頭,以便將 recipe 與 makefile 中的其他行區分開來。(請記住,make
並不知道 recipe 如何運行。您需要提供能夠正確更新目標文件的 recipe,make
所能做的就是在 target 文件需要更新時執行你指定的 recipe。) target clean
不是一個文件,而只是一個操作的名稱。由於通常不希望執行 rule 中的這個操作,因此clean
不是任何其他 rule 的 prerequisites。因此,除非你特別說明,否則make
不會做任何事情。請注意,這個 rule (clean
)不僅不是 prerequisites,也不具有任何 prerequisites,因此這個 rule 的唯一目的就是運行特定的 recipe。這種不指向文件而只是進行某種操作的 target 被稱為虛假目標(phony targets)。有關此類 target 的信息,請參見[phony targets]
。請參閱[Errors in Recipes]
,了解如何使make
忽略來自rm
或任何其他命令的錯誤。
2.3 make
如何處理 Makefile
默認情況下,make
從第一個 target 開始(不是以.
開頭的 target)。這被稱為默認目標(default goal)。(goal 是make
最終更新的 target。)你可以使用命令行(請參見 [Arguments to Specify the Goals]
)或使用特殊變數.DEFAULT_GOAL
(參見[Other Special Variables]
)來覆蓋此操作。
edit
;因此,我們把這條 rule 放在了最前面。 因此,當你發出命令: make
make
讀取當前目錄中的 makefile 並從處理第一條 rule 開始。在該示例中,此 rule 用於重新鏈接edit
;但是在make
可以完全處理這個 rule 之前,它必須處理edit
所依賴文件的 rule,在這個例子中edit
所依賴的文件就是目標文件。每個文件都按照自己的 rule 進行處理。這些 rule 就是指通過編譯其源文件來更新每個.o
文件。如果源文件或任何聲明為 prerequisites 的頭文件比目標文件(object file)更新,或者目標文件不存在,則必須重新編譯。
make
去處理(使用諸如 make clean
之類的命令)。 在重新編譯目標文件之前,make
會更新它的 prerequisites ——源文件和頭文件。在這個 makefile 中,將不會對他們執行任何操作——.c
和.h
文件不是任何 rule 的 target——因此make
不會對它們執行任何操作。但make
會更新那些自動生成的C程序,比如Bison或Yacc自己制定的 rule。
在重新編譯需要的目標文件之後,make
決定是否重新鏈接edit
。如果文件edit
不存在或者有目標文件比它更新,那麼必須要重新鏈接edit
。如果目標文件被重新編譯了,那麼它現在就比edit
更新了,因此edit
要被重新鏈接。
insert.c
並運行make
,make
將會編譯該文件來更新insert.o
,然後再鏈接edit
。如果我們改變了文件command.h
並運行make
,make
將會重新編譯目標文件kbd.o
,command.o
和files.o
,然後再鏈接文件edit
。
2.4 運用變數使 makefile 更簡潔
例子中,我們在edit
的 rule 部分列出了目標文件兩次:
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
這種重複是容易出錯的;如果一個新的目標文件被添加到系統中,我們可能只會將它添加到一個列表中而忘記將它添加到另一個列表中。我們可以通過使用變數(Variables)來消除風險並簡化 makefile 。Variables 允許定義一次文本字元串,並在後面的多個位置進行替換(請參閱[How to Use Variables]
)。
objects
,OBJECTS
,objs
,OBJS
,obj
或OBJ
的變數,它是所有目標文件名的列表。我們將在 makefile 中用這樣一行來定義這樣一個變數objects
: objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o
然後,在我們想要輸入目標文件名列表的地方,我們就可以通過輸入$(objects)
來替代變數的值(參見[How to Use Variables]
)。
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)
2.5 讓make
推斷 Recipes
沒有必要詳細說明用於編譯單個C源文件的 recipe ,因為make
可以推斷出它們:使用cc -c
命令從相應的.c
文件更新.o
文件時,具有隱式規則(implicit rule)。例如,將使用 recipe cc -c main.c -o main.o
來把main.c
編譯成main.o
。因此,我們可以從目標文件的 rule 中省略 recipe 。 參見[Using Implicit Rules]
。
.c
文件自動使用這種方式的時候,它會被自動添加到 prerequisites 的列表中。因此,只要省略掉 recipe 我們就可以從 prerequisites 中省略.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
」相關的複雜情況在其他地方有描述,參見[Phony Targets]
和[Errors in Recipes]
)
2.6 另一種風格的 makefile
當 makefile 的目標(objects
)都由隱式規則創建時,可以使用其他類型的 makefile 。在這種類型的 makefile 中,你可以按照它們的 prerequisites 而不是它們的 target 對條目進行分組。下面是一個例子:
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
是所有目標文件的 prerequisites ;command.h
和buffer.h
是那些列出了它們的目標文件的 prerequisites 。
objects
)的所有信息放在一個地方更清晰。
2.7 清除文件夾的 rule
編譯一個程序可能不是你所寫的 rule 的唯一操作。 makefile 通常會描述一些除了編譯程序之外的其他操作:例如,如何刪除目標文件和可執行文件,使文件夾變得clean
。
make
的 rule 來清空我們的例子編輯器: clean: rm edit $(objects)
在實踐中,我們可能希望以更複雜的方式編寫 rule 來處理意外情況。我們將會這樣做:
.PHONY : clean clean : -rm edit $(objects)
這樣可以避免make
在名為clean
的文件存在時產生錯誤,並在rm
產生錯誤時繼續執行。(參見[Phony Targets]
和[Errors in Recipes]
)
edit
的 rule ——重新編譯編輯器——仍然是 default goal 。
因為clean
不是edit
的 prerequisites ,所以如果我們只輸入make
命令而不加任何參數,那麼這個 rule 根本不會被執行。為了使這個 rule 被執行,我們需要輸入make clean
。詳見[How to Run make]
。
推薦閱讀:
※Makefile 與 IDE 相比效率高在哪?
※利用debug庫實現對lua的性能分析
※makefile中的通配符使用?
※make makefile cmake qmake都是什麼,有什麼區別?
※使用xmake優雅地描述工程