1. makefile文件的作用
在很多的生产环境中,一个项目的运行往往要有很多源程序文件生成最终的可执行文件,这个执行过程往往很复杂,所以很多工作者就会借助
make
命令来实现自动编译,而makefile文件就是告诉make命令如何去编译和链接一个程序。
2. makefile文件编写格式
按如下格式编写Makefile
目标文件(target): 依赖文件(prerequiries)...
执行命令(command)
注意:
- makefile文件是由一条一条的规则构成的,每条规则是由三部分构成的:目标文件、依赖文件、执行命令;
- 默认情况下,makefile文件只会执行第一条规则。很多读者可能会有疑惑,我们看多的makefile文件中往往不止一条规则,这是因为我们规则中的依赖关系决定的,依赖将文件中的所有规则串在了一起;
- makefile文件中的注释以
#
开头;- makefile文件中的命令前是一个[tab]键,这一点很重要,不要敲成空格哦;
C++代码举例编写makefile文件
我们有如下几个文件:
overload.cpp
#include "Test.h"
int Add(float a, long b){
return a+b;
}
int num;
class Simple{
public:
Simple(){}
Simple(const Simple& s){}
Simple& operator=(const Simple&){ return *this; }
~Simple(){}
};
void Func();
int main(){
int c = Add(10, 11);
}
Test.cpp
#include "Test.h"
int Add(int a, int b){
return a+b;
}
void FuncTest(){}
Test.h
#pragma once
int Add(int a, int b);
void FuncTest();
我们编写如下的makefile文件:
Test:overload.cpp Test.cpp
g++ overload.cpp Test.cpp -o Test
clean:
rm Test
在命令行执行make命令,进行编译,需要注意的是,当我们生成的可执行文件已经是目前代码所对应的最新版本的时候,make命令执行结束后,会提示该文件已经是最新文件。当我们的程序有所变化时,我们执行make命令将会正常进行编译和链接。这也是makefile文件的优点之一。
除此以外,我们编写的第二条规则可以使用make 目标文件
进行编译,针对我们上述文件,我们可以使用make clean
执行删除命令。
3. makefile编写习惯优化
以上我们已经编写好了makefile文件,我们引入了如下的一些优化,从上文的文件一步步进行优化并给出每个阶段的makefile文件内容:
使用中间文件
我们在项目中常常使用.o
文件作为目标文件编写makefile文件进行编写,这样我们如果程序中的某一个源程序发生了改变就只会重新编译该源程序文件,而不需要重新编译所有的原程序代码。
以上的makefile文件就可以写作如下:
Test:overload.o Test.o
g++ overload.o Test.o -o Test
overload.o:overload.cpp
g++ -c overload.cpp
Test.o:Test.cpp
g++ -c Test.cpp
clean:
rm Test *.o
注意:
*.o
代表任意以.o
结尾的文件
定义变量
如果我们的项目需要增加一个文件进行编译,那么我们同时还需要修改makefile文件,如果规则较少还好,当规则较多的时候,我们进行修改就很不友好,可能需要通篇进行修改,而且还容易导致遗漏,所以我们通过定义变量来避免出现这种错误。
变变量是定义一个字符串,在多处替代该字符串使用。变量定义格式如下:
变量 = 字符串
OBJS = overload.o Test.o
除此以外,系统还自己定义了一些变量供我们使用,这种变量成为预定义变量:
变量 | 用途 | 默认值 |
---|---|---|
CC | C语言编译程序 | cc |
CXX | C++编译程序 | g++ |
AR | C++打包程序 | ar |
CPP | 带有标准输出的C语言预处理程序 | $(CC) -E |
RM | 删除命令 | rm |
我们使用变量来进修改我们的makefile文件如下:
CXXFLAGS = -g
TARGET = Test
OBJECTS = overload.o Test.o
$(TARGET):$(OBJECTS)
$(CXX) $(CXXFLAGS) $(OBJECTS) -o $(TARGET)
overload.o:overload.cpp
$(CXX) $(CXXFLAGS) -c overload.cpp
Test.o:Test.cpp
$(CXX) $(CXXFLAGS) -c Test.cpp
clean:
$(RM) Test *.o
自动变量
自动变量是在规则每次执行时都基于目标和依赖产生新值的变量。我们可以直接使用自自动变量来进行推导和匹配,书写更加的方便,下面是常用的自动变量。
自动变量 | 含义 |
---|---|
$< | 表示第一个匹配的依赖 |
$@ | 表示目标 |
$^ | 所有依赖 |
$? | 所有依赖中更新的文件 |
$+ | 所有依赖文件不去重 |
$(@D) | 目标文件路径 |
$(@F) | 目标文件名称 |
我们使用自动变量修改makefile文件如下:
CXXFLAGS = -g
TARGET = Test
OBJECTS = overload.o Test.o
$(TARGET):$(OBJECTS)
$(CXX) $(CXXFLAGS) $^ -o $@
overload.o:overload.cpp
$(CXX) $(CXXFLAGS) -c $<
Test.o:Test.cpp
$(CXX) $(CXXFLAGS) -c $<
clean:
$(RM) Test *.o
通配符
如上我们编写的makefile文件中有重复代码,我们可以使用通配符来进行简化。通配符主要用于匹配文件名,makefile中使用%
作为通配符。从匹配目标格式的目标名中依据通配符抽取部分字符串,再按照抽取字符串分配到每一个依赖格式中产生依赖名。例如,使用%.o:%.cpp
优化makefile文件。
该规则推导如下:
- 匹配一个字符串为%,就找到了一个了%.o文件,该文件自动推导到该字符串结尾的.cpp文件,这时的依赖文件为.cpp结尾的文件,目标文件为.o结尾的文件。
我们使用通配符修改makefile文件如下:
CXXFLAGS = -g
TARGET = Test
OBJECTS = overload.o Test.o
$(TARGET):$(OBJECTS)
$(CXX) $(CXXFLAGS) $^ -o $@
$(OBJECTS):%.o:%.cpp
$(CXX) $(CXXFLAGS) -c $<
clean:
$(RM) Test *.o
假想目标
如我们编写的makefile文件中的目标文件为clean
,该目标其是不是一个文件,我们只是将其作为删除命令的规则名称。但是若我们该makefile的同级目录中有假想目标同名的文件,就会造成一些问题,所以我们需要将这种目标声明为假象目标,告诉编译器不需要找到该目标同名的文件。
声明规则:
.PHONY 目标
我们修改makefile文件如下:
CXXFLAGS = -g
TARGET = Test
OBJECTS = overload.o\
Test.o
.PHONY:clean all
all:$(OBJECTS)
$(TARGET):$(OBJECTS)
@echo 生成可执行文件
$(CXX) $(CXXFLAGS) $^ -o $@
$(OBJECTS):%.o:%.cpp
$(CXX) $(CXXFLAGS) -c $<
clean:
@echo 清空文件
$(RM) Test *.o
注意:
- \ :指的是换行
- @echo : 回显信息作为提示