关于自动化变量,在陈皓的文档里有下面的描述:
$@
表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。
$%
仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。
$<
依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
$?
所有比目标新的依赖目标的集合。以空格分隔。
$^
所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。
$+
这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标。
$*
这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b",并且目标的模式是"a.%.b",那么,"$*"的值就是"dir/a.foo"。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么"$*"也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么"$*"就是除了后缀的那一部分。例如:如果目标是"foo.c",因为".c"是make所能识别的后缀名,所以,"$*"的值就是"foo"。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用"$*",除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么"$*"就是空值。
假如我们有下面的程序:
/* test1.c */ #include <stdio.h> void test3_printf(void) { printf("I am test3 !\n"); } /* test2.c */ #include <stdio.h> void test2_printf(void) { printf("I am test2 !\n"); } /* test3.c */ #include <stdio.h> void test1_printf(void) { printf("I am test1 !\n"); } /* test3.h */ #ifndef _TEST_3_H #define _TEST_3_H void test3_printf(void); #endif /* test2.h */ #ifndef _TEST_2_H #define _TEST_2_H void test2_printf(void); #endif /* test1.h */ #ifndef _TEST_1_H #define _TEST_1_H void test1_printf(void); #endif /* main.c */ #include "test1.h" #include "test2.h" #include "test3.h" void main(void) { test1_printf(); test2_printf(); test3_printf(); }
如果不使用Makefile 文件,我们可以用下面的命令来执行:
[root@redhat Makefile]# ls main.c test1.c test1.h test2.c test2.h test3.c test3.h [root@redhat Makefile]# gcc -c test1.c [root@redhat Makefile]# gcc -c test2.c [root@redhat Makefile]# gcc -c test3.c [root@redhat Makefile]# gcc -c main.c [root@redhat Makefile]# ls main.c main.o test1.c test1.h test1.o test2.c test2.h test2.o test3.c test3.h test3.o [root@redhat Makefile]# gcc -o main main.o test1.o test2.o test3.o [root@redhat Makefile]# ls main main.c main.o test1.c test1.h test1.o test2.c test2.h test2.o test3.c test3.h test3.o [root@redhat Makefile]# ./main I am test1 ! I am test2 ! I am test3 !gcc 的编译参数 -c 表示:只进程编译,不进行连接。-o 用来指定输出文件。gcc -o main main.o test1.o test2.o test3.o 命令表示 连接main.o test1.o test2.o test3.o 生成指定的文件main 可执行文件
如果使用Makefile 文件来做,最简单的可以是如下:
main: main.o test1.o test2.o test3.o gcc -o main main.o test1.o test2.o test3.o main.o:main.c test1.h test2.h test3.h gcc -c main.c test1.o:test1.c test1.h gcc -c test1.c test2.o:test2.c test2.h gcc -c test2.c test3.o:test3.c test3.h gcc -c test3.c clean: rm -rf *.o mainMakefile的一般书写方式是:
目标:依赖文件
生成目标文件的规则
英文的表述是:
targets: prerequisites
command
在Makefile文件中需要注意一点的就是:第一条规则中的目标将被确立为最终的目标。这里的最终目标也就是生成可执行文件main 编译运行结果如下:
[root@redhat Makefile]# ls main.c Makefile test1.c test1.h test2.c test2.h test3.c test3.h [root@redhat Makefile]# make gcc -c main.c gcc -c test1.c gcc -c test2.c gcc -c test3.c gcc -o main main.o test1.o test2.o test3.o [root@redhat Makefile]# ls main main.c main.o Makefile test1.c test1.h test1.o test2.c test2.h test2.o test3.c test3.h test3.o [root@redhat Makefile]# ./main I am test1 ! I am test2 ! I am test3 !
进阶:
进一步简化Makefile文件,可以有如下的写法:
target = test $(target): main.o test1.o test2.o test3.o gcc -o $@ $^ main.o: main.c test1.h test2.h test3.h gcc -c $< test1.o: test1.c test1.h gcc -c $< test2.o: test2.c test2.h gcc -c $< test3.o: test3.c test3.h gcc -c $< clean: $(RM) -f *.o $(target)
上面的Makefile文件中同时使用了$@ $^ $< 三个自动化变量
================================================
Makeflie 调试
“-n”
“--just-print”
“--dry-run”
“--recon”
比如上面的Makefile,如果执行 make -n 它的输出如下:
licaibiao@ubuntu:~/Makefile_test$ licaibiao@ubuntu:~/Makefile_test$ ls main.c Makefile test1.c test1.h test2.c test2.h test3.c test3.h licaibiao@ubuntu:~/Makefile_test$ make -n gcc -c main.c gcc -c test1.c gcc -c test2.c gcc -c test3.c gcc -o test main.o test1.o test2.o test3.o licaibiao@ubuntu:~/Makefile_test$ ls main.c Makefile test1.c test1.h test2.c test2.h test3.c test3.h licaibiao@ubuntu:~/Makefile_test$ licaibiao@ubuntu:~/Makefile_test$ licaibiao@ubuntu:~/Makefile_test$
可以看到,make -n 指输出了make执行的命令,但是实际并没有进行编译工作。使用下面的参数,将输出更加多的Makefile信息
============================================
进阶版Makefile文件可以写成下面的这样:
target = test $(target): main.o test1.o test2.o test3.o gcc -o $@ $^ .c .o: gcc -c $< clean: $(RM) -f *.o $(target)这里需要注意的一点是:
.c .o:
gcc -c $<
这里表示的是,所有的.o文件都是依赖于相应的.c文件,这是Makefile的“ 后缀规则”,是一种比较古老的规则,可以使用“ 模式规则”来实现:
target = test $(target): main.o test1.o test2.o test3.o gcc -o $@ $^ %.c: %.o gcc -c $< clean: $(RM) -f *.o $(target)后缀规则和模式规则都属于Makefile的 隐含规则
后缀规则的Makefile执行结果如下:
licaibiao@ubuntu:~/Makefile_test$ ls main.c Makefile1 test1.h test2.h test3.h Makefile test1.c test2.c test3.c licaibiao@ubuntu:~/Makefile_test$ make cc -c -o main.o main.c cc -c -o test1.o test1.c cc -c -o test2.o test2.c cc -c -o test3.o test3.c gcc -o test main.o test1.o test2.o test3.o licaibiao@ubuntu:~/Makefile_test$ ls main.c Makefile test test1.h test2.c test2.o test3.h main.o Makefile1 test1.c test1.o test2.h test3.c test3.o licaibiao@ubuntu:~/Makefile_test$ ./test I am test1 ! I am test2 ! I am test3 ! licaibiao@ubuntu:~/Makefile_test$模式规则的Makefile执行结果如下:
licaibiao@ubuntu:~/Makefile_test$ licaibiao@ubuntu:~/Makefile_test$ ls main.c Makefile1 test1.h test2.h test3.h Makefile test1.c test2.c test3.c licaibiao@ubuntu:~/Makefile_test$ make gcc -c main.c gcc -c test1.c gcc -c test2.c gcc -c test3.c gcc -o test main.o test1.o test2.o test3.o licaibiao@ubuntu:~/Makefile_test$ ls main.c Makefile test test1.h test2.c test2.o test3.h main.o Makefile1 test1.c test1.o test2.h test3.c test3.o licaibiao@ubuntu:~/Makefile_test$ licaibiao@ubuntu:~/Makefile_test$
终极:
Makefile可以进一步简化,下面是简化后的版本:
EXE := test %.o: %.c $(CC) -c $< -o $@ SOURCE := $(wildcard *.c) OBJS := $(patsubst %.c,%.o,$(SOURCE)) $(EXE):$(OBJS) $(CC) -g -o ./$(EXE) $(OBJS) clean: $(RM) -f *.o $(EXE)第一行 := 表示前面的变量不能使用后面的变量,比如 EXE:= $(OBJS) 将会出错。
第二行 %.o: %.c 这个是模式规则
第三行 自动化变量$< 在模式规则中,表示依赖集合, $@表示目标集合
第四行 wildcard是扩展通配符,是Makefile的一个函数,这里是Makefile的函数调用。在这里是将当前目录下所有.c 结尾的文件展开
第五行 patsubst 是替换通配符,将SOURCE中以*c 结尾的名字替换为*.o 其实这里OBJ的值就是test1.o test2.o test3.o main.o 而SOURCE的值为:test1.c test2.c test3.c main.c
第六行 正常的Makefile书写,目标:依赖,规则,只是这里都是用变量代替了。实际执行的是:cc -g -o ./test test2.o test3.o main.o test1.o 其中-g参数表示有添加调试信息,也就是可以直接用GDB来调试生成的test 文件。
第七行 使用了伪目标clean,用来执行清除工作。
第八行 清除编译产生的中间文件。RM 的默认值是 rm -f 这里执行的是:rm -f *.o test
本文的测试代码 可以到这里下载:Makefile应用实例