前言
如果您有多個(gè) c、c++ 和其他語(yǔ)言的文件,并且想通過終端命令編譯它們,我們?cè)撊绾尉幾g他們呢?為了解決這類問題,Makefile就出現(xiàn)了。Makefile在編譯大型項(xiàng)目的過程中,可以一次性編寫大量的源文件以及需要鏈接器標(biāo)志。廢話少說咱們直接開始今天的正文!
什么是Makefile
Makefile是一種用于簡(jiǎn)化或組織編譯代碼的工具,是一組具有變量名稱和目標(biāo)的命令(類似于終端命令),用于創(chuàng)建和刪除目標(biāo)文件的工具。在單個(gè) make 文件中,我們可以創(chuàng)建多個(gè)目標(biāo)來編譯和刪除對(duì)象、二進(jìn)制文件。您可以使用Makefile多次編譯您的項(xiàng)目(程序)。
讓我們通過一個(gè)例子來理解:
假設(shè)我們有 3 個(gè)文件main.c(主源文件)、 misc.c(包含函數(shù)定義的源文件)、misc.h(包含函數(shù)聲明)。在這里,我們將聲明和定義一個(gè)名為myFunc()的函數(shù)來打印一些東西——這個(gè)函數(shù)將分別在misc.c和misc.h中定義和聲明。
misc.c
?
#include?#include?"misc.h" ? /*function?definition*/ void?myFunc(void) { ????printf("Body?of?myFunc?function. "); }
?
misc.h
?
#ifndef?MISC_H ????#define?MISC_H ????? ????/*function?declaration.*/ ????void?myFunc(void); ????? #endif
?
main.c
?
#include?#include?"misc.h" ? int?main() { ????printf("Hello,?World. "); ????myFunc(); ????fflush(stdout); ? ????return?0; }
?
上面這個(gè)場(chǎng)景是非常常見也是最簡(jiǎn)單的一個(gè)多文件系統(tǒng)了,我們想要編譯他,并將他們鏈接在一起該如何做呢?顯然僅僅使用gcc等這些簡(jiǎn)單的編譯器是不夠的,此時(shí)我們就需要用到Makefile了。
下面將內(nèi)容放在一個(gè)名為Makefile的文件中,注意Makefile文件的名字只能是這幾個(gè)字,而且區(qū)分大小寫。
Makefile
?
#make?file?-?this?is?a?comment?section ? all:????#target?name ????gcc?main.c?misc.c?-o?main
?
保存名為Makefile。
插入注釋,后跟#字符。
all是一個(gè)目標(biāo)名稱,在目標(biāo)名稱之后插入:。
gcc是編譯器名稱,main.c,misc.c源文件名,-o是鏈接器標(biāo)志,main是二進(jìn)制文件名。
“
注意: Makefile必須使用 TAB 而不是空格縮進(jìn),否則make會(huì)失敗。
”
我們寫好Makefile后怎么進(jìn)行編譯呢?下面是代碼的編譯過程:
沒有目標(biāo)名稱:
?
make
?
帶有目標(biāo)名稱:
?
make?all
?
輸出:
?
????sh-4.3$?make ????gcc?????main.c?misc.c?-o?main ????sh-4.3$?./main ????Hello,?World. ????Body?of?myFunc?function. ????sh-4.3$
?
此時(shí)我們就可以看到對(duì)應(yīng)文件夾里已經(jīng)生成了對(duì)應(yīng)的可執(zhí)行文件了!這就是Makefile的作用!
為什么會(huì)存在 Makefile?
Makefile 用于幫助決定大型程序的哪些部分需要重新編譯。在絕大多數(shù)情況下,編譯 C 或 C++ 文件。其他語(yǔ)言通常有自己的工具,其用途與 Make 相似。當(dāng)您需要一系列指令來運(yùn)行取決于哪些文件已更改時(shí),Make 也可以在編譯之外使用。本教程將重點(diǎn)介紹 C/C++ 編譯用例。
這是您可以使用 Make 構(gòu)建的示例依賴關(guān)系圖。如果任何文件的依賴項(xiàng)發(fā)生更改,則該文件將被重新編譯:
Makefile的語(yǔ)法
一個(gè) Makefile 由一組規(guī)則組成。規(guī)則通常如下所示:
?
targets:?prerequisites ?command ?command ?command
?
targets:是文件名,以空格分隔。通常,每條規(guī)則只有一個(gè)。
command:是通常用于制作目標(biāo)的一系列步驟。這些需要以制表符開頭,而不是空格。
prerequisites:先決條件也是文件名,以空格分隔。這些文件需要在運(yùn)行目標(biāo)命令之前存在。這些也稱為依賴項(xiàng)
Makefile的精髓
讓我們從一個(gè) hello world 示例開始:
?
hello: ?echo?"Hello,?World" ?echo?"This?line?will?always?print,?because?the?file?hello?does?not?exist."
?
然后我們將運(yùn)行make hello,只要hello文件不存在,命令就會(huì)運(yùn)行。如果hello存在,則不會(huì)運(yùn)行任何命令。
重要的是要意識(shí)到我說hello的是target和file,那是因?yàn)閮烧呤侵苯勇?lián)系在一起的。通常,當(dāng)運(yùn)行目標(biāo)時(shí)(也就是運(yùn)行目標(biāo)的命令時(shí)),這些命令將創(chuàng)建一個(gè)與目標(biāo)同名的文件。在這種情況下,hello 目標(biāo)不會(huì)創(chuàng)建hello 文件。
那么我們?cè)趺礃硬拍茏尦绦蛉恐匦律赡兀窟@就要用到清理目標(biāo)文件的語(yǔ)句了,下面我們一起看一下如何清理已生成的目標(biāo)文件。
清理生成的目標(biāo)文件
我們還可以使用 Makefile 中的變量來概括Makefile。在此示例中,我們使用變量和干凈的目標(biāo)名稱編寫 Makefile 以刪除所有對(duì)象(.o 擴(kuò)展文件)和二進(jìn)制文件(主文件)。
?
#make?file?-?this?is?a?comment?section ? CC=gcc??#compiler TARGET=main?#target?file?name ? all: ????$(CC)?main.c?misc.c?-o?$(TARGET) ? clean: ????rm?$(TARGET)
?
編譯:
?
make
?
此時(shí)我們想要的目標(biāo)文件以及.o文件已經(jīng)出現(xiàn)在對(duì)應(yīng)的文件夾中,那我們?nèi)绾蝿h除編譯出來的文件呢?是不是要使用rm語(yǔ)句一個(gè)一個(gè)的刪除呢?其實(shí)大可不必,而且在大的工程中你也不可能一個(gè)一個(gè)的刪除,所以這時(shí)候make clean就出現(xiàn)了,他能通過一條語(yǔ)句就刪除剛才編譯出來的所有文件,下面我們來看一下應(yīng)該如何操作!
直接在Makefile對(duì)應(yīng)的文件夾先輸入一下命令,就會(huì)發(fā)現(xiàn)剛才生成的文件已經(jīng)消失了。
?
make?clean
?
當(dāng)我們有多個(gè)文件時(shí),我們可以在 Makefile 中編寫命令來為每個(gè)源文件創(chuàng)建目標(biāo)文件。如果你這樣做 只有那些被修改的文件將被編譯。
如果我們想要全部重新編譯只需要先執(zhí)行make clean 語(yǔ)句在執(zhí)行make即可。
Makefile中如何使用變量
在上面的示例中,大多數(shù)目標(biāo)值和先決條件值都是硬編碼的,但在實(shí)際項(xiàng)目中,這些值被替換為變量和模式。
在 Makefile 中定義變量的最簡(jiǎn)單方法是使用=運(yùn)算符。例如,要將命令分配給gcc變量CC:
?
CC?=?gcc
?
這也稱為遞歸擴(kuò)展變量,它用于如下所示的規(guī)則中:
?
hello:?hello.c ????${CC}?hello.c?-o?hello
?
那么實(shí)際在終端中執(zhí)行的語(yǔ)句是下面的:
?
gcc?hello.c?-o?hello
?
兩者${CC}和$(CC)都是對(duì) gcc的有效引用。但是如果想將一個(gè)變量重新分配給它自己,它將導(dǎo)致一個(gè)無限循環(huán)。讓我們驗(yàn)證一下:
?
CC?=?gcc CC?=?${CC} all: ????@echo?${CC}
?
運(yùn)行make將導(dǎo)致下面的錯(cuò)誤:
?
$?make Makefile:8:?***?Recursive?variable?'CC'?references?itself?(eventually).??Stop.
?
為了避免這種情況,我們可以使用:=運(yùn)算符(這也稱為簡(jiǎn)單擴(kuò)展變量)。我們運(yùn)行下面的makefile應(yīng)就不會(huì)出現(xiàn)上面的問題了:
?
CC?:=?gcc CC?:=?${CC} all: ????@echo?${CC}
?
舉個(gè)例子
下面我們通過一個(gè)實(shí)際的例子來體會(huì)一下上面講的知識(shí)點(diǎn)。以下 makefile 使用了變量、模式和函數(shù)編譯所有 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}
?
#注釋整行。
Line.PHONY = all clean定義虛假目標(biāo)all和clean.
變量LINKERFLAG定義要在gcc中使用的標(biāo)志。
SRCS := $(wildcard *.c):$(wildcard pattern)是文件名的功能之一。在這種情況下,所有帶有.c擴(kuò)展名的文件都將存儲(chǔ)在一個(gè)變量SRCS中。
BINS := $(SRCS:%.c=%): 這稱為替代參考。在這種情況下,如果SRCS有值'foo.c bar.c',BINS就會(huì)有'foo bar'。
Line all: ${BINS}:虛假目標(biāo)all將值${BINS}作為單獨(dú)的目標(biāo)調(diào)用。
讓我們看一個(gè)例子來理解這個(gè)規(guī)則。假設(shè)foo是中的值之一${BINS}。然后%將匹配foo(%可以匹配任何目標(biāo)名稱)。以下是擴(kuò)展形式的規(guī)則:
?
foo:?foo.o ??@?echo?"Checking.." ??gcc?-lm?foo.o?-o?foo
?
如上所示,%替換為foo。%.o替換為foo.o。%.o被模式化以匹配先決條件,并將%匹配為目標(biāo)。
下面是對(duì)上述makefile的重寫,并將它被放置在具有單個(gè)文件的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
?
這樣我們就可以使用一條語(yǔ)句make 完成整個(gè)程序的編譯了,如果想刪除編譯中生成的文件,可以使用make clean
多目標(biāo)Makefile
制作多個(gè)目標(biāo)并且您希望所有目標(biāo)都運(yùn)行?做一個(gè)all目標(biāo)。make由于這是列出的第一條規(guī)則,如果在沒有指定目標(biāo)的情況下調(diào)用它,它將默認(rèn)運(yùn)行。
?
all:?one?two?three one: ?touch?one two: ?touch?two three: ?touch?three clean: ?rm?-f?one?two?three
?
自動(dòng)變量和通配符
* 通配符*和%在 Make 中都稱為通配符,但它們的含義完全不同。*在您的文件系統(tǒng)中搜索匹配的文件名。
?
#?Print?out?file?information?about?every?.c?file print:?$(wildcard?*.c) ?ls?-la??$?
?
*可以在目標(biāo)、先決條件或wildcard函數(shù)中使用。
*不能在變量定義中直接使用
當(dāng)*沒有匹配到文件時(shí),保持原樣(除非在wildcard函數(shù)中運(yùn)行)
% 通配符%確實(shí)很有用,但是由于可以使用的情況多種多樣,因此有些混亂。
在matching模式下使用時(shí),它匹配字符串中的一個(gè)或多個(gè)字符。
在replacing模式下使用時(shí),它采用匹配的詞干并替換字符串中的詞干。
%最常用于規(guī)則定義和某些特定功能中。
結(jié)語(yǔ)
最后讓我們通過一個(gè)非常多汁的 Make 示例來結(jié)束本文,它適用于中型項(xiàng)目。
這個(gè) makefile 的巧妙之處在于它會(huì)自動(dòng)為您確定依賴關(guān)系。您所要做的就是將您的 C/C++ 文件放入該src/文件夾中。
?
TARGET_EXEC?:=?final_program BUILD_DIR?:=?./build SRC_DIRS?:=?./src #?Find?all?the?C?and?C++?files?we?want?to?compile #?Note?the?single?quotes?around?the?*?expressions.?Make?will?incorrectly?expand?these?otherwise. SRCS?:=?$(shell?find?$(SRC_DIRS)?-name?'*.cpp'?-or?-name?'*.c'?-or?-name?'*.s') #?String?substitution?for?every?C/C++?file. #?As?an?example,?hello.cpp?turns?into?./build/hello.cpp.o OBJS?:=?$(SRCS:%=$(BUILD_DIR)/%.o) #?String?substitution?(suffix?version?without?%). #?As?an?example,?./build/hello.cpp.o?turns?into?./build/hello.cpp.d DEPS?:=?$(OBJS:.o=.d) #?Every?folder?in?./src?will?need?to?be?passed?to?GCC?so?that?it?can?find?header?files INC_DIRS?:=?$(shell?find?$(SRC_DIRS)?-type?d) #?Add?a?prefix?to?INC_DIRS.?So?moduleA?would?become?-ImoduleA.?GCC?understands?this?-I?flag INC_FLAGS?:=?$(addprefix?-I,$(INC_DIRS)) #?The?-MMD?and?-MP?flags?together?generate?Makefiles?for?us! #?These?files?will?have?.d?instead?of?.o?as?the?output. CPPFLAGS?:=?$(INC_FLAGS)?-MMD?-MP #?The?final?build?step. $(BUILD_DIR)/$(TARGET_EXEC):?$(OBJS) ?$(CXX)?$(OBJS)?-o?$@?$(LDFLAGS) #?Build?step?for?C?source $(BUILD_DIR)/%.c.o:?%.c ?mkdir?-p?$(dir?$@) ?$(CC)?$(CPPFLAGS)?$(CFLAGS)?-c?$-o?$@ #?Build?step?for?C++?source $(BUILD_DIR)/%.cpp.o:?%.cpp ?mkdir?-p?$(dir?$@) ?$(CXX)?$(CPPFLAGS)?$(CXXFLAGS)?-c?$-o?$@ .PHONY:?clean clean: ?rm?-r?$(BUILD_DIR) #?Include?the?.d?makefiles.?The?-?at?the?front?suppresses?the?errors?of?missing #?Makefiles.?Initially,?all?the?.d?files?will?be?missing,?and?we?don't?want?those #?errors?to?show?up. -include?$(DEPS)
審核編輯:湯梓紅
評(píng)論
查看更多