源地址 :http://blog.csdn.net/maopig/article/details/6801749
1、前言
回想本身的第一个Makefile,是这个样子的
html
后来有所进步,陆续地写了一些大都是这个样子的Makefile:mysql
看上去还行,用起来也不错,可是随着程序规模的扩大,每次添加一个新文件,都要手动修改Makefile,实在是不厌其烦。
后来阅读了一些开源程序的Makefile源代码,固然,不是automake生成的那种,有了一些心得,几番进化,一段时间后,感受对GNU make算是有了些初步的了解,在此总结一下,也算是温故而知新了。并且我记性比较差 ,放在这里算是记录一下,省得之后忘记。同时也省得你们再去翻那些繁复的手册,浪费没必要要的时间。
下文中makefile操做的对象有三个文件: foo.c , bar.c 和bar.h,内容分别以下:
foo.clinux
bar.csql
bar.hshell
OK,该交代的都交代了,进入正题。
2、个人makefile模板
把上个项目的makefile整理了一下,感受结构比较清晰,能够做为模板供之后使用。
文件内容大致是这个样子的:编程
解释:
前几行都是变量的定义,至于为何要定义这些变量,理由和编程中使用宏定义是同样的,那就是改一个就可使不少地方同时生效,避免了重复的工做。
按照惯例:
CC变量指定了使用的编译器
CFLAGS变量包含了所需的编译选项
INCLUDE是寻找头文件的路径
LFLAGS是加载外部库时的指定选项。
TARGET变量表明最终要生成的可执行程序
下面的内容就是关键了,咱们将利用一些GNU make内置的函数与推导规则来完成咱们的目标。
首先的任务是自动得到当前目录下全部的源文件,好让咱们新添文件后没必要再修改Makefile。
完成这个功能的是这行代码
SOUCE_FILES = $(wildcard *.c)
wildcard 是GNU make程序预约义的一个函数,做用即是获取匹配模式文件名,原型为$(wildcard PATTERN)。它的详细说明能够看这里。简单来讲wildcard函数的参数只有一个,就是函数名以后的文件名模式,这里的模式使用shell可识别 的通配符,包括“?”(单字符)、“*”(多字符)等。如今咱们的需求是获取当前目录下的全部.c文件,模式天然是*.c。
按照最基本的依赖规则,生成TARGET文件依赖于一系列的.o文件,那么如何得到这些.o文件的列表呢?答案是使用patsubst模式替换函数函数:
$(patsubst %.c,%.o,$(SOUCE_FILES))
模 式替换函数patsubst函数原型为$(patsubst PATTERN,REPLACEMENT,TEXT),相比wildcard,它要复杂一些,顾名思义,三个参数依次表明了匹配模式,替换规则,替换目标 字符串。在这里,咱们须要把全部.c替换成.o,因此写成上面的样子就能够了。
如今c源文件列表和obj文件列表都有了,下一步就该为每一个源文件编写规则了。
其实不少源文件的编译规则都是同样的,就像最开始那个Makefile中那样小程序
仅仅是文件名不一样而已,所以就给了咱们提取模式的某种可能性。我在一个关于winsock的makefile中找到了答案:bash
这个规则利用了GNU make的后缀规则。
在这里,当定义了一个目标是“.c.o”的规则时。它的含义是全部“.o”文件的依赖文件是对应的“.c”文件。所以在这条规则下,foo.c将被自动编译成foo,bar.c被编译成bar。
而特殊目标.SUFFIXES这句的做用是: 在默认后缀的基础上,增长了能够做为后缀的关键字符串。
其实.c.o是确定在默认识别的规则中的,不过为了保险起见,仍是显式地声明一下比较好。
能够看到,这个规则十分的晦涩,反正我第一眼真是没看明白。所以,新版本的GNU make已经使用模式规则替代了后缀规则。
一样的功能,利用模式规则实现以下:函数
这样看起来便清晰多了。若是考虑到头文件,完美的写法应该是这样的:工具
在上面的规则中,还使用了一些GNU make的自动化变量,他们的含义分别以下:
$@ --- 目标文件
$< --- 第一个依赖文件
$^ --- 全部的依赖文件
更多的自动化变量能够参见这里
最后的规则就是生成可执行文件了,很普通,再也不赘述。
为了方便调试,能够在makefile中定义一些伪目标。(伪目标的解释和意义能够看这里)
通常调试用的makefile中都会有两个伪目标,一个clean,一个debug
对 于clean,手册里说:“make存在一个内嵌隐含变量“RM”,它被定义为:“RM = rm –f”。所以在书写“clean”规则的命令行时可使用变量“$(RM)”来代替“rm”,这样能够免出现一些没必要要的麻烦!”虽然不知道“必要的麻 烦”是什么,可是当心不为过,照着手册作比较好。
对于debug,和正常模式不一样的就是添加了一些编译选项,修改CFLAGS的内容就能够了。但目前还没搞明白怎么动态地在makefile里修改变量的内容。这个问题之后再说。
3、在多文件夹状况使用makefile组织代码
上一段中给出的makefile,对于通常的小程序已经足矣,可是若是代码文件愈来愈多,最后不得不放到几个文件夹中,这时又该怎么办?
好比说咱们准备把bar.c中的函数整理成了一个函数库libbar放在主程序文件夹中的子文件夹libbar中,这时该如何利用makefile来组织这些文件?
比较好的办法是在libbar文件夹中放置一个独立的子makefile,而后在主makefile里调用它。
libbar/Makefile:
主Makefile:
在主makefile中使用了shell的for语句,循环取出SUBDIRS中的子文件夹名,而后进入子文件夹执行make,而后返回。若是在子makefile中出错,编译过程将终止。
4、编译多个目标
不知你有没有遇到过这样的状况,那就是须要从不少的代码,生成不少的可执行文件。
例如编写了一堆小工具,而每一个工具只有一个源文件,用foo.c生成foo,用bar.c生成bar。
一个一个编译确定不现实,这时该怎么作?让咱们用GNU make来解决吧!
仔细阅读手册,发现GNU make中的静态模式,正好能够知足这个要求。
方便阅读,直接将手册中关于静态模式的解释粘贴以下:
对应咱们的需求,应该是用符合%.c模式的文件,生成文件名为%的可执行文件,同时利用自动化变量,构造规则以下:
其中$(TARGET_FILES)为最终的可执行文件名,能够用wildcard配合patsubs函数得到。
由于$(TARGET_FILES)不止一个,因此直接写这个命令的结果是只会编译出一个可执行文件,即第目标文件列表中的一个文件,要想成功编译出全部的,还须要伪目标的帮忙。
完整的makefile以下:
CC = gcc
CFLAGS = -Wall -O
SOUCE_FILES=$(wildcard *.c)
TARGET_FILES=$(patsubst %.c,%,$(SOUCE_FILES))
.PHONY:all
all:$(TARGET_FILES)
$(TARGET_FILES): % : %.c
g++ $(CFLAGS) $< -o $@
clean:
$(RM) $(TARGET_FILES)
这里介绍两种变量的高级使用方法,第一种是变量值的替换。
咱们能够替换变量中的共有的部分,其格式是“$(var:a=b)”或是“${var:a=b}”,其意思是,把变量“var”中全部以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符”。
仍是看一个示例吧:
foo := a.o b.o c.o
bar := $(foo:.o=.c)
这个示例中,咱们先定义了一个“$(foo)”变量,而第二行的意思是把“$(foo)”中全部以“.o”字串“结尾”所有替换成“.c”,因此咱们的“$(bar)”的值就是“a.c b.c c.c”。
另一种变量替换的技术是以“静态模式”(参见前面章节)定义的,如:
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
这依赖于被替换字串中的有相同的模式,模式中必须包含一个“%”字符,这个例子一样让$(bar)变量的值为“a.c b.c c.c”。