轉自:https://www.linuxidc.com/Linux/2018-09/154071.htmlinux
當你須要在一些源文件改變後運行或更新一個任務時,一般會用到 make
工具。make
工具須要讀取一個 Makefile
(或 makefile
)文件,在該文件中定義了一系列須要執行的任務。你可使用 make
來將源代碼編譯爲可執行程序。大部分開源項目會使用 make
來實現最終的二進制文件的編譯,而後使用 make install
命令來執行安裝。編程
本文將經過一些基礎和進階的示例來展現 make
和 Makefile
的使用方法。在開始前,請確保你的系統中安裝了 make
。編程語言
依然從打印 「Hello World」 開始。首先建立一個名字爲 myproject
的目錄,目錄下新建 Makefile
文件,文件內容爲:函數
say_hello:
echo"Hello World"
在 myproject
目錄下執行 make
,會有以下輸出:工具
$ make
echo"Hello World"
HelloWorld
在上面的例子中,「say_hello」 相似於其餘編程語言中的函數名。這被稱之爲目標target。在該目標以後的是預置條件或依賴。爲了簡單起見,咱們在這個示例中沒有定義預置條件。echo ‘Hello World'
命令被稱爲步驟recipe。這些步驟基於預置條件來實現目標。目標、預置條件和步驟共同構成一個規則。spa
總結一下,一個典型的規則的語法爲:code
目標:預置條件
<TAB>步驟
做爲示例,目標能夠是一個基於預置條件(源代碼)的二進制文件。另外一方面,預置條件也能夠是依賴其餘預置條件的目標。htm
final_target: sub_target final_target.c
Recipe_to_create_final_target
sub_target: sub_target.c
Recipe_to_create_sub_target
目標並不要求是一個文件,也能夠只是步驟的名字,就如咱們的例子中同樣。咱們稱之爲「僞目標」。遞歸
再回到上面的示例中,當 make
被執行時,整條指令 echo "Hello World"
都被顯示出來,以後纔是真正的執行結果。若是不但願指令自己被打印處理,須要在 echo
前添加 @
。
ip
say_hello:
@echo "Hello World"
從新運行 make
,將會只有以下輸出:
$ make
HelloWorld
接下來在 Makefile
中添加以下僞目標:generate
和 clean
:
say_hello:
@echo"Hello World"
generate:
@echo"Creating empty text files..."
touchfile-{1..10}.txt
clean:
@echo"Cleaning up..."
rm*.txt
隨後當咱們運行 make
時,只有 say_hello
這個目標被執行。這是由於Makefile
中的第一個目標爲默認目標。一般狀況下會調用默認目標,這就是你在大多數項目中看到 all
做爲第一個目標而出現。all
負責來調用它他的目標。咱們能夠經過 .DEFAULT_GOAL
這個特殊的僞目標來覆蓋掉默認的行爲。
在 Makefile
文件開頭增長 .DEFAULT_GOAL
:
.DEFAULT_GOAL := generate
make
會將 generate
做爲默認目標:
$ make
Creatingempty text files...
touchfile-{1..10}.txt
顧名思義,.DEFAULT_GOAL
僞目標僅能定義一個目標。這就是爲何不少 Makefile
會包括 all
這個目標,這樣能夠調用多個目標。
下面刪除掉 .DEFAULT_GOAL
,增長 all
目標:
all: say_hello generate
say_hello:
@echo"Hello World"
generate:
@echo"Creating empty text files..."
touchfile-{1..10}.txt
clean:
@echo"Cleaning up..."
rm*.txt
運行以前,咱們再增長一些特殊的僞目標。.PHONY
用來定義這些不是文件的目標。make
會默認調用這些僞目標下的步驟,而不去檢查文件名是否存在或最後修改日期。完整的 Makefile
以下:
.PHONY: all say_hello generate clean
all: say_hello generate
say_hello:
@echo"Hello World"
generate:
@echo"Creating empty text files..."
touchfile-{1..10}.txt
clean:
@echo"Cleaning up..."
rm*.txt
make
命令會調用 say_hello
和 generate
:
$ make
HelloWorld
Creatingempty text files...
touchfile-{1..10}.txt
clean
不該該被放入 all
中,或者被放入第一個目標中。clean
應當在須要清理時手動調用,調用方法爲 make clean
。
$ make clean
Cleaning up...
rm*.txt
如今你應該已經對 Makefile
有了基礎的瞭解,接下來咱們看一些進階的示例。
在以前的實例中,大部分目標和預置條件是已經固定了的,但在實際項目中,它們一般用變量和模式來代替。
定義變量最簡單的方式是使用 =
操做符。例如,將命令 gcc
賦值給變量 CC
:
CC =gcc
這被稱爲遞歸擴展變量,用於以下所示的規則中:
hello: hello.c
${CC} hello.c -o hello
你可能已經想到了,這些步驟將會在傳遞給終端時展開爲:
gcc hello.c -o hello
${CC}
和 $(CC)
都能對 gcc
進行引用。但若是一個變量嘗試將它自己賦值給本身,將會形成死循環。讓咱們驗證一下:
CC =gcc
CC = ${CC}
all:
@echo ${CC}
此時運行 make
會致使:
$ make
Makefile:8:***Recursive variable 'CC' references itself (eventually). Stop.
爲了不這種狀況發生,可使用 :=
操做符(這被稱爲簡單擴展變量)。如下代碼不會形成上述問題:
CC :=gcc
CC := ${CC}
all:
@echo ${CC}
下面的 Makefile
使用了變量、模式和函數來實現全部 C 代碼的編譯。咱們來逐行分析下:
#Usage:
#make # compile all binary
#make clean # remove ALL binaries and objects
.PHONY = all clean
CC =gcc # compiler to use
LINKERFLAG =-lm
SRCS := $(wildcard *.c)
BINS := $(SRCS:%.c=%)
all: ${BINS}
%:%.o
@echo"Checking.."
${CC} ${LINKERFLAG} $<-o $@
%.o:%.c
@echo"Creating object.."
${CC}-c $<
clean:
@echo"Cleaning up..."
rm-rvf *.o ${BINS}
#
開頭的行是評論。.PHONY = all clean
行定義了 all
和 clean
兩個僞目標。LINKERFLAG
定義了在步驟中 gcc
命令須要用到的參數。SRCS := $(wildcard *.c)
:$(wildcard pattern)
是與文件名相關的一個函數。在本示例中,全部 「.c」後綴的文件會被存入 SRCS
變量。BINS := $(SRCS:%.c=%)
:這被稱爲替代引用。本例中,若是 SRCS
的值爲 'foo.c bar.c'
,則 BINS
的值爲 'foo bar'
。all: ${BINS}
行:僞目標 all
調用 ${BINS}
變量中的全部值做爲子目標。規則:
%:%.o
@echo"Checking.."
${CC} ${LINKERFLAG} $<-o $@
下面經過一個示例來理解這條規則。假定 foo
是變量 ${BINS}
中的一個值。%
會匹配到 foo
(%
匹配任意一個目標)。下面是規則展開後的內容:
foo: foo.o
@echo"Checking.."
gcc-lm foo.o -o foo
如上所示,%
被 foo
替換掉了。$<
被 foo.o
替換掉。$<
用於匹配預置條件,$@
匹配目標。對 ${BINS}
中的每一個值,這條規則都會被調用一遍。
規則:
%.o:%.c
@echo"Creating object.."
${CC}-c $<
以前規則中的每一個預置條件在這條規則中都會都被做爲一個目標。下面是展開後的內容:
foo.o: foo.c
@echo"Creating object.."
gcc-c foo.c
最後,在 clean
目標中,全部的二進制文件和編譯文件將被刪除。
下面是重寫後的 Makefile
,該文件應該被放置在一個有 foo.c
文件的目錄下:
#Usage:
#make # compile all binary
#make clean # remove ALL binaries and objects
.PHONY = all clean
CC =gcc # compiler to use
LINKERFLAG =-lm
SRCS := foo.c
BINS := foo
all: foo
foo: foo.o
@echo"Checking.."
gcc-lm foo.o -o foo
foo.o: foo.c
@echo"Creating object.."
gcc-c foo.c
clean:
@echo"Cleaning up..."
rm-rvf foo.o foo