1.初識Makefile
Makefile的作用
Makefile文件告訴Make指令怎樣編譯和連接成一個程序。
Makefile的構成
Makefile主要由多條規則構成,每條規則由三部分構成:目標(target)、依賴(prerequiries)和命令(command)。
Makefile的格式
目標(target): 依賴(prerequiries)...
命令(command)
_ 目標(target)通常是要產生的文件的名稱,目標的例子是可執行文件或OBJ文件。目標也可是一個執行的動作名稱,諸如‘clean’(僅僅表達動作的目標稱爲假想目標)。
_ 依賴是用來輸入從而產生目標的文件,一個目標經常有幾個依賴。
_ 命令是Make執行的動作,一個規則可以含有幾個命令,每個命令佔一行。
注意:每個命令行前面必須是一個Tab字符,即命令行第一個字符是Tab。這是不小心容易出錯的地方。
說明
1.默認情況下,make最先執行第一條。
2.使用make 目標名的方式,執行指定的規則。
2.Makefile多文件編譯
1.測試代碼
String.h
#ifndef _STRING_H_
#define _STRING_H_
#include <iostream>
#include <string.h>
using namespace std;
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) {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"
using namespace std;
int main() {
String s1;
String s2("hello");
String s3(s1); //拷貝構造函數
cout << s3 << endl;
s3 = s2; //拷貝賦值函數
cout << s3 << endl;
return 0;
}
Makefile
**//目標:依賴**
StringTest:String.o StringTest.o String.h
**//命令**
g++ -o StringTest StringTest.o String.o
String.o:String.cpp
g++ -c String.cpp
StringTest.o:StringTest.cpp
g++ -c StringTest.cpp
**//假想目標**
clean:
rm String.o StringTest.o StringTest
說明
make執行規則之前,檢查依賴是否存在或者是否最新的。如果不是則執行依賴對應的規則,創建或者更新依賴。
實際項目中的cpp文件是很多的,所以我們要在makefile中分開使得cpp文件先生成.o文件,然後再鏈接,這樣可以避免修改文件導致所有文件都要重新的編譯,浪費時間。
3.Makefile的簡化
每次增加新的文件,需要在makefile的很多地方增加依賴,容易導致遺漏。可以使用變量可以簡化,避免這種出錯的可能。
_ 變量定義:變量 = 字符串
_ 變量使用:$(變量名)
//定義了一個變量OBJ
OBJ = StringTest
//$(OBJ)此時相當於宏替換,替換成StringTest
$(OBJ):String.o StringTest.o String.h
說明:
在makefile文件中使用名爲objects, OBJECTS, objs, OBJS, obj, 或 OBJ的變量代表所有OBJ文件已是約定成俗。
簡化後的Makefile
OBJ = StringTest.o String.o
StringTest:$(OBJ)
g++ -o StringTest $(OBJ)
String.o:String.cpp
g++ -c String.cpp
StringTest.o:StringTest.cpp
g++ -c StringTest.cpp
clean:
rm $(OBJ) StringTest
4. 命令自動推導
makefile提供了自動推導的規則,可以通過頭文件.h來推導其對應的.cpp文件並且生成.o文件
文件名.o:頭文件
這種簡化規則稱爲隱含規則,非簡化規則成爲具體規則。
簡化後的Makefile
OBJS = StringTest.o String.o
StringTest:$(OBJS)
g++ -o StringTest $(OBJS)
//根據頭文件自動推倒
StringTest.o String.o:String.h
//按照依賴分組規則可以減少規則數量,規則按照目標分組更符合我們日常思維習慣。
clean :
rm StringTest $(OBJS)
5.假想目標
表達動作的目標稱爲假想目標。通常規則會生成或者更新與目標的同名文件,但是假想目標不生成文件,只是作爲幾個命令組成特殊規則的名稱。例如例子中的clean,只是執行清理動作。如果,makefile同級目錄存在與假想目標同名的文件(例如:clean),那麼會導致命令不會被執行。所以需要把目標顯示聲明爲假想目標。
.PHONY 目標
舉個栗子
OBJS = StringTest.o String.o
.PHONY: all clean
all:StringTest
StringTest:$(OBJS)
g++ -o StringTest $(OBJS)
StringTest.o String.o:String.h
clean :
rm StringTest $(OBJS)
NO. | 假想目標 | 功能 |
---|---|---|
1 | all | 這所有目標的目標,一般是編譯所有的目標。 |
2 | clean | 刪除所有被make創建的文件。 |
3 | install | 安裝已編譯好的程序,就是把目標執行文件拷貝到指定的目標中去。 |
3 | 列出改變過的源文件。 | |
3 | tar | 源程序打tar包備份 |
3 | dist | 創建一個壓縮文件,一般是把tar文件壓成Z文件。或是gz文件。 |
6.通配符與變量
6.1通配符
通配符主要用於匹配文件名,makefile中使用%作爲通配符。從匹配目標格式的目標名中依據通配符抽取部分字符串,再按照抽取字符串分配到每一個依賴格式中產生依賴名。例如,使用%.o:%.cpp
。
6.2自動變量
自動變量是在規則每次執行時都基於目標和依賴產生新值的變量。下面是常用的自動變量。
NO. | 自動變量 | 含義 |
---|---|---|
1 | $< | 表示第一個匹配的依賴 |
2 | $@ | 表示目標 |
3 | $^ | 所有依賴 |
6.3預定義變量
程序名變量
變量 | 程序 | 默認值 |
---|---|---|
CC | C語言編譯程序 | cc |
CXX | C++編譯程序 | g++ |
CPP | 帶有標準輸出的C語言預處理程序 | $(CC) -E |
程序運行參數的變量
變量 | 程序參數 |
---|---|
CFLAGS | 用於C編譯器的額外標誌 |
CXXFLAGS | 用於C++編譯器的額外標誌 |
簡化後的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)
匹配規則推倒
其他
註釋#
換行\
回顯命令@echo