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