Makfile 应用进阶实例
2017-11-26 22:40
344 查看
本文转载自http://blog.csdn.net/li_wen01/article/details/57947242
最近看了陈皓的《跟我一起写Makefile》,里面介绍了很多的Makefile的知识,但是实例较少,我这里举例几个自动化变量使用,以便了解Makefile的强大功能。
关于自动化变量,在陈皓的文档里有下面的描述:
$@
表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。
$%
仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"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所不能识别的,那么"$*"就是空值。
入门:
假如我们有下面的程序:
[python] view
plain copy
print?
/* 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 文件,我们可以用下面的命令来执行:
[python] view
plain copy
print?
[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 文件来做,最简单的可以是如下:
[python] view
plain copy
print?
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 main
Makefile的一般书写方式是:
目标:依赖文件
生成目标文件的规则
英文的表述是:
targets: prerequisites
command
在Makefile文件中需要注意一点的就是:第一条规则中的目标将被确立为最终的目标。这里的最终目标也就是生成可执行文件main 编译运行结果如下:
[python] view
plain copy
print?
[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文件,可以有如下的写法:
[python] view
plain copy
print?
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 调试
如果只想看Makefile的执行命令,可以使用Makefile的调试,只需要执行make的时候带上下面的参数之一就可以了。
“-n”
“--just-print”
“--dry-run”
“--recon”
比如上面的Makefile,如果执行 make -n 它的输出如下:
[python] view
plain copy
print?
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信息
“-p”
“--print-data-base”
输出makefile中的所有数据,包括所有的规则和变量。这个参数会让一个简单的makefile都会输出一堆信息。如果你只是想输出信息而不想执行makefile,你可以使用“make -qp”命令。如果你想查看执行makefile前的预设变量和规则,你可以使用“make –p –f /dev/null”。这个参数输出的信息会包含着你的makefile文件的文件名和行号,所以,用这个参数来调试你的makefile会是很有用的,特别是当你的环境变量很复杂的时候。
============================================
高级:
进阶版Makefile文件可以写成下面的这样:
[python] view
plain copy
print?
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的“后缀规则”,是一种比较古老的规则,可以使用“模式规则”来实现:
[python] view
plain copy
print?
target = test
$(target): main.o test1.o test2.o test3.o
gcc -o $@ $^
%.c: %.o
gcc -c $<
clean:
$(RM) -f *.o $(target)
后缀规则和模式规则都属于Makefile的隐含规则
后缀规则的Makefile执行结果如下:
[python] view
plain copy
print?
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执行结果如下:
[python] view
plain copy
print?
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可以进一步简化,下面是简化后的版本:
[python] view
plain copy
print?
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应用实例
最近看了陈皓的《跟我一起写Makefile》,里面介绍了很多的Makefile的知识,但是实例较少,我这里举例几个自动化变量使用,以便了解Makefile的强大功能。
关于自动化变量,在陈皓的文档里有下面的描述:
$@
表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。
$%
仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"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所不能识别的,那么"$*"就是空值。
入门:
假如我们有下面的程序:
[python] view
plain copy
print?
/* 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 文件,我们可以用下面的命令来执行:
[python] view
plain copy
print?
[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 文件来做,最简单的可以是如下:
[python] view
plain copy
print?
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 main
Makefile的一般书写方式是:
目标:依赖文件
生成目标文件的规则
英文的表述是:
targets: prerequisites
command
在Makefile文件中需要注意一点的就是:第一条规则中的目标将被确立为最终的目标。这里的最终目标也就是生成可执行文件main 编译运行结果如下:
[python] view
plain copy
print?
[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文件,可以有如下的写法:
[python] view
plain copy
print?
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 调试
如果只想看Makefile的执行命令,可以使用Makefile的调试,只需要执行make的时候带上下面的参数之一就可以了。
“-n”
“--just-print”
“--dry-run”
“--recon”
比如上面的Makefile,如果执行 make -n 它的输出如下:
[python] view
plain copy
print?
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信息
“-p”
“--print-data-base”
输出makefile中的所有数据,包括所有的规则和变量。这个参数会让一个简单的makefile都会输出一堆信息。如果你只是想输出信息而不想执行makefile,你可以使用“make -qp”命令。如果你想查看执行makefile前的预设变量和规则,你可以使用“make –p –f /dev/null”。这个参数输出的信息会包含着你的makefile文件的文件名和行号,所以,用这个参数来调试你的makefile会是很有用的,特别是当你的环境变量很复杂的时候。
============================================
高级:
进阶版Makefile文件可以写成下面的这样:
[python] view
plain copy
print?
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的“后缀规则”,是一种比较古老的规则,可以使用“模式规则”来实现:
[python] view
plain copy
print?
target = test
$(target): main.o test1.o test2.o test3.o
gcc -o $@ $^
%.c: %.o
gcc -c $<
clean:
$(RM) -f *.o $(target)
后缀规则和模式规则都属于Makefile的隐含规则
后缀规则的Makefile执行结果如下:
[python] view
plain copy
print?
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执行结果如下:
[python] view
plain copy
print?
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可以进一步简化,下面是简化后的版本:
[python] view
plain copy
print?
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应用实例
相关文章推荐
- Makfile 应用进阶实例
- JNI(java本地接口)的应用实例 含工具使用及cmd下使用 --JAVA 进阶
- 《FPGA全程进阶---实战演练》第三十二章 Signal Tap II 应用实例
- Visual Basic 2005入门、进阶与应用实例
- [shell应用进阶]:限制同时运行脚本实例的个数 -- 串行化:换一个思路。
- Docker容器虚拟化(四)—dockerfile的格式与应用(with实例)
- 随机读取文件--RandomAccessFile的应用实例
- JS进阶篇--IE浏览器的ActiveXObject对象以及FileSystemobject的应用扩展(完成)
- 推荐陈锐的新书《深入浅出--Visual Basic 2005入门、进阶与应用实例》
- IOS 开发之数据存储writeToFile的应用实例
- java高级进阶关于java多线程的应用 ThreadLocal多线程实例详解
- 强烈推荐<深入浅出--Visual Basic 2005入门、进阶与应用实例>
- 进阶--正则表达式的应用实例通俗说明
- cut、file、for、ls、的综合应用实例
- oracle的utl_file应用讨论实例
- java中file目录管理(三)walk与local应用实例,及walk与local的区别
- java高级进阶关于java多线程的应用 ThreadLocal多线程实例详解
- JsonResult&FileResult应用实例
- [友情推荐]深入浅出:Visual Basic 2005入门、进阶与应用实例
- 《循序渐进-SQL Server 2005入门、进阶与应用实例》即将上市