0. 作用
Makefile文件告诉Make怎样编译和连接成一个程序。
1. Makefile基本语法与执行
示例
编译一个单文件HelloWorld.cpp
- 编写
Makefile
HelloWorld : HelloWorld.cpp
g++ HelloWorld.cpp -o HelloWorld
clean :
rm HelloWorld
- 编译
make
- 清空
make clean
构成
Makefile主要由多条规则构成,每条规则由三部分构成:目标(target)、依赖(prerequiries)和命令(command)。
格式
按如下格式编写Makefile
目标(target): 依赖(prerequiries)...
命令(command)
- 目标(target)通常是要产生的文件的名称,目标的例子是可执行文件或OBJ文件。目标也可是一个执行的动作名称,诸如‘clean’(仅仅表达动作的目标称为假想目标)。
- 依赖是用来输入从而产生目标的文件,一个目标经常有几个依赖。
- 命令是Make执行的动作,一个规则可以含有几个命令,每个命令占一行。
注意:每个命令行前面必须是一个Tab字符,即命令行第一个字符是Tab。这是不小心容易出错的地方。
说明
- 默认情况下,
make
最先执行第一条。 - 使用
make 目标名
的方式,执行指定的规则。
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) {
//检测是否自我赋值
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); //拷贝构造函数
cout << s3 << endl;
s3 = s2; //拷贝赋值函数
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
执行规则之前,检查依赖是否存在或者是否最新的。如果不是则执行依赖对应的规则,创建或者更新依赖。
3. 使用变量简化makefile
每次增加新的文件,需要在makefile的很多地方增加依赖,容易导致遗漏。可以使用变量可以简化,避免这种出错的可能。
- 变量定义:
变量 = 字符串
- 变量使用:
$(变量名)
示例
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
文件已是约定成俗。
说明
- 变量是定义一个字符串,在多处替代该字符串使用。
4. 命令自动推导
编译.o
文件这类非常普遍并且常用,规则也比较简单
文件名.o:文件名.cpp 头文件
g++ -c 文件名.cpp
make提供一种简化写法,可以自动推导出该规则
文件名.o:头文件
这种简化规则称为隐含规则,非简化规则成为具体规则。
示例
makefile
OBJS = StringTest.o String.o
StringTest:$(OBJS)
g++ -o StringTest $(OBJS)
StringTest.o:String.h
String.o:String.h
clean :
rm StringTest $(OBJS)
- 小知识
通常,规则按照目标进行分组。规则也可以按照依赖分组。例如,例子中String.o
和StringTest.o
都依赖String.h
。那么,可以这两个合并到一个规则中。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 目标
示例
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)
- 常用假想目标
No. | 假想目标 | 功能 |
---|---|---|
1 | all |
这所有目标的目标,一般是编译所有的目标。 |
2 | clean |
删除所有被make创建的文件。 |
3 | install |
安装已编译好的程序,就是把目标执行文件拷贝到指定的目标中去。 |
4 | print |
列出改变过的源文件。 |
5 | tar |
源程序打tar包备份 |
6 | dist |
创建一个压缩文件,一般是把tar文件压成Z文件。或是gz文件。 |
6. 通配符与变量
编译.o
文件可以写成更通用的方式,使用我们之前已经定义好的变量$(OBJS)
,自动推导出需要生成的规则。
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中使用%
作为通配符。从匹配目标格式的目标名中依据通配符抽取部分字符串,再按照抽取字符串分配到每一个依赖格式中产生依赖名。例如,使用%.o:%.cpp
。
2. 自动变量
自动变量是在规则每次执行时都基于目标和依赖产生新值的变量。下面是常用的自动变量。
No. | 自动变量 | 含义 |
---|---|---|
1 | $< |
表示第一个匹配的依赖 |
2 | $@ |
表示目标 |
3 | $^ |
所有依赖 |
4 | $? |
所有依赖中更新的文件 |
5 | $+ |
所有依赖文件不去重 |
6 | $(@D) |
目标文件路径 |
7 | $(@F) |
目标文件名称 |
3. 预定义变量
预定义变量是makefile已经定义好的变量,用户可以在makefile文件中改变变量的值。
- 程序名变量
No. | 变量 | 程序 | 默认值 |
---|---|---|---|
1 | CC |
C语言编译程序 | cc |
2 | CXX |
C++编译程序 | g++ |
3 | AR |
C++打包程序 | ar |
4 | CPP |
带有标准输出的C语言预处理程序 | $(CC) -E |
5 | RM |
删除命令 | rm |
- 程序运行参数的变量
No. | 变量 | 程序参数 |
---|---|---|
1 | CFLAGS |
用于C编译器的额外标志 |
2 | CXXFLAGS |
用于C++编译器的额外标志 |
3 | ARFLAGS |
用于C/C++打包器的额外标志 |
4 | LDFLAGS |
链接库路径-L
|
5 | LDLIBS |
链接库-l
|
9. 其他
- 注释
#
- 换行
\
- 回显命令
@echo