Makefile是一个很常见的文件,他定义了软件的编译规则,是软件编译不再痛苦,而是make一下就解决编译的问题,当然,在windows下,IDE帮你做了这些事情,是你只要按一个按钮就可以完成软件的全部编译,但并不能了解,他是如何做到的。
Makefile是make命令执行时所需要的文件,当然,他的名字比一定非要叫Makefile,叫啥都行,只是Makefile比较方便,在执行make的时候不要再用-f参数指定Makefile,当然,makefile也是可以的。
首先看一下make的规则:
1,若该工程没有被编译,则作有的文件都将会被编译并连接;
2,如该.c被文件修改,则只编译被修改的文件,并链接;
3,若.h文件被修改,则只编译包含该头文件的.c文件,并连接;
Makefile的格式:
target ... : prerequisites...
command
command
...
target是目标文件,他可以是.o文件,也可以是最终的可执行文件,也可以是Lable;command前面是一个Tab,也就是\t,makefile中命令都是以Tab开头的,当然,写在同一行的命令除外;上面的格式我们这也这样理解,要生成target,就要用prerequites;而生成的方法是command。就是这么简单。
我们举个例子,有如下文件:
/* test.c */
#include "myheader.h"
int main(void)
{
p1();
p2();
return ;
}
/* myheader.h */
#ifndef _MYHEADER_H_
#define _MYHEADER_H_
#include <stdio.h>
void p1 (void);
void p2 (void);
#endif
/* p1.c */
#include "myheader.h"
void p1(void)
{
printf("123456789\n");
}
/* p2.c */
#include "myheader.h"
void p2(void)
{
printf("987654321\n");
}
则makefile可以写成:
test : test.o p1.o p2.o
gcc -o test test.o p1.o p2.o
test.o : test.c myheader.h
gcc -c test.c
p1.o : p1.c myheader.h
gcc -c p1.c
p2.o : p2.c myheader.h
gcc -c p2.c
clean :
-rm test test.o p1.o p2.o
然后执行make就可以生成tesrt可执行文件。当然,这时候的文件数目很少要是很多,光写makefile就不知道要到什么时候,所以,我们要用使用变量的概念,makefile中的变量有点类似于C中的宏。那上面的例子来说:
obj = test.o p1.o p2.o
这样,当要使用test.o p1.o p2.o时,就可以使用obj这个变量,引用该变量的方法是$(obj),例如:
test : $(obj)
gcc -o test $(obj)
出了自己定义的变量之外还有自动变量:
$@ #表示依赖文件中的目标文件集合
$< #表示所有依赖目标中的第一个文件
$^ #表示所有的依赖文件(去除重复)
$? #表示所有比目标新的依赖文件
$+ #表示所有依赖文件,不去除重复
所以上面有可以写成:
test : $(obj)
gcc -o $@ $^
make还有自动推导的功能,例如:根据name.o可以推出的依赖文件是name.c,而cc -c name.c 也就随之被推出来了,
所以,
test.o : test.c myheader.h
gcc -c test.c
可以简写成:
test.o :myheader.h
最后的clean是伪目标,他永远都不会被执行,因为,make将makefile中的第一个目标作为最终的目标,而他的依赖文件中并没有clean,所以他不会被执行,让他执行的办法是在make 中明确的指出,也就是make clean。更为稳健的做法是用.PHONY声明clean
.PHONY : clean
clean:
-rm test $(obj) #前面的-号表示忽略错误,而#在makefile中是注释符,他不会被执行,#属于行注释
当然,利用伪目标我们可以一次生成多个可执行文件,例如:
target : p1 p2
.PHONY : target
p1 : p1.o x.o
command
p2 : p2.o x.o
command
因为声明了target是伪目标,所以并不会建立target文件,而p1 p2会被建立。
上例的makefile仔细观察就会发现,所有的.o文件的依赖文件就是.c文件和myheader.h文件,所以我们还可以写成:
%.o : %.c myheader.h
然后在根据自动推导,就行成了:
%.o : myheader.h
经过修改的makefile就成了下面这个样子:
obj = test.o p1.o p2.o
test : $(obj)
gcc -o $@ $^
%.o : myheader.h
.PHONY : clean
clean:
-rm test $(obj)
在makefile中还可以引用其他的makefile,
include filename
这就可以将filename所表示的makefile文件包含到该include的位置中来,这很有利于大型软件,因为大型软件不可能用一个makefile来控制该程序的编译于连接工作,这样,makefile很难维护,所以,通常是将起放在不同的位置,然后用包含的方式来统一的使用。
makefile中还可以使用数量有限的函数来丰富makefile的功能:
函数调用语法:
$(<function> <arguments>)
${<function> <arguments>}
字符串处理
字符串替换 $(subst <from>,<to>,<text>)
模式字符串替换 $(patsubst <pattern>,<replacement>,<text>)
去空格 $(strip <string>)
查找字符串 $(findstring <find>,<in>)
过滤 $(filter <pattern...>,<text>)
反过滤 $(filter-out <pattern...>,<text>)
排序 $(sort <list>)
取单词 $(word <n>,<text>)
取单词串 $(wordlist <s>,<e>,<text>)
单词个数统计 $(words <text>)
首单词 $(firstword <text>)
文件名操作
取目录 $(dir <names...>)
取文件 $(notdir <names...>)
取后缀 $(suffix <names...>)
取前缀 $(basename <names...>)
加后缀 $(addsuffix <suffix>,<names...>)
加前缀 $(addprefix <prefix>,<names...>)
连接 $(join <list1>,<list2>)
foreach
循环 $(foreach <var>,<list>,<text>)
if
$(if <condition>,<then-part>)
$(if <condition>,<then-part>,<else-part>)
call
新建参数化函数 $(call <expression>,<parm1>,<parm2>,<parm3>...)
origin
变量类型 $(origin <variable>)
未定义 “undefined”
默认定义 “default”
环境变量 “environment”
定义在 Makefile中“file”
命令行定义 “command line”
override重新定义 “override”
自动化变量 “automatic”
控制make
产生一个致命错误 $(error <text ...>)
输出一段警告信息 $(warning <text ...>)
/******************Makefile的编写指导****************************/