1. 为什么需要编写makefile
- 1.一个工程中的源文件不计其数,按类型、功能、模块分别放在若干目录中,若尝试一个个文件编译,未免效率太低,而编写一个良好的makefile则会提高我们编译的效率
- 2.在makefile中,我们可以定义一系列的规则,指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译。甚至于进行更复杂的功能操作,给我们在编译过程中带来极大的便利
- 3.makefile带来的好处即是实现了“
自动化编译
”,只需一个make操作就能自动将一个具有多目录的大项目编译完成,大大提高了开发的效率
2. "初级"makefile编写
一个makefile基本的规则:
A: B
(tab)<command>
(tab)<command>
A为目标文件,B为依赖文件(可能含有多个),接下来为命令(必须含有tab),操作依赖文件得到目标文件
以一个简单的示例来演示如何编写一个基础的makefile:
//file1.h
#ifndef FILE1_H
#define FILE1_H
#ifdef __cplusplus
extern "C" {
#endif
void File1Print();
#ifdef __cplusplus
}
#endif
#endif
//file1.cpp
#include <iostream>
#include "file1.h"
using namespace std;
void File1Print() {
cout << "Print file1**************************" << endl;
}
//file2.cpp
#include <iostream>
#include "file1.h"
using namespace std;
int main()
{
cout << "Print file2***********************" << endl;
File1Print();
return 0;
}
根据上述的规则,我们可以写出如下的makefile:
helloworld: file1.o file2.o
g++ file1.o file2.o -o helloworld
file2.o: file2.cpp
g++ -c file2.cpp -o file2.o
file1.o: file1.cpp file1.h
g++ -c file1.cpp -o file1.o
clean:
rm -rf *.o helloworld
在这里可以看出基本基于规则来写的,而且先执行file2.cpp,同时在这里加了一条clean语句,方便清除文件(*.o即匹配所有后缀为.o的文件并清除)
来简单测试一下,得到如下的结果:
可以看到make之后执行的是我们在makefile中编写的几条命令,接着我们编译得到的可执行文件helloworld,同时测试所编写的clean语句,得到如下结果:
到这里就可以简单编写一下makefile了,再上一层楼,简单使用变量!
3. “进阶”makefile
为什么需要在makefile中引入变量呢?有时出现在makefile中的参数重复出现或依赖项太长等原因,导致makefile编写效率低,这个时候引入变量就可以让我们的makefile看着更为简洁,编写也更为方便
要设定一个变量,只要在一行的前端写下这个变量的名字,后面跟一个“=”号,后面跟要设定的这个变量的值即可。以后要引用这个变量,只写一个“$”符号,后面是在括号里的变量名即可
那我们将上面的makefile简单修改一下,引入变量,如下:
OBJS = file1.o file2.o
XX = g++
CFLAGS = -Wall -O -g
helloworld : $(OBJS)
$(XX) $(OBJS) -o helloworld
file2.o:file2.cpp file1.h
$(XX) $(CFLAGS) -c file2.cpp -o file2.o
file1.o:file1.cpp file1.h
$(XX) $(CFLAGS) -c file1.cpp -o file1.o
clean:
rm -rf *.o helloworld
在这里可以发现,g++这一操作重复出现,可以以变量代替,在这里我们还添加了一个变量CFLAGS,这个变量被加入到g++操作中,可以简单分析一下这个变量
CFLAGS = -Wall -O -g:配置编译器设置,并把它赋值给CFLAGS变量,其中每个部分含义为:1. -Wall:输出所有的警告信息;2.-O:编译时进行优化;3.-g:表示编译debug版本
来make一下,得到以下结果(可以看到变量在运行时被替换):
添加变量也比较简单及容易理解,但我们很容易发现其缺点,即当我们添加一个文件到我们的项目中时,我们就得修改我们的makefile,这样也显得较为繁杂,所以尝试引入内嵌函数及内部变量来让我们编写的makefile有较好的兼容性
4. “高级”makefile
先将上述的makefile改写,然后再来详细解析:
CC = gcc
XX = g++
TARGET = helloworld
CFLAGS = -Wall -O -g
%.o : %.c
$(CC) $(CFLAGS) -c $< -o $@
%.o : %.cpp
$(XX) $(CFLAGS) -c $< -o $@
SOURCES = $(wildcard *.c *.cpp)
OBJS = $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES)))
$(TARGET) : $(OBJS)
$(XX) $(OBJS) -o $(TARGET)
clean:
rm -rf *.o helloworld
1.首先来看看其中的两个内嵌函数:
- $(wildccard PATTERN…),在makefile中,它被展开为已经存在的、使用空格分开的、匹配此模式的所有文件列表。如果不存在任何符合此模式的文件,函数会忽略模式字符并返回空;在本例中这个函数代表着匹配所有后缀为.c或.cpp的文件并存入SOURCES
- patsubst函数,用于匹配替换,有3个参数。第一个是一个需要匹配的字样,第二个表示用什么来替换它,第三个是一个变量需要被处理的由空格分隔的列表;
本例中第一个patsubst函数表示将所有.c后缀的文件替换为.o后缀的文件,而第三个参数又是一个函数,表示将所有.cpp后缀的文件替换为.o后缀的文件,这样子形成的一个列表作为第一个patsubst的参数,所以整句话表示了将SOURCES中所有.c或.cpp后缀的文件替换为.o文件
关于更多内嵌函数Makefile所有内嵌函数
2.接着来认识其中的几个内部变量:
- $@,表示目标文件
%.o : %.c
$(CC) $(CFLAGS) -c $< -o $@
在上述语句中表示以.o为后缀的文件
- $<,表示第一个依赖文件,即要生成目标文件需要多个依赖文件,则选取其中的第一个
//例
helloworld: file1.o file2.o
则表示file1.o
- $^,表示全部依赖文件,即上述示例中的file1.o file2.o
更多的了解内部变量关于内部变量
了解了这些,可以来编译此makefile:
到这里我们的简单makefile编写就算结束了,想要编写极为出色的makefile,还需潜心学习!!!
--------------------------------------------get------------------------------------------
简单makefile的编写