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