0. 作用
Makefile文件告訴Make怎樣編譯和連接成一個(gè)程序。
1. Makefile基本語法與執(zhí)行
示例
編譯一個(gè)單文件HelloWorld.cpp
- 編寫
Makefile
HelloWorld : HelloWorld.cpp
g++ HelloWorld.cpp -o HelloWorld
clean :
rm HelloWorld
- 編譯
make
- 清空
make clean
構(gòu)成
Makefile主要由多條規(guī)則構(gòu)成,每條規(guī)則由三部分構(gòu)成:目標(biāo)(target)、依賴(prerequiries)和命令(command)。
格式
按如下格式編寫Makefile
目標(biāo)(target): 依賴(prerequiries)...
命令(command)
- 目標(biāo)(target)通常是要產(chǎn)生的文件的名稱,目標(biāo)的例子是可執(zhí)行文件或OBJ文件。目標(biāo)也可是一個(gè)執(zhí)行的動(dòng)作名稱,諸如‘clean’(僅僅表達(dá)動(dòng)作的目標(biāo)稱為假想目標(biāo))。
- 依賴是用來輸入從而產(chǎn)生目標(biāo)的文件,一個(gè)目標(biāo)經(jīng)常有幾個(gè)依賴。
- 命令是Make執(zhí)行的動(dòng)作,一個(gè)規(guī)則可以含有幾個(gè)命令,每個(gè)命令占一行。
注意:每個(gè)命令行前面必須是一個(gè)Tab字符,即命令行第一個(gè)字符是Tab。這是不小心容易出錯(cuò)的地方。
說明
- 默認(rèn)情況下,
make最先執(zhí)行第一條。 - 使用
make 目標(biāo)名的方式,執(zhí)行指定的規(guī)則。
2. Makefile多文件編譯
示例
String.h
#ifndef _STRING_H_
#define _STRING_H_
#include <iostream>
using namespace std;
#include <string.h>
class String{
public:
String(const char* cstr = NULL);
String(const String& str);
String& operator=(const String& str);
~String();
char* c_str() const {
return m_data;
}
private:
char* m_data;
};
ostream& operator<<(ostream& os, const String& str);
#endif // _STRING_H_
String.cpp
#include "String.h"
String::String(const char* cstr /*= NULL*/) {
if (cstr) {
m_data = new char[strlen(cstr) + 1];
strcpy(m_data, cstr);
}
else {
m_data = new char[1];
*m_data = '\0';
}
}
String::String(const String& str) {
m_data = new char[strlen(str.m_data) + 1];
strcpy(m_data, str.m_data);
}
String& String::operator=(const String& str) {
//檢測(cè)是否自我賦值
if (this == &str)
return *this;
delete [] m_data;
m_data = new char[strlen(str.m_data) + 1];
strcpy(m_data, str.m_data);
return *this;
}
String::~String() {
delete[] m_data;
}
ostream& operator<<(ostream& os, const String& str) {
os << str.c_str();
return os;
}
StringTest.cpp
#include "String.h"
int main() {
String s1;
String s2("hello");
String s3(s1); //拷貝構(gòu)造函數(shù)
cout << s3 << endl;
s3 = s2; //拷貝賦值函數(shù)
cout << s3 << endl;
return 0;
}
makefile
StringTest:StringTest.o String.o
g++ -o StringTest StringTest.o String.o
StringTest.o:StringTest.cpp String.h
g++ -c StringTest.cpp
String.o:String.cpp String.h
g++ -c String.cpp
clean :
rm StringTest StringTest.o String.o
說明
-
make執(zhí)行規(guī)則之前,檢查依賴是否存在或者是否最新的。如果不是則執(zhí)行依賴對(duì)應(yīng)的規(guī)則,創(chuàng)建或者更新依賴。
3. 使用變量簡(jiǎn)化makefile
每次增加新的文件,需要在makefile的很多地方增加依賴,容易導(dǎo)致遺漏??梢允褂米兞靠梢院?jiǎn)化,避免這種出錯(cuò)的可能。
- 變量定義:
變量 = 字符串 - 變量使用:
$(變量名)
示例
makefile
OBJS = StringTest.o String.o
StringTest:$(OBJS)
g++ -o StringTest $(OBJS)
StringTest.o:StringTest.cpp String.h
g++ -c StringTest.cpp
String.o:String.cpp String.h
g++ -c String.cpp
clean :
rm StringTest $(OBJS)
在makefile文件中使用名為
objects,OBJECTS,objs,OBJS,obj, 或OBJ的變量代表所有OBJ文件已是約定成俗。
說明
- 變量是定義一個(gè)字符串,在多處替代該字符串使用。
4. 命令自動(dòng)推導(dǎo)
編譯.o文件這類非常普遍并且常用,規(guī)則也比較簡(jiǎn)單
文件名.o:文件名.cpp 頭文件
g++ -c 文件名.cpp
make提供一種簡(jiǎn)化寫法,可以自動(dòng)推導(dǎo)出該規(guī)則
文件名.o:頭文件
這種簡(jiǎn)化規(guī)則稱為隱含規(guī)則,非簡(jiǎn)化規(guī)則成為具體規(guī)則。
示例
makefile
OBJS = StringTest.o String.o
StringTest:$(OBJS)
g++ -o StringTest $(OBJS)
StringTest.o:String.h
String.o:String.h
clean :
rm StringTest $(OBJS)
- 小知識(shí)
通常,規(guī)則按照目標(biāo)進(jìn)行分組。規(guī)則也可以按照依賴分組。例如,例子中String.o和StringTest.o都依賴String.h。那么,可以這兩個(gè)合并到一個(gè)規(guī)則中。OBJS = StringTest.o String.o StringTest:$(OBJS) g++ -o StringTest $(OBJS) StringTest.o String.o:String.h clean : rm StringTest $(OBJS)按照依賴分組規(guī)則可以減少規(guī)則數(shù)量,規(guī)則按照目標(biāo)分組更符合我們?nèi)粘K季S習(xí)慣。
5. 假想目標(biāo)
表達(dá)動(dòng)作的目標(biāo)稱為假想目標(biāo)。通常規(guī)則會(huì)生成或者更新與目標(biāo)的同名文件,但是假想目標(biāo)不生成文件,只是作為幾個(gè)命令組成特殊規(guī)則的名稱。例如例子中的clean,只是執(zhí)行清理動(dòng)作。如果,makefile同級(jí)目錄存在與假象目標(biāo)同名的文件(例如:clean),那么會(huì)導(dǎo)致命令不會(huì)被執(zhí)行。所以需要把目標(biāo)顯示聲明為假想目標(biāo)。
.PHONY 目標(biāo)
示例
makefile
OBJS = StringTest.o String.o
.PHONY: all clean
all:StringTest
StringTest:$(OBJS)
g++ -o StringTest $(OBJS)
StringTest.o:String.h
String.o:String.h
clean :
rm StringTest $(OBJS)
- 常用假想目標(biāo)
| No. | 假想目標(biāo) | 功能 |
|---|---|---|
| 1 | all |
這所有目標(biāo)的目標(biāo),一般是編譯所有的目標(biāo)。 |
| 2 | clean |
刪除所有被make創(chuàng)建的文件。 |
| 3 | install |
安裝已編譯好的程序,就是把目標(biāo)執(zhí)行文件拷貝到指定的目標(biāo)中去。 |
| 4 | print |
列出改變過的源文件。 |
| 5 | tar |
源程序打tar包備份 |
| 6 | dist |
創(chuàng)建一個(gè)壓縮文件,一般是把tar文件壓成Z文件?;蚴莋z文件。 |
6. 通配符與變量
編譯.o文件可以寫成更通用的方式,使用我們之前已經(jīng)定義好的變量$(OBJS),自動(dòng)推導(dǎo)出需要生成的規(guī)則。
makefile
OBJS = StringTest.o String.o
.PHONY: all clean
all:StringTest
StringTest:$(OBJS)
g++ -o StringTest $^
$(OBJS):%.o:%.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
.PHONY: clean
clean :
rm StringTest $(OBJS)
說明
1. 通配符
通配符主要用于匹配文件名,makefile中使用%作為通配符。從匹配目標(biāo)格式的目標(biāo)名中依據(jù)通配符抽取部分字符串,再按照抽取字符串分配到每一個(gè)依賴格式中產(chǎn)生依賴名。例如,使用%.o:%.cpp。
2. 自動(dòng)變量
自動(dòng)變量是在規(guī)則每次執(zhí)行時(shí)都基于目標(biāo)和依賴產(chǎn)生新值的變量。下面是常用的自動(dòng)變量。
| No. | 自動(dòng)變量 | 含義 |
|---|---|---|
| 1 | $< |
表示第一個(gè)匹配的依賴 |
| 2 | $@ |
表示目標(biāo) |
| 3 | $^ |
所有依賴 |
| 4 | $? |
所有依賴中更新的文件 |
| 5 | $+ |
所有依賴文件不去重 |
| 6 | $(@D) |
目標(biāo)文件路徑 |
| 7 | $(@F) |
目標(biāo)文件名稱 |

3. 預(yù)定義變量
預(yù)定義變量是makefile已經(jīng)定義好的變量,用戶可以在makefile文件中改變變量的值。
- 程序名變量
| No. | 變量 | 程序 | 默認(rèn)值 |
|---|---|---|---|
| 1 | CC |
C語言編譯程序 | cc |
| 2 | CXX |
C++編譯程序 | g++ |
| 3 | AR |
C++打包程序 | ar |
| 4 | CPP |
帶有標(biāo)準(zhǔn)輸出的C語言預(yù)處理程序 | $(CC) -E |
| 5 | RM |
刪除命令 | rm |
- 程序運(yùn)行參數(shù)的變量
| No. | 變量 | 程序參數(shù) |
|---|---|---|
| 1 | CFLAGS |
用于C編譯器的額外標(biāo)志 |
| 2 | CXXFLAGS |
用于C++編譯器的額外標(biāo)志 |
| 3 | ARFLAGS |
用于C/C++打包器的額外標(biāo)志 |
| 4 | LDFLAGS |
鏈接庫路徑-L
|
| 5 | LDLIBS |
鏈接庫-l
|
9. 其他
- 注釋
# - 換行
\ - 回顯命令
@echo
10. 總結(jié)
