一个工程中的源文件不计其数,其按照类型、功能、模块分别存放在若干个不同的目录中。Makefile
文件定义了一系列的 规则 来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至进行更为复杂的功能操作,因为 Makefile
就像一个脚本一样,也可以执行操作系统的命令。
Makefile
的优点就是 “自动化编译”,一旦写好,只需要一个 make
命令,整个工程完全自动编译,这样就极大的提高了软件开发的效率。
make
是一个命令工具,是一个解释 Makefile
文件中指令的命令工具。一般来说,大多数的 IDE 都有这个命令,比如 Delphi 的 make
,Visual C++ 的 nmake
,Linux 下的 GNU make
。
文件命名:
makefile
或者 Makefile
;Makefile
规则:
Makefile
文件中可以有 一个或者多个规则。目标... : 依赖...
命令(shell命令)
...
Tab
缩进);Makefile
中其他的规则都是为第一条规则服务的。
add.c
#include "head.h"
int add(int a,int b){
return a + b;
}
div.c
#include "head.h"
double div(int a,int b){
return a * 1.0 / b;
}
head.h
#include
int add(int a,int b);
int sub(int a,int b);
int mul(int a,int b);
double div(int a,int b);
main.c
#include
#include "head.h"
int main(){
int a = 10 , b = 20;
printf("a = %d , b = %d\n",a,b);
printf("a + b = %d\n",add(a,b));
printf("a - b = %d\n",sub(a,b));
printf("a * b = %d\n",mul(a,b));
printf("a / b = %.2lf\n",div(a,b));
printf("xixixiixixixi\n");
return 0;
}
mul.c
#include "head.h"
int mul(int a,int b){
return a * b;
}
sub.c
#include "head.h"
int sub(int a,int b){
return a - b;
}
makefile
app:add.c sub.c mul.c div.c main.c
gcc add.c sub.c mul.c div.c main.c -o app
然后执行 make
命令,再执行可执行文件。
命令在执行之前,需要先检查规则中的依赖是否存在。
检测更新,在执行规则中的命令时,会比较目标和依赖文件的时间。
Makefile
#app 依赖于 add.o sub.o mul.o div.o main.o 这些文件
#由于这些文件还不存在,所以向下检查其他规则
#比如首先检测到 add.o 发现它不存在,就向下找到生成 add.o 的规则 并执行命令
#其他的也是类似,直到得到所有的依赖文件 最终生成目标文件 app
app:add.o sub.o mul.o div.o main.o
gcc add.o sub.o mul.o div.o main.o -o app
add.o:add.c
gcc -c add.c -o add.o
sub.o:sub.c
gcc -c sub.c -o sub.o
mul.o:mul.c
gcc -c mul.c -o mul.o
div.o:div.c
gcc -c div.c -o div.o
main.o:main.c
gcc -c main.c -o main.o
因为这样写的话,如果我们修改了其中的部分文件,那么我们也只需要重新编译这些被修改过的文件,而不是所有文件。
比如在 main
函数最后,添加一句输出 hello makefile
的语句,再编译。
我们发现这次只重新编译了 main.c
文件,这样的形式就提高了编译的效率。之前的那种形式就算修改了一个文件,也会重新编译所有文件。
自定义变量
var=hello
;预定义变量
AR
:归档维护程序的名称,默认值为 ar
;CC
:C编译器的名称,默认值为 cc
;CXX
:C++编译器的名称,默认值为 g++
;$@
:目标的完整名称;$<
:第一个依赖文件的名称;$^
:所有的依赖文件;获取变量的值
$(变量名)
;将工作原理中的示例,全部用变量替换。
Makefile
src=add.o sub.o mul.o div.o main.o
target=app
$(target):$(src)
$(cc) $(src) -o $(target)
add.o:add.c
$(cc) -c add.c -o add.o
sub.o:sub.c
$(cc) -c sub.c -o sub.o
mul.o:mul.c
$(cc) -c mul.c -o mul.o
div.o:div.c
$(cc) -c div.c -o div.o
main.o:main.c
$(cc) -c main.c -o main.o
在 Makefile
中我们也可以使用 模式匹配。
%.o : %.c
%
:通配符,匹配一个字符串;%
匹配的是同一个字符串;例如:
%.o:%.c
gcc -c $< -o $@
把变量中的示例使用模式匹配。
src=add.o sub.o mul.o div.o main.o
target=app
$(target):$(src)
$(CC) $(src) -o $(target)
%.o:%.c
$(CC) -c $< -o $@
$(wildcard PATTERN...)
PATTERN
指的是 某一个或者多个目录下的对应的某种类型的文件,如果有多个目录,一般使用 空格间隔;例如:
$(wildcard *.c ./sub/*.c)
#返回 a.c b.c c.c d.c e.c ... 当前目录下 以及 当前目录的 sub 子目录下的 .c文件
$(patsubst
中的单词(单词以 空格,Tab
或者 回车 ,换行 分隔)是否符合模式
,如果匹配的话则以
替换;
可以包括通配符 %
,表示任意长度的字串。如果
中也包含 %
,那么这个 %
将是
中那个 %
所代表的子串;例如:
$(patsubst %.c , %.o , x.c bar.c)
#返回为 x.o bar.o
使用函数来实现。
src=$(wildcard *.c)
objs=$(patsubst %.c,%.o,$(src))
target=app
$(target):$(objs)
$(CC) $(objs) -o $(target)
%.o:%.c
$(CC) -c $< -o $@
我们可以添加 clean
,使用 make clean
删除目标文件。
clean:
rm $(objs) -f
需要注意的是,如果我们此时在 当前目录下创建一个 clean
文件,make clean
文件就会失效。
.PHONY:clean
clean:
rm $(objs) -f