Makefile是什么?
*学makefile之前需要复习一下编译过程
在大型软件项目设计中,我们不再像编译简单的C++程序一样包含头文件和源文件就直接开始编译。而是,需要制定规则去限制哪些文件需要怎么编译和怎么去链接。make是一种编译工具,makefile文件一旦确定了之后使得我们的编译过程自动化,提高软件开发效率。
大多数IDE都有自己的make工具,比如:Visual C++的nmake,QT Creator的qmake以及非常流行的CMake编译工具。
makefile的基本要素
- 目标:要生成的目标文件(可执行文件或者库)
- 依赖:目标通过哪些文件去生成
- 命令:通过执行命令由依赖文件生成目标文件
makefile规则如下:
目标 依赖
TAB(*注意必须是tab键,不能是空格) 命令;
下面通过具体的例子来初识makefile:
当前目录下有main.cpp function1.cpp function2.cpp sum.cpp以及一个head.h
下面是各个源文件和头文件的内容:
//head.h
void function1();
void function2();
int sum(int n);
//function1.cpp
#include <iostream>
using namespace std;
void function1(){
cout<<"this is function1\n";
}
//function2.cpp
#include <iostream>
using namespace std;
void function2(){
cout<<"this is function2\n";
}
//sum.cpp
#include <iostream>
using namespace std;
int sum(int n){
int sum=0;
for(int i=0;i<n;i++){
sum+=i;
}
return sum;
}
//main.cpp
#include <iostream>
#include "head.h"
int main(void){
function1();
function2();
int n=0;
std::cin>>n
int result=sum(n);
std::cout<<result<<std::endl;
return 0;
}
最简单的makefile
vim makefile
makefile的内容:
main: main.cpp function1.cpp function2.cpp sum.cpp
g++ -o main main.cpp function1.cpp function2.cpp sum.cpp
这是一个最简单的makefile,包括了目标、依赖和命令三要素。
make
之后在命令行输入make命令就直接按照我们制定的makefile规则编译生成可执行文件
先再次分解一下g++ -o main main.cpp function.cpp function2.cpp sum.cpp
main: main.o function1.o function2.o sum.o
g++ -o main main.o function1.o function2.o sum.o
main.o: main.c
g++ -c main.c -I./
function1.o: function1.cpp
g++ -c function1.cpp
function2.o: function2.cpp
g++ -c function2.cpp
sum.o: sum.cpp
g++ -c sum.cpp
不要慌,只是把一步编译到位模式分解了(没懂看C++编译过程)。
进阶makefile:
在实际应用中我们像上一步一样写makefile还不如不写,当我们有数十个cpp和库依赖的时候makefile有什么作用呢?
makefile中的变量 :
- 普通变量
- 自带变量
- 自动变量
普通变量:
简单来说:直接用"name1=name2"去定义 ,用$(name1)去使用。再来看看之前的makefile文件:
main: main.cpp function1.cpp function2.cpp sum.cpp
g++ -o main main.cpp function1.cpp function2.cpp sum.cpp
通过普通变量运用:
target = main
objects = main.cpp function1.cpp function2.cpp sum.cpp
//以上都是变量部分,以下是我们对原来的makefile更改
$(target): $(objects)
g++ -c $(target) $(objects)
自带变量:
CC:编译器名称(g++\clang)
CPPFLAGS:预处理选项 -I
CFLAGS:编译器选项 -Wall -g(gdb调试) -c
LDFLAGS:链接器选项 -L -l
再改进一次makefile:
target = main
objects = main.cpp function1.cpp function2.cpp sum.cpp
//以上都是变量部分,以下是我们对原来的makefile更改
$(target): $(objects)
CC = g++
$(CC) -c $(target) $(objects)
自动变量:
$@:表示规则中的目标
$<:表示规则中的第一个条件
$^:表示规则中的所有条件组成一个列表,如果有重复项消除重复项
%:模式规则---在规则的目标定义中包含%,表示一个或多个。
先看一下之前分解的makefile:
main: main.o function1.o function2.o sum.o
g++ -o main main.o function1.o function2.o sum.o
main.o: main.c
g++ -c main.c -I./
function1.o: function1.cpp
g++ -c function1.cpp
function2.o: function2.cpp
g++ -c function2.cpp
sum.o: sum.cpp
g++ -c sum.cpp
再利用普通变量和自带变量改进:
target = main
objects = main.o function1.o function2.o sum.o
CC = g++
CPPFLAGS = I./
$(target): $(objects)
$(CC) -o $(target) $(objects)
main.o: main.cpp
$(CC) -c main.cpp $(CPPFLAGS)
function1.o: function1.cpp
$(CC) -c function1.cpp
function2.o: function2.cpp
$(CC) -c function2.cpp
sum.o: sum.cpp
$(CC) -c sum.cpp
再通过自动变量去改进:
target = main
objects = main.o function1.o function2.o sum.o
CC = g++
CPPFLAGS = I./
$(target): $(objects)
$(CC) -o $@ $^
%.o: %.cpp
$(CC) -c $< $(CPPFLAGS)
//main.o: main.cpp
// $(CC) -c $< $(CPPFLAGS)
//function1.o: function1.cpp
// $(CC) -c $<
//function2.o: function2.cpp
// $(CC) -c $<
//sum.o: sum.cpp
// $(CC) -c $<
//实际上makefile中的注释用#,不是//
最终:
target = main
objects = main.o function1.o function2.o sum.o
CC = g++
CPPFLAGS = I./
$(target): $(objects)
$(CC) -o $@ $^
%.o: %.cpp
$(CC) -c $< $(CPPFLAGS)
makefile中的函数
函数太多,主要介绍两个函数:
- wildcard--查找指定目录下的指定类型文件
例如:src=$(wildcard*.cpp)-->查找当前目录下的所有后缀为cpp的文件赋值给变量src
- patsubt---匹配替换
例如:objects=$(patsubt%.cpp,%.o,$(src))-->将把src变量里的所有.cpp文件替换成.o
再次改进makefile:
target = main
src = $(wildcard *.cpp)
objects = $(patsubt %.cpp,%.o,$(src))
CC = g++
CPPFLAGS = I./
$(target): $(objects)
$(CC) -o $@ $^
%.o: %.cpp
$(CC) -c $< $(CPPFLAGS)