浅显易懂 Makefile 入门 (01)— 什么是Makefile、为什么要用Makefile、Makefile规则、Makefile流程如何实现增量编译
1. 什么是 Makefile
Makefile 文件描述了 Linux 系統(tǒng)下 C/C++ 工程的編譯規(guī)則,它用來(lái)自動(dòng)化編譯 C/C++ 項(xiàng)目。一旦寫編寫好 Makefile 文件,只需要一個(gè) make 命令,整個(gè)工程就開始自動(dòng)編譯,不再需要手動(dòng)執(zhí)行 GCC 命令。一個(gè)中大型 C/C++ 工程的源文件有成百上千個(gè),它們按照功能、模塊、類型分別放在不同的目錄中,Makefile 文件定義了一系列規(guī)則,指明了源文件的編譯順序、依賴關(guān)系、是否需要重新編譯等。
Makefile 可以簡(jiǎn)單的認(rèn)為是一個(gè)工程文件的編譯規(guī)則,描述了整個(gè)工程的編譯和鏈接等規(guī)則。其中包含了那些文件需要編譯,那些文件不需要編譯,那些文件需要先編譯,那些文件需要后編譯,那些文件需要重建等等。編譯整個(gè)工程需要涉及到的,在 Makefile 中都可以進(jìn)行描述。換句話說(shuō),Makefile 可以使得我們的項(xiàng)目工程的編譯變得自動(dòng)化,不需要每次都手動(dòng)輸入一堆源文件和參數(shù)。
2. 為什么要使用 Makefile
以 Linux 下的 C 語(yǔ)言開發(fā)為例來(lái)具體說(shuō)明一下,多文件編譯生成一個(gè)文件,編譯的命令如下所示:
gcc -o outfile name1.c name2.c ...
outfile 要生成的可執(zhí)行程序的名字,nameN.c 是源文件的名字。這是我們?cè)?Linux 下使用 gcc 編譯器編譯 C 文件的例子。如果我們遇到的源文件的數(shù)量不是很多的話,可以選擇這樣的編譯方式。如果源文件非常的多的話,就會(huì)遇到下面的這些問(wèn)題。
2.1 編譯的時(shí)候需要鏈接庫(kù)的的問(wèn)題
下面列舉了一些需要我們手動(dòng)鏈接的標(biāo)準(zhǔn)庫(kù):
name1.c用到了數(shù)學(xué)計(jì)算庫(kù)math中的函數(shù),我們得手動(dòng)添加參數(shù)-lm;name4.c用到了小型數(shù)據(jù)庫(kù)SQLite中的函數(shù),我們得手動(dòng)添加參數(shù)-lsqlite3;name5.c使用到了線程,我們需要去手動(dòng)添加參數(shù)-lpthread;
因?yàn)橛泻芏嗟奈募?#xff0c;還要去鏈接很多的第三方庫(kù)。所以在編譯的時(shí)候命令會(huì)很長(zhǎng),并且在編譯的時(shí)候我們可能會(huì)涉及到文件鏈接的順序問(wèn)題,所以手動(dòng)編譯會(huì)很麻煩。
如果我們學(xué)會(huì)使用 Makefile 就不一樣了,它會(huì)徹底簡(jiǎn)化編譯的操作。把要鏈接的庫(kù)文件放在 Makefile 中,制定相應(yīng)的規(guī)則和對(duì)應(yīng)的鏈接順序。這樣只需要執(zhí)行 make 命令,工程就會(huì)自動(dòng)編譯,省略掉手動(dòng)編譯中的參數(shù)選項(xiàng)和命令,非常的方便。
2.2 編譯大的工程會(huì)花費(fèi)很長(zhǎng)的時(shí)間
Makefile 支持多線程并發(fā)操作,會(huì)極大的縮短我們的編譯時(shí)間,并且當(dāng)我們修改了源文件之后,編譯整個(gè)工程的時(shí)候,make 命令只會(huì)編譯我們修改過(guò)的文件,沒有修改的文件不用重新編譯,也極大的解決了我們耗費(fèi)時(shí)間的問(wèn)題。
并且文件中的 Makefile 只需要完成一次,一般我們只要不增加或者是刪除工程中的文件,Makefile 基本上不用去修改,編譯時(shí)只用一個(gè) make 命令。為我們提供了極大的便利,很大程度上提高編譯的效率。
3. Makefile 規(guī)則
它的規(guī)則主要是兩個(gè)部分組成,分別是依賴的關(guān)系和執(zhí)行的命令,其結(jié)構(gòu)如下所示:
targets : prerequisitescommand
或者
targets : prerequisites; commandcommand
相關(guān)說(shuō)明如下:
targets:規(guī)則的目標(biāo),是必須要有的,可以是Object File(一般稱它為中間文件),也可以是可執(zhí)行文件,還可以是一個(gè)標(biāo)簽;prerequisites:是我們的依賴文件,要生成targets需要的文件或者是目標(biāo)??梢允嵌鄠€(gè),用空格隔開,也可以是沒有;command:make需要執(zhí)行的命令(任意的shell命令)。可以有多條命令,每一條命令占一行;
如果 command 太長(zhǎng), 可以用 \ 作為換行符。
注意:我們的目標(biāo)和依賴文件之間要使用冒號(hào)分隔開,命令的開始一定要使用
Tab鍵,不能使用空格鍵。
簡(jiǎn)單的概括一下Makefile 中的內(nèi)容,它主要包含有五個(gè)部分,分別是:
3.1 顯式規(guī)則
顯式規(guī)則說(shuō)明了,如何生成一個(gè)或多的的目標(biāo)文件。這是由 Makefile 的書寫者明顯指出,要生成的文件,文件的依賴文件,生成的命令。
3.2 隱晦規(guī)則
由于我們的 make 命名有自動(dòng)推導(dǎo)的功能,所以隱晦的規(guī)則可以讓我們比較粗糙地簡(jiǎn)略地書寫 Makefile,這是由 make 命令所支持的。
3.3 變量的定義
在 Makefile 中我們要定義一系列的變量,變量一般都是字符串,這個(gè)有點(diǎn)像 C 語(yǔ)言中的宏,當(dāng) Makefile 被執(zhí)行時(shí),其中的變量都會(huì)被擴(kuò)展到相應(yīng)的引用位置上。
3.4 文件指示
其包括了三個(gè)部分,一個(gè)是在一個(gè) Makefile 中引用另一個(gè) Makefile,就像 C 語(yǔ)言中的 include 一樣;另一個(gè)是指根據(jù)某些情況指定 Makefile 中的有效部分,就像 C 語(yǔ)言中的預(yù)編譯 #if 一樣;還有就是定義一個(gè)多行的命令。
3.5 注釋
Makefile 中只有行注釋,和 UNIX 的 Shell 腳本一樣,其注釋是用 # 字符,如果你要在你的 Makefile 中使用 # 字符,可以用反斜框進(jìn)行轉(zhuǎn)義,如: \#。
3.6 規(guī)則中的通配符
*表示任意一個(gè)或多個(gè)字符?表示任意一個(gè)字符[...][abcd] 表示 a,b,c,d中任意一個(gè)字符, [^abcd]表示除 a,b,c,d 以外的字符, [0-9] 表示 0~9中任意一個(gè)數(shù)字~表示用戶的 home 目錄
4. Makefile 示例
main.cpp 代碼:
#include <iostream>int main()
{std::cout << "hello,world" << std::endl;return 0;
}
通過(guò)下面的例子來(lái)具體使用一下 Makefile 的規(guī)則,Makefile文件中添代碼如下:
main: main.cppg++ main.cpp -o main
其中 main 是的目標(biāo)文件,也是我們的最終生成的可執(zhí)行文件。依賴文件就是 main.cpp 源文件,重建目標(biāo)文件需要執(zhí)行的操作是 g++ main.cpp -o main。這就是 Makefile 的基本的語(yǔ)法規(guī)則的使用。
使用
Makefile的方式:首先需要編寫好Makefile文件,然后在 shell 中執(zhí)行make命令,程序就會(huì)自動(dòng)執(zhí)行,得到最終的目標(biāo)文件。
wohu@ubuntu:~/cpp/demo$ ls
main.cpp `Makefile`
wohu@ubuntu:~/cpp/demo$ make
g++ main.cpp -o main
wohu@ubuntu:~/cpp/demo$ ls
main main.cpp `Makefile`
wohu@ubuntu:~/cpp/demo$ ./main
hello,world
wohu@ubuntu:~/cpp/demo$
如果命令的開始使用的是空格鍵,那么會(huì)報(bào)錯(cuò)
Makefile:2: *** missing separator. Stop.
Makefile:2 表示第二行錯(cuò)誤,應(yīng)該以 Tab 開始。
5. Makefile 流程
當(dāng)我們?cè)趫?zhí)行 make 條命令的時(shí)候,make 就會(huì)去當(dāng)前文件下找要執(zhí)行的編譯規(guī)則,也就是 Makefile 文件。我們編寫 Makefile 的時(shí)可以使用的文件的名稱 GNUMakefile 、makefile 、Makefile ,make 執(zhí)行時(shí)回去尋找 Makefile 文件,找文件的順序也是這樣的。
推薦使用 Makefile(一般在工程中都這么寫,大寫的會(huì)比較的規(guī)范)。如果文件不存在,make 就會(huì)給我們報(bào)錯(cuò),提示:
make: *** No targets specified and no `Makefile` found. Stop.
在 Makefile 中添加下面的代碼:
main: main.o name.o greeting.og++ main.o name.o greeting.o -o main
main.o: main.cppg++ -c main.cpp -o main.o
name.o: name.cppg++ -c name.cpp -o name.o
greeting.o: greeting.cppg++ -c greeting.cpp -o greeting.o
在我們編譯項(xiàng)目文件的時(shí)候,默認(rèn)情況下,make 執(zhí)行的是 Makefile 中的第一規(guī)則(Makefile 中出現(xiàn)的第一個(gè)依賴關(guān)系),此規(guī)則的第一目標(biāo)稱之為“最終目標(biāo)”或者是“終極目標(biāo)”。
在 shell 命令行執(zhí)行的 make 命令,就可以得到可執(zhí)行文件 main 和中間文件 main.o、name.o 和 greeting.o,main 就是我們要生成的最終文件。
通過(guò) Makefile 我們可以發(fā)現(xiàn),目標(biāo) main 在 Makefile 中是第一個(gè)目標(biāo),因此它就是 make 的終極目標(biāo),當(dāng)修改過(guò)任何文件后,執(zhí)行 make 將會(huì)重建終極目標(biāo) main。
它的具體工作順序是:當(dāng)在 shell 提示符下輸入 make 命令以后。 make 讀取當(dāng)前目錄下的 Makefile 文件,并將 Makefile 文件中的第一個(gè)目標(biāo)作為其執(zhí)行的“終極目標(biāo)”,開始處理第一個(gè)規(guī)則(終極目標(biāo)所在的規(guī)則)。
在我們的例子中,第一個(gè)規(guī)則就是目標(biāo) main 所在的規(guī)則。規(guī)則描述了 main 的依賴關(guān)系,并定義了鏈接 .o 文件生成目標(biāo) main 的命令;make 在執(zhí)行這個(gè)規(guī)則所定義的命令之前,首先處理目標(biāo) main 的所有的依賴文件(例子中的那些 .o 文件)的更新規(guī)則(以這些 .o 文件為目標(biāo)的規(guī)則)。
對(duì)這些 .o 文件為目標(biāo)的規(guī)則處理有下列三種情況:
- 目標(biāo)
.o文件不存在,使用其描述規(guī)則創(chuàng)建它; - 目標(biāo)
.o文件存在,目標(biāo).o文件所依賴的 “.cpp” 源文件 “.h” 文件中的任何一個(gè)比目標(biāo).o文件“更新”(在上一次 make 之后被修改),則根據(jù)規(guī)則重新編譯生成它; - 目標(biāo)
.o文件存在,目標(biāo).o文件比它的任何一個(gè)依賴文件(".c" 源文件、".h" 文件)“更新”(它的依賴文件在上一次 make 之后沒有被修改),則什么也不做;
通過(guò)上面的更新規(guī)則我們可以了解到中間文件的作用,也就是編譯時(shí)生成的 .o 文件。作用是檢查某個(gè)源文件是不是進(jìn)行過(guò)修改,最終目標(biāo)文件是不是需要重建。
我們執(zhí)行 make 命令時(shí),只有修改過(guò)的源文件或者是不存在的目標(biāo)文件會(huì)進(jìn)行重建,而那些沒有改變的文件不用重新編譯,這樣在很大程度上節(jié)省時(shí)間,提高編程效率。
總結(jié)
以上是生活随笔為你收集整理的浅显易懂 Makefile 入门 (01)— 什么是Makefile、为什么要用Makefile、Makefile规则、Makefile流程如何实现增量编译的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: GCC 链接 xxx:No such f
- 下一篇: 2022-2028年中国钽酸锂单晶行业市