make 是一個命令工具,它用來解釋 Makefile 中的規則。Makefile 中可使用系統 shell 所提供的任何命令。但注意有些像 set,setenv 等是不行的。
Makefile 最大的優勢是簡單,只須要一句話的解釋就可讓一個以前不懂的人能夠用起來併發揮做用。但只有掌握了它的內涵才能真正駕輕就熟。linux
Makefile 開始實際上是爲了 C/C++的編譯而誕生的,因此它裏面的不少隱藏規則都是針對 C/C++的。在講 Makefile 以前有必要對 C/C++的編譯有一點了解
過程以下:shell
一個簡單的 Makefile 規則組成以下:bash
Targets...: Prerequisites... Command Command ...
或併發
Targets: Prerequisites;Command Command ...
下面會稱 Target 爲目標, Prerequisites 爲目標依賴, Command 爲規則的命令行
Command 必須以[Tab]開始, Command 能夠寫成多行,經過來繼行,但行尾的後不能有空格。
規則包含了文件之間的依賴關係和更新此規則 target 所須要的 Command
targets 可使用通配符, 若是格式是"A(M)"表示檔案文件(.a)中的成員「M」
在須要用$本義的時候,使用兩$$來表示函數
當規則的 target 是一個文件,它的任何一個依賴文件被修改後,在執行 make <target>時這個目標文件都會被從新編譯或從新鏈接。若是有必要此 target 的一個依賴文件也會被先從新編譯。工具
Makefile 中把那些沒胡任何依賴只有執行動做的目標稱爲「僞目標「(Phony targets)ui
.PHONY : clean clean : -rm edit $(objects
經過.PHONY 將 clean 聲明爲僞目標,避免當目錄下有名爲「clean」文件時,clean 沒法執行
這樣的目標不是爲了建立或更新程序,而是執行相應動做。spa
在使用 make 編譯.c 源文件時,編譯.c 源文件規則的命令能夠不用明確給出。這是由於 make 自己存在一個默認的規則,可以自動完成對.c 文件的編譯並生成對應的.o 文件。它執行命令「cc -c」來編譯.c 源文件。在 Makefile 中咱們只須要給出須要重建的目標文件名(一個.o 文件),make 會自動爲這個.o 文件尋找合適的依賴文件(對應的.c 文件。對應是指:文件名除後綴外,其他都相同的兩個文件),並且使用正確的命令來重建這個目標文件。對於上邊的例子,此默認規則就使用命令「cc -c main.c -o main.o」來建立文件「main.o」。對一個目標文件是「N.o」,倚賴文件是「N.c」的規則,徹底能夠省略其規則的命令行,而由 make 自身決定使用默認命令。此默認規則稱爲 make 的隱含規則。命令行
書寫規則建議的方式是:單目標,多依賴。就是說盡可能要作到一個規則中只存在一個目標文件,可有多個依賴文件。儘可能避免使用多目標,單依賴的方式。code
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
上面是很差的風格
GNUmakefile
makefile
Makefile
當前目錄下不存在以「GNUmakefile 」、「makefile 」、「Makefile 」命名的任何文件,
則自動生成 foo.o 。當執行「make foo.o 」時。咱們能夠看到其執行的命令爲:cc –c –o foo.o foo.c
以後,foo.o 將會被建立或者更新。
若是使用命令「文件目標的隱含規則中依賴文件不存在。
若是使用命令「make foo.o 」時,將回到到以下提示:
make: *** No rule to make target ‘foo.o’. Stop.
make: *** No targets specified and no makefile found. Stop.
include foo *.mk ${bar}
會被展開爲include foo a.mk b.mk c.mk bish bash
能夠在 make 命令行中用-I 指定包含文件搜索目錄,默認搜索
能夠在 include 前加上-來使 make 不會由於未找到包含文件而退出
若是定義了這個值,那麼 make 會先讀入這個變量指定的多個文件
有些狀況下,存在兩個比較相似的 makefile 文件。其中一個(makefile-A)須要使用另一個(makefile-B)中所定義的變量和規則。一般咱們會想到在「makefile-A」中使用指示符「include」包含「mkaefile-B」來達到目的。但使用這種方式,若是在兩個 makefile 文件中存在相同目標,而在不一樣的文件中其描述規則使用不一樣的命令。這樣,相同的目標文件就同時存在兩個不一樣的規則命令,這是 makefile 所不容許的。遇到這種狀況,使用指示符「include」顯然是行不通的。GNU make 提供另一種途徑來實現此目的。具體的作法以下:
在須要包含的 makefile 文件(makefile-A)中,定義一個稱之爲「全部匹配模式」(參考 10.5 模式規則 一節)的規則,它用來述那些在「makefile-A」中沒有給出明確建立規則的目標的重建規則。就是說,若是在當前 makefile 文件中不能找到重建一個目標的規則時,就使用「全部匹配模式」所在的規則來重建這個目標。
看一個例子,若是存在一個命名爲「Makefile」的 makefile 文件,其中描述目標「foo」的規則和其餘的一些規,咱們也能夠書寫一個內容以下命名爲「GNUmakefile」的文件。
foo: frobnicate > foo %: force @$(MAKE) -f Makefile $@ force: ;
執行命令「make foo」,make 將使用工做目錄下命名爲「GNUmakefile」的文件並執行目標「foo」所在的規則,建立目標「foo」的命令是:「frobnicate > foo」。若是執行另一個命令「make bar」,由於在「GUNmakefile」中沒有此目標的更新規則。make 將使用「全部匹配模式」規則,執行命令「$(MAKE) -f Makefile bar」。若是文件「Makefile」中存在此目標更新規則的定義,那麼這個規則會被執行。此過程一樣適用於其它 「GNUmakefile」中沒有給出的目標更新規則。此方式的靈活之處在於:若是在「Makefile」文件中存在一樣一一個目標「foo」的重建規則,因爲 make 執行時首先讀取文件「GUNmakefile」並在其中可以找到目標「foo」的重建規則,因此 make 就不會去執行這個「全部模式匹配規則」(上例中目標「%」所在的規則)。這樣就避免了使用指示符「include」包含一個 makefile 文件時所帶來的目標規則的重複定義問題。
此種方式,模式規則的模式只使用了單獨的「%」(咱們稱他爲「全部模式匹配規則」),它能夠匹配任何一個目標;它的依賴是「force」,保證了即便目標文件已經存在也會執行這個規則(文件已存在時,須要根據它的依賴文件的修改狀況決定是否須要重建這個目標文件);
「force」規則中使用空命令是爲了防止 make 程序試圖尋找一個規則去建立目標「force」時,又使用了模式規則「%: force」而陷入無限循環
讀取全部的 makefile 文件(包括「MAKIFILES」變量指定的、指示符「include」指定的、以及命令行選項「-f(--file)」指定的 makefile 文件),內建全部的變量、明確規則和隱含規則,並創建全部目標和依賴之間的依賴關係結構鏈表。
根據第一階段已經創建的依賴關係結構鏈表決定哪些目標須要更新,並使用對應的規則來重建這些目標。
在 make 執行的第一階段中若是變量和函數被展開,那麼稱此展開是「當即」的,此時全部的變量和函數被展開在須要構建的結構鏈表的對應規則中(此規則在創建鏈表是須要使用)。其餘的展開稱之爲「延後」的。這些變量和函數不會被「當即」展開,而是直到後續某些規則需要使用時或者在 make 處理的第二階段它們纔會被展開。
IMMEDIATE = DEFERRED IMMEDIATE ?= DEFERRED IMMEDIATE := IMMEDIATE IMMEDIATE += DEFERRED or IMMEDIATE define IMMEDIATE DEFERRED endef
全部使用到條件語句在產生分支的地方,make 程序會根據預設條件將正確地分支展開。就是說條件分支的展開是「當即」的。其中包括:「ifdef」、「ifeq」、「ifndef」和「ifneq」所肯定的全部分支命令。
全部的規則在 make 執行時,都按照以下的模式展開:
IMMEDIATE : IMMEDIATE ; DEFERRED DEFERRED
其中,規則中目標和依賴若是引用其餘的變量,則被當即展開。而規則的命令行中的變量引用會被延後展開。此模板適合全部的規則,包括明確規則、模式規則、後綴規則、靜態模式規則。
TARGETS : NORMAL-PREREQUISITES | ORDER-ONLY-PREREQUISITES
兩種類型:
當"order-only"依賴更新後,不須要更新目標
好比:
LIBS = libtest.a foo : foo.c | $(LIBS) $(CC) $(CFLAGS) $< -o $@ $(LIBS)
表示文件名時,可用的通配符有: 「*", "?", "[...]"
Makefile 中通配符能夠出如今如下兩種場合:
除上面兩種狀況以外的上下文中,不能直接使用通配符,須要經過函數"wildcard"來實現
示例一:
print: *.c lpr -p $? touch print
變量定義中的通配符不會被處理,好比: "objects = _.o", 它表示 objects 的值是字符串"_.o",而不是當前文件夾下的.o 文件列表。
示例以下:
objects = *.o foo : $(objects) cc -o foo $(CFLAGS) $(objects)
若是將工做目錄下全部的.o 文件刪除,從新執行 make 將會獲得一個相似於「沒有建立*.o 文件的規則」 的錯誤提示。
好的作法是:
objects = $(wildcard *.o) foo : $(objects) cc -o foo $(CFLAGS) $(objects)
在規則中, 通配符會被自動展開。但在變量定義和函數引用時, 通配符不會展開。這時候就須要用 wildcard
能夠用$(wildcard _.c)來獲取工做目錄下全部的.c 文件列表,能夠用$(patsubst %.c,%.o,$(wildcard _.c))來獲得對應.c 的目標文件
在一個較大的工程中,通常會將源代碼和二進制文件(.o 文件和可執行文件)安排在不一樣的目錄來進行區分管理。這種狀況下,咱們可使用 make 提供的目錄搜索依賴文件功能(在指定的若干個目錄下自動搜索依賴文件)。在 Makefile 中,使用依賴文件的目錄搜索功能。當工程的目錄結構發生變化後,就能夠作到不更改 Makefile 的規則,只更改依賴文件的搜索目錄。
這是一個 makefilej 裏的變量
VPATH = src:../headers
這是 make 的一個關鍵字。能夠爲不一樣類型文件指定不一樣搜索目錄,有三種形式
vpath %.h ../headers
當有衝突時,按順序來查找,好比
vpath %.c foo vpath % blish vpath %.c bar
表示對全部的.c 文件,make 依次查找目錄:「foo」、blish」、「bar」。
vpath %.c foo : bar vpath % blish
對於全部的.c 文件 make 將依次查找目錄:「foo」、「bar」、「blish」
舉例, 有一個目錄"armgen", 它下面有一個子目錄「src", 存在"sum.c"和」memcp.c"兩個源文件,在"armgen"下的 Makefile 內容以下:
LIBS = libtest.a VPATH = src libtest.a : sum.o memcp.o $(AR) $(ARFLAGS) $@ $^
固然咱們有一個變量 GPATH,能夠指定目標文件的目錄
當咱們經過目錄搜索獲得依賴文件會在其餘目錄,可是若是命令行中沒有路徑的話,就會出錯。因此必須使用自動變量
foo.o : foo.c cc -c $(CFLAGS) $^ -o $@
規則命令行中的自動化變量「$^」表明全部經過目錄搜索獲得的依賴文件的完整路徑名(目錄 + 通常文件名)列表。「$@」表明規則的目標。
VPATH = src:../headers foo.o : foo.c defs.h hack.h cc -c $(CFLAGS) $< -o $@
自動化變量「$<」表明規則中經過目錄搜索獲得的依賴文件列表的第一個依賴文件
makefile 中的程序連接的靜態庫和共享庫也能夠經過搜索目錄獲得。這一特性須要咱們在書寫規則依賴時用"-I<name>"來指定一個依賴文件名
foo : foo.c -lcurses cc $^ -o $@
上面的命令只是定義在 foo.c 和/usr/lib/libcurses.a 或.so 被更新時要重建 foo, 但不會自動重建 libcurses.a,由於 make 不知道它的依賴
若是找不到 libcurses.a 或.so 會,報出相似沒有規則能夠建立目標「foo」須要的目標「-lcurses 的錯誤
.so 或.a 能夠同變量".LIBPATTERNS"來指定,它是一個多個包含模式字符%的字,多個值之間用空格分隔。它的默認值是"lib%.so lib%.a"
有些目標並不會建立目標,只是執行命令,因此咱們定義了僞目標,如常見的 clean
clean: rm *.o temp .PHONY: clean
若是沒有定義僞目標,那麼當存在文件"clean"時, "rm *.o temp"就不會被執行
另外一種使用場合是在 make 的並行和遞歸執行中。
好比
SUBDIRS = foo bar baz subdirs: for dir in $(SUBDIRS); do \ $(MAKE) -C $$dir; \ done
但上面的方法有 2 個問題
SUBDIRS = foo bar baz .PHONY: subdirs $(SUBDIRS) subdirs: $(SUBDIRS) $(SUBDIRS): $(MAKE) -C $@ foo: baz
上邊的實現中有一個沒有命令行的規則「foo: baz」,此規則用來限制子目錄的 make 順序。它的做用是限制同步目錄「foo」和「baz」的 make 過程(在處理「foo」目錄以前,須要等待「baz」目錄處理完成)。在書寫一個並行執行 make 的 Makefile 時,目錄的處理順序是須要特別注意的。
另外,make 存在一個內嵌隱含變量「RM」,它被定義爲:「RM = rm –f」。所以在書寫「clean」規則的命令行時可使用變量「$(RM)」來代替「rm」,這樣能夠免出現一些沒必要要的麻煩!這是推薦的用法
makefile 裏變量的展開是循環進行的
t2 := t3 t1 := t2 t0 := t1 define bar irun $($(t$(1))) endef all: $(call bar,0) $($($(t0)))
輸出結果是
irun t2 t3