2021-05-12 14:32:11
Makefile 及其工作原理
用這個方便的工具來更有效的執行和編譯你的程式。
當你需要在一些原始檔改變後執行或更新一個任務時,通常會用到 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。這些步驟基於預置條件來實現目標。目標、預置條件和步驟共同構成一個規則。
總結一下,一個典型的規則的語法為:
目標:預置條件
<TAB>步驟
作為範例,目標可以是一個基於預置條件(原始碼)的二進位制檔案。另一方面,預置條件也可以是依賴其他預置條件的目標。
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
前新增 @
。
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
關於 Makefile
的更多資訊,GNU Make 手冊提供了更完整的說明和範例。
via: https://opensource.com/article/18/8/what-how-makefile
作者:Sachin Patil 選題:lujun9972 譯者:Zafiry 校對:wxy
Linux公社的RSS地址:https://www.linuxidc.com/rssFeed.aspx
本文永久更新連結地址:https://www.linuxidc.com/Linux/2018-09/154071.htm
相關文章