makefile的规则
target ... : prerequisites ...
command
...
...
target被称为目标文件,prerequisites 表示需要生成target的文件列表。command也就是make要执行的命令。即,target中包含的一个或多个目标文件依赖于prerequisites中的文件,其规则定义在comman中。
在默认的情况下,输入make命令,那么make将会在当前目录下寻找名称叫做"Makefile"或"makefile"的文件。如果找到,他会找文件中存在的第一个目标文件(target),并把这个文件作为最终的文件。如果目标文件文件不存在或者是依赖文件的修改时间比.o文件的生成时间新,那么它就会执行后面所定义的命令。如果依赖文件也不存在则将会在当前文件中找目标为.o的依赖,如果找到该规则,再根据那一个规则生成.o文件,然后再执行当前的规则。
编译一个文件
创建一个文件test.c,并在该文件中输入代码段,用于测试
#include <stdio.h>
int main(void){
printf("hello word\n");
return 0;
}
在test.c相同目录下新建Makefile文件,输入一个Makefile规则用于编译test.c文件。
test:test.c
gcc -o test test.c
其中第一行test表示目标体,即当前规则最后要生成的文件,test.c表示目标体的依赖文件,即test文件需要使用test.c生成。第二行的起始是一个tab键(tab键必须要有,且只能有一个),gcc -o test test.c就是编译生成test的命令。
然后输入make,终端将会编译test.c并生成test文件。执行test文件将输出"hello word".
编译当前目录下所有文件
新建func1.c,func2.c文件,分别在两个文件中写入两个函数的测试代码:
func1:
#include <stdio.h>
void func1(void){
printf("this is func1\n");
}
func2:
#include <stdio.h>
void func2(void){
printf("this is func2\n");
}
修改test.c
#include <stdio.h>
extern void func1(void);
extern void func2(void);
int main(void){
printf("hello word\n");
func1();
func2();
return 0;
}
要编译这个程序可以将Makefile修改为:
test:test.o func1.o func2.o
gcc -o test test.o func1.o func2.o
test.o:test.c
gcc -o test.o -c test.c
func1.o:func1.c
gcc -o func1.o -c func1.c
func2.o:func2.c
gcc -o func2.o -c func2.c
但以上方式需要为每个文件添加规则,如果文件数量非常庞大显然是不可能的。比较简便的方法是:
test:test.o func1.o func2.o
gcc -o test test.o func1.o func2.o
%.o:%.c
gcc -c -o $@ $^
第三行和第四行完成对每个源文件的编译,其效果和上面第三行到第八行一致,这里引入的makefile模板的概念:
%号表示任意一个文件名,%.o :%.c表示任意.o文件都依赖于和其同名的.c文件生成。
$@ 表示当前规则的目标体,这里的目标体用%.o表示,
$^ 表示当前规则中的依赖文件列表,这里的依赖文件列表用%.c表示
但此时,如果新增一个文件同样需要修改Makefile,不用修改makefile的方法如下:
SRC = ${wildcard *.c}
OBJS = ${patsubst %.c,%.o,$(SRC)}
test:$(OBJS)
gcc -o test $(OBJS)
%.o:%.c
gcc -c -o $@ $^
makefile下所有变量均为字符串形式,不区分数据类型,即声明一个变量,直接输入变量名称即可。这里声明了两个变量SRC和OBJS,其中第一行调用了makefile中的扩展通配符wildcard,其含义是获取当前目录下所有的.c文件的文件名并赋值给变量SRC。第二行调用了替换通配符patsubst,含义是将SRC中的.c文件名替换为.o文件名,并将结果赋值给OBJS。这样如果在当前目录下新增一个文件,不用手动修改makefile了。