全部博文(2759)
分类: LINUX
2014-12-15 09:16:05
原文地址:跟我一行一行写Makefile 作者:cqw_cu_
跟我一行一行写Makefile
想必很多读者都拜读过陈皓的《跟我一起写Makefile》一文,我也是该文的忠实读者,从中受益颇深,该文至今我读了不下五遍,现在偶尔也会翻阅一下,因为有的东西看的时候懂了,但记忆不深刻,用到的时候还是写不出来,更别谈灵活运用了。
该文不写Makefile理论,因为我自知以我目前的水平再写Makefile理论,只能是画虎不成反类犬。
全文以实例、提问的方式引导读者思考,这样能有效的突出重点,也有利于读者加深记忆,还可以延长读者的观看时间,避免打瞌睡。
《跟我一起写Makefile-陈皓.pdf》
有好心人整理了一份pdf版本,目录清晰,排版整洁,比直接拉着网页看简便快捷多了,直接到百度搜索即可下载。
文章所有的Makefile文件及源码文件已经上传到CSDN,可0分下载,CSDN下载路径如下:
实例工程netembryo中包含如下的源文件和头文件:
main.c、get_info.c、multicast.c、rtsp.c、Sock.c、socket.c、url.c、sock_ntop_host.c、config.h、rtsp.h、url.h、wsocket.h、wsocket-internal.h
Makefile.1将main.c 、get_info.c、multicast.c、rtsp.c、Sock.c、socket.c、url.c、sock_ntop_host.c编译为.o文件,然后将编译生成的.o文件链接为可执行文件main。
Makefile.1内容截图如下:
main: main.o Sock.o socket.o sock_ntop_host.o url.o rtsp.o multicast.o get_info.o
gcc -g -o main main.o Sock.o socket.o sock_ntop_host.o url.o rtsp.o multicast.o get_info.o
main.o: main.c
Sock.o: Sock.c
gcc -c -I. Sock.c
socket.o: socket.c
gcc -c -I. socket.c
sock_ntop_host.o: sock_ntop_host.c
gcc -c -I. sock_ntop_host.c
url.o: url.c
gcc -c -I. url.c
rtsp.o: rtsp.c
gcc -c -I. rtsp.c
multicast.o: multicast.c
gcc -c -I. multicast.c
get_info.o: get_info.c
gcc -c -I. get_info.c
.PHONY: clean
clean:
-rm -rf main *.o
问题:
1. 第一个箭头处,没有写编译命令,而是让make“自动推导”,该处make自动推导出的编译命令是什么?
2. 第二个箭头处,“.PHONY”是伪目标,伪目标的定义是什么,在Makefile中伪目标有哪些妙用?
3. 第三个箭头处,“rm”命令前面的“-”有什么作用?
4. clean也是一个目录,为什么clean没有依赖,也不会生成目标文件clean?
5. 如果把编译命令中的“-I.”选项去掉,能编译通过吗,为什么?
6. 你觉得Makefile.1哪些地方好,哪些地方不好,你会如何优化?
Makefile.1目标、依赖和命令都比较清晰,对于小型工程这样写也没什么不好。如果工程文件多达几百个,这样写,整个Makefile就会显得比较臃肿,更重要的是不易修改、维护和扩展。
Makefile.2对Makefile.1进行第一阶段的优化,引入了“变量”、“静态模式”、“自动化变量”特性,让Makefile的体积一下缩小很多。这会让刚接触Makefile的新手感觉不易读,写成这样,不是为了炫技,而是这些都是Makefile中实用的特性,是很有必要掌握的,可以对照陈皓的“跟我一起写Makefile”一起看。
Makefile.2内容截图如下:
OBJS = main.o Sock.o socket.o sock_ntop_host.o url.o rtsp.o multicast.o get_info.o
main: $(OBJS)
gcc -g -o main $(OBJS)
$(OBJS): %.o : %.c
gcc -c -I. $<
.PHONY: clean
clean:
-rm -rf main *.o
1. 第一个箭头处,定义变量使用了“=”号,还有“:=”和“+=”号,此三种定义变量的方式有什么不同,各自适用于什么样的场景?
2. 第二个箭头处,使用了Makefile的“静态模式”,如何使用静态模式?
3. 第三个箭头处,使用了Makefile的“自动化变量”,Makefile有哪些自动化变量,各自代表什么意思?
4. 你觉得Makefile.2哪些地方好,哪些地方不好,你会如何进行优化?
Makefile.2相对于Makefile.1已经简便、轻巧了很多,但还是要巧一些编译命令,就像在C语言中使用魔数一样,不易阅读,也不易修改、维护,所以一般将魔数定义为宏。
Makefile.3在Makefile.2的基础上对Makefile.1进行第二阶段的优化,对编译命令进行优化。
Makefile.3内容截图如下:
CC= gcc
CFLAGS= -g -Wall -Werror
INCLUDES= -I.
SOURCES= main.c get_info.c multicast.c rtsp.c Sock.c socket.c sock_ntop_host.c url.c
OBJS := $(SOURCES:.c=.o)
main: $(OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $(OBJS)
$(OBJS): %.o : %.c
$(CC) -c $(CFLAGS) $(INCLUDES) $<
.PHONY: clean
clean:
-rm -rf main *.o
1. 矩形处,将编译命令定义为变量,这样有什么好处?
2. 第一个箭头处,OBJS变量的值是什么?
Makefile.4在Makefile.3的基础引入“模式规则”,改动非常小,但该特性十分重要且有用,在很多开源项目如ffmpeg中都可见到。
Makefile.4内容截图如下:
CC= gcc
CFLAGS= -g -Wall -Werror
INCLUDES= -I.
SOURCES= main.c get_info.c multicast.c rtsp.c Sock.c socket.c sock_ntop_host.c url.c
OBJS := $(SOURCES:.c=.o)
%.o: %.c
$(CC) -c $(CFLAGS) $(INCLUDES) $<
main: $(OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $(OBJS)
$(OBJS): %.o : %.c
.PHONY: clean
clean:
-rm -rf main *.o
问题:
1. 第一个箭头处,使用了Makefile中的什么规则,该规则的作用是什么?
2. 第二个箭头处,没有命令部分,该命令还是Makefile自动推导的吗,为什么?
Makefile.1-Makefile.4都是在一个Makefile文件中完成的,就像写c程序一样,把全部东西都塞在一个文件中,显然不是一种好的设计。
Makefile.5在Makefile.4的基础上将Makefile一分为二。
Rules5.mak内容截图如下:
CC= gcc
CFLAGS= -g -Wall -Werror
INCLUDES= -I.
SOURCES= main.c get_info.c multicast.c rtsp.c Sock.c socket.c sock_ntop_host.c url.c
Makefile.5内容截图如下:
include ./Rules5.mak
OBJS := $(SOURCES:.c=.o)
all: $(OBJS) main
.PHONY: all clean
%.o: %.c
$(CC) -c $(CFLAGS) $(INCLUDES) $<
main: $(OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $(OBJS)
$(OBJS): %.o : %.c
clean:
-rm -rf main *.o
问题:
1. 第一个箭头处,“include”的作用是什么?
2. 第二个箭头处,会生成“all”这个目标文件吗,为什么?
上面的Makefile依然不够灵活,如果有时需要将源码编译为库文件,供其它部件调用接口,有时需要将其编译为二进制文件,该如何改造和优化呢?
上面的Makefile存在一个很大的缺陷,就是修改了头文件,make命令无法检测到更新,不会重新编译工程,该如何改造和优化呢?