二、Makefile介绍
1、规则的格式
2、Makefile如何工作
3、指定变量
4、自动推导规则(隐含规则)
5、对规则进行分组(简洁的Makefile)
6、.PHONY
三、Makefile常用知识点
1、变量
2、函数
3、Makefile的条件执行
4、通配符和目录搜索
5、Makefile隐含规则
6、静态模式
一、准备知识[top]
编译:把高级语言书写的代码转换为机器可识别的机器指令。编译高级语言后生成的指令虽然可被机器识别,但是还不能被执行。编译时,编译器检查高级语言的语法、函数与变量的声明是否正确。只有所有的语法正确、相关变量定义正确编译器就可以编译出中间目标文件。通常,一个高级语言的源文件都可对应一个目标文件。目标文件在Linux 中默认后缀为“.o ”(如“foo.c”的目标文件为“foo.o”)。 为了和规则的目标文件相区别。本文将编译高级语言后生成的目标文件成为.o 文件。
链接:将多.o 文件,或者.o 文件和库文件链接成为可被操作系统执行的可执行程序(Linux 环境下,可执行文件的格式为“ELF ”格式)。链接器不检查函数所在的源文件,只检查所有.o 文件中的定义的符号。将.o 文件中使用的函数和其它.o 或者库文件中的相关符号进行合并,对所有文件中的符号进行重新安排(重定位),并链接系统相关文件(程序启动文件等)最终生成可执行程序。链接过程使用GNU 的“ld ”工具。
静态库:又称为文档文件(Archive File)。它是多个.o 文件的集合。Linux 中静态库文件的后缀为“.a ”。静态库中的各个成员(.o 文件)没有特殊的存在格式,仅仅是一个.o 文件的集合。使用“ar ”工具维护和管理静态库。
共享库:也是多个.o 文件的集合,但是这些.o 文件时有编译器按照一种特殊的方式生成(Linux 中,共享库文件格式通常为“ELF ”格式。共享库已经具备了可执行条件)。模块中各个成员的地址(变量引用和函数调用)都是相对地址。使用此共享库的程序在运行时,共享库被动态加载到内存并和主程序在内存中进行连接。多个可执行程序可共享库文件的代码段(多个程序可以共享的使用库中的某一个模块,共享代码,不共享数据)。另外共享库的成员对象可被执行(由libdl.so 提供支持)。
二、Makefile介绍[top]
1、规则的格式[top]
target [target ...]: [prerequisites ...]
[<TAB>command 1]
.
.
.
[<TAB>command n]
target :规则的目标。通常是最后需要生成的文件名或者为了实现这个目的而必需的中间过程文件名。可以是.o 文件、也可以是最后的可执行程序的文件名等。另外,目标也可以是一个make执行的动作的名称,如目标“clean”,我们称这样的目标是“伪目标”。
prerequisites :规则的依赖。生成规则目标所需要的文件名列表。通常一个目标依赖于一个或者多个文件。
command:规则的命令行。规则所要执行的动作(任意的 shell命令或者是可在shell下执行的程序)。
2、Makefile如何工作[top]
3个头文件和8个C文件如何创建最终的可执行文件“edit”
可以将一个较长行使用反斜线(\)来分解为多行,这样可以使我们的Makefile书写清晰、容易阅读理解。但需要注意:反斜线之后不能有空格。
默认的情况下,make执行的是Makefile中的第一个规则,此规则的第一个目标称之为“最终目的”或者“终极目标”(就是一个Makefile最终需要更新或者创建的目标)。
当在shell提示符下输入“make”命令以后。make读取当前目录下的Makefile 文件,并将 Makefile 文件中的第一个目标作为其执行的“终极目标”,开始处理第一个规则(终极目标所在的规则)。
3、指定变量[top]
终极目标"edit”所在的规则中,.o文件列表出现了两次:第一次:作为目标“edit ”的依赖文件列表出现,第二次:规则命令行中作为“cc”的参数列表。这样做所带来的问题是:如果我们需要为目标“edit ”增加一个的依赖文件,我们就需要在两个地方添加(依赖文件列表和规则的命令中)。
为了避免这个问题,在实际工作中大家都比较认同的方法是,使用一个变量“objects”、“OBJECTS”、“objs ”、“OBJS”、“obj”或者“OBJ ”来作为所有的.o文件的列表的替代。
4、自动推导规则(隐含规则)[top]
因为make本身存在一个默认的规则,能够自动完成对.c 文件的编译并生成对应的.o 文件,在使用make编译.c 源文件时,编译.c 源文件规则的命令可以不用明确给出。
在Makefile中我们只需要给出需要重建的目标文件名(一个.o 文件),make会自动为这个.o 文件寻找合适的依赖文件(对应的.c 文件。对应是指:文件名除后缀外,其余都相同的两个文件),而且使用正确的命令来重建这个目标文件。
这样,在书写Makefile 时,我们就可以省略掉描述.c 文件和.o 依赖关系的规则,而只需要给出那些特定的规则描述(.o 目标所需要的.h 文件)。
5、对规则进行分组(简洁的Makefile)[top]
Makefile 中,所有的.o 目标文件都可以使用隐含规则由make自动重建,我们可以根据这一点书写更加简洁的Makefile。而且在这个Makefile 中,我们是根据依赖而不是目标对规则进行分组。形成另外一种风格的Makefile。
这种风格的Makefile 并不值得我们借鉴。问题在于:同时把多个目标文件的依赖放在同一个规则中进行描述(一个规则中含有多个目标文件),这样导致规则定义不明了,比较混乱。建议大家不要在Makefile 中采用这种方式了书写。否则后期维护将会是一件非常痛苦的事情。
书写规则建议的方式是:单目标,多依赖。就是说尽量要做到一个规则中只存在一个目标文件,可有多个依赖文件。尽量避免使用多目标,单依赖的方式。这样书写的好处是后期维护会非常方便,而且这样做会使Makefile 会更清晰、明了。
6、.PHONY[top]
当清除工作目录过程文件时,前面用到了规则
但在实际应用中,应该用.PHONY将目标clean声明为伪目标
这两个实现有两点不同: 1. 通过“.PHONY ”特殊目标将“clean”目标声明为伪目标。避免当磁盘上存在一个名为“clean”文件时,目标“clean”所在规则的命令无法执行。2. 在命令行之前使用“- ”,意思是忽略命令“rm”的执行错误。
这样的一个目标在Makefile 中,不能将其作为终极目标(Makefile 的第一个目标)。因为我们的初衷并不是当你在命令行上输入make以后执行删除动作。而是要创建或者更新程序。
三、Makefile常用知识点[top]
1、变量[top]
(1) 在Makefile中,变量是一个名字(像是 C 语言中的宏),代表一个文本字符串(变量的值)。
(2) 变量可以用来代表一个文件名列表、编译选项列表、程序运行的选项参数列表、搜索源文件的目录列表、编译输出的目录列表和所有我们能够想到的事物。
(3) 变量名是大小写敏感的。
(4) “$<”、“$@ ”、“$?”、“$* ”等称为自动化变量。
1.1 自动化变量
模式规则中,规则的目标和依赖文件名代表了一类文件名;规则的命令是对所有这一类文件重建过程的描述,显然,在命令中不能出现具体的文件名,否则模式规则失去意义。那么在模式规则的命令行中该如何表示文件?所用到的就是自动化变量。
常用的自动化变量如下:
$@ 表示规则的目标文件名。如果目标是一个文档文件(Linux中,一般称.a 文件为文档文件,也称为静态库文件),那么它代表这个文档的文件名。在多目标模式规则中,它代表的是哪个触发规则被执行的目标文件名。
$% 当规则的目标文件是一个静态库文件时,代表静态库的一个成员名。例如,规则的目标是“foo.a(bar.o)”,那么,“ $%”的值就为“bar.o”,“ $@ ”的值为“foo.a”。如果目标不是静态库文件,其值为空。
$< 规则的第一个依赖文件名。如果是一个目标文件使用隐含规则来重建,则它代表由隐含规则加入的第一个依赖文件。
$? 所有比目标文件更新的依赖文件列表,空格分割。如果目标是静态库文件名,代表的是库成员(.o 文件)。
$^ 规则的所有依赖文件列表,使用空格分隔。如果目标是静态库文件,它所代表的只能是所有库成员(.o 文件)名。一个文件可重复的出现在目标的依赖中,变量“$^”只记录它的一次引用情况。就是说变量“$^”会去掉重复的依赖文件。
1.2 两种变量的定义
递归展开式(=)
这种变量的引用,在引用的地方是严格的文本替换过程,此变量值的字符串原模原样的出现在引用它的地方。在变量定义时,变量值中对其他变量的引用不会被替换展开;而是变量在引用它的地方替换展开的同时,它所引用的其它变量才会被一同替换展开。
foo = $(bar)
bar = $(ugh)
ugh = Huh?
all:;echo $(foo)
输出结果:Huh?
再如:
CFLAGS = $(include_dirs) -O
include_dirs = -Ifoo -Ibar
CFLAGS的值为-Ifoo -Ibar -O
直接展开式(:=)
为了避免“递归展开式”变量存在的问题和不方便。GNU make支持另外一种风格的变量,称为“直接展开”式。这种风格的变量使用“:= ”定义。在使用“:= ”定义变量时,变量值中对其他量或者函数的引用在定义变量时被展开(对变量进行替换)。所以变量被定义后就是一个实际需要的文本串,其中不再包含任何变量的引用。
一个例子:
CFLAGS = $(include_dirs) -O
include_dirs = -Ifoo -Ibar
由于变量“include_dirs ”的定义出现在“CFLAGS ”定义之后。因此在“CFLAGS ”的定义中,“include_dirs ”的值为空。“CFLAGS”的值为“-O”而不是“-Ifoo -Ibar -O ”。
在复杂的Makefile 中,推荐使用直接展开式变量。因为这种风格变量的使用方式和大多数编程语言中的变量使用方式基本上相同。它可以使一个比较复杂的Makefile在一定程度上具有可预测性。而且这种变量允许我们利用之前所定义的值来重新定义它(比如使用某一个函数来对它以前的值进行处理并重新赋值),此方式在Makefile 中经常用到。尽量避免和减少递归式变量的使用。
1.3 追加变量值
通过+=
objects = main.o foo.o bar.o utils.o
objects += another.o
相当于
objects = main.o foo.o bar.o utils.o
objects := $(object s) another.o
2、函数[top]
函数的调用语法:$(FUNCTION ARGUMENTS) 或 ${FUNCTION ARGUMENTS}
(1) FUNCTION专指make内嵌的函数名
(2) 参数与函数名之间用空格隔开,参数之间用逗号隔开
foreach函数: $(foreach VAR,LIST,TEXT)
来看一个例子,定义变量“files ”,它的值为四个目录(变量“dirs ”代表的 a、b、c、d 四个目录)下的文件列表:
dirs := a b c d files := $(foreach dir,$( dirs),$(wildcard $(dir)/*))
例子中,“TEXT”的表达式为“$(wildcard $(dir)/*)”。表达式第一次执行时将展开为“$(wildcard a/*) ”;第二次执行时将展开为“$(wildcard b/*) ”;第三次展开为$(wildcard c/*)”;….;以此类推。所以此函数所实现的功能就和一下语句等价:
files := $(wildcard a/* b/* c/* d/*)
if函数:$(if CONDITION,THEN-PART[,ELSE-PART])
示例:
SUBDIR += $(if $(SRC_DIR) $(SRC_DIR),/home/src)
函数的结果是:如果“SRC_DIR”变量值不为空,则将变量“SRC_DIR”指定的目录作为一个子目录;否则将目录“/home/src ”作为一个子目录。
shell函数:
contents := $(shell cat foo) files := $(shell echo *.c)
3、Makefile的条件执行[top]
来看一个使用条件判断的Makefile 例子;对变量“CC”进行判断,其值如果是“gcc ”那么在程序连接时使用库“libgnu.so”或者“libgnu.a ”,否则不链接任何库。Makefile 中的条件判断部分如下:
上面的例子,一种更简洁实现方式:
4、通配符和目录搜索[top]
clean:
rm -f *.o
函数wildcard:$(wildcard PATTERN...)
目录搜索:VPATH和vpath
5、Makefile隐含规则[top]
“隐含规则”为make提供了重建一类目标文件通用方法,不需要在Makefile 中明确地给出重建特定目标文件所需要的细节描述。
另外,在make执行时根据需要也可能是用多个隐含规则。比如:make将从一个.y文件生成对应的.c 文件,最后再生成最终的.o 文件。就是说,只要目标文件名中除后缀以外其它部分相同,make都能够使用若干个隐含规则来最终产生这个目标文件(当然最原始的那个文件必须存在)。
内嵌的“隐含规则”在其所定义的命令行中,会使用到一些变量(通常也是内嵌变量)。我们可以通过改变这些变量的值来控制隐含规则命令的执行情况。例如:内嵌变量“CFLAGS”代表了gcc 编译器编译源文件的编译选项,我们就可以在Makefile 中重新定义它,来改变编译源文件所要使用的参数。
尽管我们不能改变make内嵌的隐含规则,但是我们可以使用模式规则重新定义自己的隐含规则,也可以使用后缀规则来重新定义隐含规则。
5.1 make内嵌的隐含规则
使用make内嵌的隐含规则,在Makefile 中就不需要明确给出重建某一个目标的命令,甚至可以不需要规则。make会自动根据已存在(或者可以被创建)的源文件类型来启动相应的隐含规则。
例如:
foo : foo.o bar.o
cc -o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)
这里并没有给出重建文件“foo.o”的规则,make执行这条规则时,无论文件“foo.o”存在与否,都会试图根据隐含规则来重建这个文件(就是试图重新编译文件“foo.c”或者其它类型的源文件)。
make执行过程中找到的隐含规则,提供了此目标的基本依赖关系,确定了目标的依赖文件(通常是源文件,不包含对应的头文件依赖)和重建目标需要使用的命令行。隐含规则所提供的依赖文件只是一个最基本的(通常它们之间的对应关系为:“EXENAME.o ”对应“EXENAME.c ”、“EXENAME ”对应于“EXENAME.o ”)。当需要增加这个目标的依赖文件时,要在Makefile 中使用没有命令行的规则给出。
通常,make会对那些没有命令行的规则、双冒号规则寻找一个隐含规则来执行。作为一个规则的依赖文件,在没有一个规则明确描述它的依赖关系的情况下;make会将其作为一个目标并为它搜索一个隐含规则,试图重建它。
5.2 模式规则
模式规则类似于普通规则。只是在模式规则中,目标名中需要包含有模式字符“% ”(一个),包含有模式字符“% ”的目标被用来匹配一个文件名,“% ”可以匹配任何非空字符串。规则的依赖文件中同样可以使用“% ”,依赖文件中模式字符“% ”的取值情况由目标中的“% ”来决定。例如:对于模式规则“%.o : %.c”,它表示的含义是:所有的.o 文件依赖于对应的.c 文件。我们可以使用模式规则来定义隐含规则。
例如:“%.c”匹配所有以“.c ”结尾的文件(匹配的文件名长度最少为3 个字母),“s%.c”匹配所有第一个字母为“s”,而且必须以“.c ”结尾的文件,文件名长度最小为5个字符(模式字符“% ”至少匹配一个字符)。在目标文件名中“% ”匹配的部分称为“茎”。
因此,一个模式规则的格式为:
%.o : %.c ; COMMAND...
这个模式规则指定了如何由文件“N.c”来创建文件“N.o”,文件“N.c”应该是已存在的或者可被创建的。
模式规则中依赖文件也可以不包含模式字符“% ”。当依赖文件名中不包含模式字符“% ”时,其含义是所有符合目标模式的目标文件都依赖于一个指定的文件(例如:%.o : debug.h,表示所有的.o 文件都依赖于头文件“debug.h ”)。这样的模式规则在很多场合是非常有用的。
同样一个模式规则可以存在多个目标。多目标的模式规则和普通多目标规则有些不同,普通多目标规则的处理是将每一个目标作为一个独立的规则来处理。
%.o : %.c
$(CC) -c $(CFLAGS) $( CPPFLAGS) $< -o $@
5.3 后缀规则
双后缀规则定义一对后缀:目标文件的后缀和依赖目标的后缀。它匹配所有后缀为目标后缀的文件。对于一个匹配的目标文件,它的依赖文件这样形成:将匹配的目标文件名中的后缀替换为依赖文件的后缀得到。如:一个描述目标和依赖后缀的“.o ”和“.c ”的规则就等价于模式规则“%o : %c”。
单后缀规则只定义一个后缀:此后缀是源文件名的后缀。它可以匹配任何文件,其依赖文件这样形成:将后缀直接追加到目标文件名之后得到。例如:单后缀“.c ”就等价于模式规则“% : %.c”。
判断一个后缀规则是单后缀还是双后缀的过程:判断后缀规则的目标,如果其中只存在一个可被make识别的后缀,则规则是一个“单后缀”规则;当规则目标中存在两个可被make识别的后缀时,这个规则就是一个“双后缀”规则。
例如:“.c ”和“.o ”都是make可识别的后缀。因此当定义了一个目标是“.c.o ”的规则时。make会将它作为一个双后缀规则来处理,它的含义是所有“.o ”文件的依赖文件是对应的“.c ”文件。下边是使用后缀规则定义的编译.c 源文件的规则:
.c.o:
$(CC) -c $(CFLAGS) $( CPPFLAGS) -o $@ $<
注意:一个后缀规则中不存在任何依赖文件。否则,此规则将被作为一个普通规则对待。因此规则:
.c.o: foo.h
$(CC) -c $(CFLAGS) $( CPPFLAGS) -o $@ $<
就不是一个后缀规则。它是一个目标文件为“.c.o ”、依赖文件是“foo.h”的普通规则。它也不等价于规则:
%.o: %.c foo.h
$(CC) -c $(CFLAGS) $( CPPFLAGS) -o $@ $<
可识别的后缀指的是特殊目标“.SUFFIXES”所有依赖的名字。通过给特殊目标“SUFFIXES ”添加依赖来增加一个可被识别的后缀。像下边这样:
.SUFFIXES: .hack .win
它所实现的功能是把后缀“.hack ”和“.win ”加入到可识别后缀列表的末尾。
6、静态模式[top]
Makefile 中,静态模式规则和被定义为隐含规则的模式规则都是我们经常使用的两种方式。两者相同的地方都是用目标模式和依赖模式来构建目标的规则中的文件依赖关系。
隐含规则可被用在任何和它相匹配的目标上,在Makefile 中没有为这个目标指定具体的规则、存在规则但规则没有命令行或者这个目标的依赖文件可被搜寻到。当存在多个隐含规则和目标模式相匹配时,只执行其中的一个规则。具体执行哪一个规则取决于定义规则的顺序。
相反的,静态模式规则只能用在规则中明确指出的那些文件的重建过程中。不能用在除此之外的任何文件的重建过程中,并且它对指定的每一个目标来说是唯一的。如果一个目标存在于两个规则,并且这两个规则都定以了命令,make执行时就会提示错误。
静态模式规则相比隐含模式规则由以下两个优点:
(1) 不能根据文件名通过词法分析进行分类的文件,我们可以明确列出这些文件,并使用静态模式规则来重建其隐含规则。
(2) 对于无法确定工作目录内容,并且不能确定是否此目录下的无关文件会使用错误的隐含规则而导致make 失败的情况。当存在多个适合此文件的隐含规则时,使用哪一个隐含规则取决于其规则的定义顺序。这种情况下我们使用静态模式规则就可以避免这些不确定因素,因为静态模式中,指定的目标文件有明确的规则来描述其依赖关系和重建命令。
静态模式规则的基本语法:
TARGETS ...: TARGET-PATTERN: PREREQ-PATTERNS ...
COMMANDS
...
“TAGETS”列出了此规则的一系列目标文件。像普通规则的目标一样可以包含通配符。
“TAGET-PATTERN”和“PREREQ-PATTERNS ”说明了如何为每一个目标文件生成依赖文件。从目标模式(TAGET-PATTERN)的目标名字中抽取一部分字符串(称为“茎”)。使用“茎”替代依赖模式(PREREQ-PATTERNS )中的相应部分来产生对应目标的依赖文件。
首先在目标模式和依赖模式中,一般需要包含模式字符“% ”。在目标模式(TAGET-PATTERN)中“% ”可以匹配目标文件的任何部分,模式字符“%”匹配的部分就是“茎”。目标文件和目标模式的其余部分必须精确的匹配。看一个例子:目标“foo.o”符合模式“%.o”,其“茎”为“ foo ”。而目标“foo.c”和“foo.out”就不符合此目标模式。
每一个目标的依赖文件是使用此目标的“茎”代替依赖模式(PREREQ-PATTERNS )中的模式字符“% ”而得到。
objects = foo.o bar.o all: $(objects) $(objects): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@
例子中,规则描述了所有的.o 文件的依赖文件为对应的.c 文件,对于目标“foo.o”,取其茎“foo ”替代对应的依赖模式“%.c”中的模式字符“% ”之后可得到目标的依赖文件“foo.c”。这就是目标“foo.o”的依赖关系“foo.o: foo.c”,规则的命令行描述了如何完成由“foo.c”编译生成目标“foo.o”。
上边的这个规则描述了以下两个具体的规则:
foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o
在使用静态模式规则时,指定的目标必须和目标模式相匹配,否则执行make时将会得到一个错误提示。如果存在一个文件列表,其中一部分符合某一种模式而另外一部分符合另外一种模式,这种情况下我们可以使用“filter ”函数来对这个文件列表进行分类,在分类之后对确定的某一类使用模式规则。
例如:
files = foo.elc bar.o lose.o $(filter %.o,$(fi les)): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@ $(filter %.elc,$(files)): %.elc: %.el emacs -f batch-byte-compile $<
其中;$(filter %.o,$(files)) 的结果为“bar.o lose.o”。“filter ”函数过滤不符合“%.o”模式的文件名而返回所有符合此模式的文件列表。第一条静态模式规则描述了这些目标文件是通过编译对应的.c 源文件来重建的。同样第二条规则也是使用这种方式。
静态模式规则对一个较大工程的管理非常有用。它可以对整个工程的同一类文件的重建规则进行一次定义,而实现对整个工程中此类文件指定相同的重建规则。比如,可以用来描述整个工程中所有的.o 文件的依赖规则和编译命令。通常的做法是将生成同一类目标的模式定义在一个make.rules 的文件中。在工程各个模块的 Makefile 中包含此文件。
四、Makefile举例[top]
例1
#############################################################################
#
# Generic Makefile for C/C++ Program
#
# Author: ChengMing
#
# Description:
# ------------
# This is an easily customizable makefile template. The purpose is to
# provide an instant building environment for C/C++ programs.
#
# It searches all the C/C++ source files in the specified directories,
# makes dependencies, compiles and links to form an executable.
#
# Besides its default ability to build C/C++ programs which use only
# standard C/C++ libraries, you can customize the Makefile to build
# those using other libraries. Once done, without any changes you can
# then build programs using the same or less libraries, even if source
# files are renamed, added or removed. Therefore, it is particularly
# convenient to use it to build codes for experimental or study use.
#
# GNU make is expected to use the Makefile. Other versions of makes
# may or may not work.
#
# Usage:
# ------
# 1. Copy the Makefile to your program directory.
# 2. Customize in the "Customizable Section" only if necessary:
# * to use non-standard C/C++ libraries, set pre-processor or compiler
# options to and linker ones to
# (See Makefile.gtk+-2.0 for an example)
# * to search sources in more directories, set to <SRC_DIRS>
# * to specify your favorite program name, set to <APP>
# 3. Type make to start building your program.
#
# Make Target:
# ------------
# The Makefile provides the following targets to make:
# $ make compile and link
# $ make NODEP=yes compile and link without generating dependencies
# $ make objs compile only (no linking)
# $ make tags create tags for Emacs editor
# $ make ctags create ctags for VI editor
# $ make clean clean objects and the executable file
# $ make distclean clean objects, the executable and dependencies
# $ make help get the usage of the makefile
#
#===========================================================================
## Customizable Section: adapt those variables to suit your program.
##==========================================================================
# The Compiling Mode, Options: Debug, Release
MODE = Debug
# dependencies must be up to date,Options: Yes, No
CHECK_DEPS = Yes
#==================================================================
# The pre-processor options used by the cpp (man cpp for more).
CPPFLAGS =
# The options used in linking as well as in any direct use of ld.
LDFLAGS =
# The lib list to be linked.
LIBS =
# The directories in which source files reside.
# If not specified, only the current directory will be serached.
SRC_DIRS =
# The executable file name.
# If not specified, current directory name or `a.out' will be used.
APP =
## Implicit Section: change the following only when necessary.
##==========================================================================
# The source file types (headers excluded).
# .c indicates C source files, and others C++ ones.
SRCEXTS = .c .C .cc .cpp .CPP .c++ .cxx .cp
# The header file types.
HDREXTS = .h .H .hh .hpp .HPP .h++ .hxx .hp
# The pre-processor and compiler options.
# Users can override those variables from the command line.
CFLAGS = -O2 -Wall
CXXFLAGS = $(CFLAGS)
# Debug flag Options: -g, -g2, -g3
DEBUG_FLAG = -g
# std flag Options: -std=c99,-std=iso9899:199409, -std=c89
STD_FLAG = -std=c99
# The C program compiler.
CC = gcc
# The C++ program compiler.
CXX = g++
# Un-comment the following line to compile C programs as C++ ones.
#CC = $(CXX)
# The command used to delete file.
RM = rm -f
CTAGS = ctags
CTAGSFLAGS =
## Stable Section: usually no need to be changed. But you can add more.
##==========================================================================
SHELL = /bin/sh
#------------------------------------------------------------#
EMPTY =
SPACE = $(EMPTY) $(EMPTY)
ifeq ($(APP),)
CUR_PATH_NAMES = $(subst /,$(SPACE),$(subst $(SPACE),_,$(CURDIR)))
APP = $(word $(words $(CUR_PATH_NAMES)),$(CUR_PATH_NAMES))
ifeq ($(APP),)
APP = a.out
endif
endif
#----------------------#
ifeq ($(OS),Windows_NT)
APP := $(APP).exe
endif
#----------------------#
ifeq ($(MODE),Debug)
CFLAGS += $(DEBUG_FLAG)
endif
#----------------------#
ifeq ($(SRC_DIRS),)
SRC_DIRS = .
endif
#----------------------#
ifneq ($(STD_FLAG),)
CFLAGS += $(STD_FLAG)
endif
#==========================================================#知识点:变量; 函数
#SOURCES = $(foreach d,$(SRC_DIRS),$(wildcard $(addprefix $(d)/*,$(SRCEXTS))))
#HEADERS = $(foreach d,$(SRC_DIRS),$(wildcard $(addprefix $(d)/*,$(HDREXTS))))
SOURCES = $(wildcard $(SRC_DIRS)/*$(SRCEXTS))
HEADERS = $(wildcard $(SRC_DIRS)/*$(HDREXTS))
SRC_CXX = $(filter-out %.c,$(SOURCES))
OBJS = $(addsuffix .o, $(basename $(SOURCES)))
DEPS = $(OBJS:.o=.d)
## Define some useful variables.
DEP_OPT = $(shell if `$(CC) --version | grep "GCC" >/dev/null`; then \
echo "-MM -MP"; else echo "-M"; fi )
DEPEND = $(CC) $(DEP_OPT) $(CPPFLAGS) $(CFLAGS)
#---------------------------------------------------------#
DEPEND_d = $(subst $(DEBUG_FLAG),,$(DEPEND))
COMPILE_c = $(CC) $(CPPFLAGS) $(CFLAGS) -c
COMPILE_cxx = $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c
LINK_c = $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS)
LINK_cxx = $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS)
#---------------------------------------------------------#
PRODUCTS = $(APP) $(OBJS)
#==================================================================
.PHONY: all objs ctags clean distclean help show #知识点:特殊目标.PHONY
# Delete the default suffixes
.SUFFIXES: #知识点:特殊目标.SUFFIXES
all: $(APP)
# Rules for creating dependency files (.d).
#------------------------------------------
%.d:%.c #知识点:模式符号%
@echo -n $(dir $<) > $@ #知识点:前面加@符号则不会显示命令的执行过程; 函数$(dir $<); 自动变量$<和$@; 重定向符>
@$(DEPEND_d) $< >> $@ #知识点:变量的引用$(DEPEND_d); 重定向符号>>
%.d:%.C
@echo -n $(dir $<) > $@
@$(DEPEND_d) $< >> $@
%.d:%.cc
@echo -n $(dir $<) > $@
@$(DEPEND_d) $< >> $@
%.d:%.cpp
@echo -n $(dir $<) > $@
@$(DEPEND_d) $< >> $@
%.d:%.CPP
@echo -n $(dir $<) > $@
@$(DEPEND_d) $< >> $@
%.d:%.c++
@echo -n $(dir $<) > $@
@$(DEPEND_d) $< >> $@
%.d:%.cp
@echo -n $(dir $<) > $@
@$(DEPEND_d) $< >> $@
%.d:%.cxx
@echo -n $(dir $<) > $@
@$(DEPEND_d) $< >> $@
# Rules for generating object files (.o).
#----------------------------------------
objs:$(OBJS)
%.o:%.c
$(COMPILE_c) $< -o $@
%.o:%.C
$(COMPILE_cxx) $< -o $@
%.o:%.cc
$(COMPILE_cxx) $< -o $@
%.o:%.cpp
$(COMPILE_cxx) $< -o $@
%.o:%.CPP
$(COMPILE_cxx) $< -o $@
%.o:%.c++
$(COMPILE_cxx) $< -o $@
%.o:%.cp
$(COMPILE_cxx) $< -o $@
%.o:%.cxx
$(COMPILE_cxx) $< -o $@
# Rules for generating the tags.
#-------------------------------------
ctags: $(HEADERS) $(SOURCES)
$(CTAGS) $(CTAGSFLAGS) $(HEADERS) $(SOURCES)
# Rules for generating the executable.
#-------------------------------------
$(APP):$(OBJS)
ifeq ($(SRC_CXX),) # C program
$(LINK_c) $(OBJS) -o $@
@echo Type ./$@ to execute the program.
else # C++ program
$(LINK_cxx) $(OBJS) -o $@
@echo Type ./$@ to execute the program.
endif
#==========================================================================
# If dependencies have to be up to date then include dependencies makefiles.
#-----------------------------------
ifeq ($(CHECK_DEPS), Yes)
ifneq ($(DEPS),)
sinclude $(DEPS)
PRODUCTS += $(DEPS)
endif
endif
PRODUCTS += TAGS tags
#==========================================================================
clean:
$(RM) $(OBJS) $(APP)
distclean:
$(RM) $(PRODUCTS)
rebuild: clean all
# Show help.
help:
@echo
@echo 'Usage: make [TARGET]'
@echo 'TARGETS:'
@echo ' all (=make) compile and link.'
@echo ' objs compile only (no linking).'
@echo ' tags create tags for Emacs editor.'
@echo ' ctags create ctags for VI editor.'
@echo ' clean clean objects and the executable file.'
@echo ' distclean clean objects, the executable and dependencies.'
@echo ' rebuild clean and make all again.'
@echo ' show show variables (for debug use only).'
@echo ' help print this message.'
@echo
# Show variables (for debug use only.)
show:
@echo 'OS :' $(OS)
@echo 'APP :' $(APP)
@echo 'MODE :' $(MODE)
@echo 'SHELL :' $(SHELL)
@echo 'SRC_DIRS :' $(SRC_DIRS)
@echo 'HEADERS :' $(HEADERS)
@echo 'SOURCES :' $(SOURCES)
@echo 'SRC_CXX :' $(SRC_CXX)
@echo 'OBJS :' $(OBJS)
@echo 'DEPS :' $(DEPS)
@echo 'DEPEND_d :' $(DEPEND_d)
@echo 'COMPILE_c :' $(COMPILE_c)
@echo 'COMPILE_cxx :' $(COMPILE_cxx)
@echo 'LINK_c :' $(LINK_c)
@echo 'LINK_cxx :' $(LINK_cxx)
@echo 'PRODUCTS :' $(PRODUCTS)
## End of the Makefile ## Suggestions are welcome ## All rights reserved ##
#############################################################################
例2
# Generic GNUMakefile # Just a snippet to stop executing under other make(1) commands # that won't understand these lines ifneq (,) This makefile requires GNU Make. endif PROGRAM = foo C_FILES := $(wildcard *.c) OBJS := $(patsubst %.c, %.o, $(C_FILES)) CC = cc CFLAGS = -Wall -pedantic LDFLAGS = all: $(PROGRAM) $(PROGRAM): .depend $(OBJS) $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(PROGRAM) depend: .depend .depend: cmd = gcc -MM -MF depend $(var); cat depend >> .depend; .depend: @echo "Generating dependencies..." @$(foreach var, $(C_FILES), $(cmd)) @rm -f depend -include .depend # These are the pattern matching rules. In addition to the automatic # variables used here, the variable $* that matches whatever % stands for # can be useful in special cases. %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ %: %.c $(CC) $(CFLAGS) -o $@ $< clean: rm -f .depend *.o .PHONY: clean depend
例3
例4
例5
详细请参考:多目录的Makefile
例6
--------------------- env.mk----------------------------
CC := gcc
#头文件目录
INCLUDE_DIR := \
-I$(MAKEROOT)/ui \
-I$(MAKEROOT)/main \
-I$(MAKEROOT)/math
CFLAGS := $(INCLUDE_DIR)
#对所有的.o文件以.c文件创建它
%.o : %.c
${CC} ${CFLAGS} -c $< -o $(MAKEROOT)/obj/$@
-------------------proj/makefile -----------------------
export MAKEROOT := $(shell pwd)
TARGET_DIRS := main \
ui \
math
include ./env.mk
define build_obj
for SubDir in $(TARGET_DIRS); do \ #对所每一个子目录如果其存在
if ! [ -d $$SubDir ]; then \
echo "The $$SubDir is not exist !"; \
exit 11; \
fi; \
echo "Building $$SubDir ..."; \
make -C $$SubDir ; \ #执行子目录的makefile
if [ $$? -ne 0 ]; then \
echo "Building $$SubDir is failed !"; \
exit 12; \
fi; \
done
endef
all :
@$(call build_obj) #调用过程
gcc -fPIC -o out ./obj/*.o ./obj/*.so #生成最终目标
clean :
-rm ./obj/*.o ./out ./obj/*.so
------------- ui/makefile main/makefile ----------------
include $(MAKEROOT)/env.mk #包括头文件,执行模式规则
SRCS := $(wildcard *.c)
OBJS := $(patsubst %.c,%.o,$(SRCS)) #生成所有.c文件对应的.o文件
.PHONY : all
all : $(OBJS)
------------------ math/makefile -----------------------
.PHONY : all
all :
gcc -shared -o ../obj/libmath.so math.c math.h #生成动态库文件
详细请参考:多目录工程的Makefile
例7
# Readme: # use: make config # use: make dir # use: make GlobalClean # use: make clean # use: make rebuild # use: make cleanall # use: make test # use: make # firs of all, use "make config" or "make dir" to # build a source files struct. and then, # put your source files into the DIR src # link libs to the DIR lib # set as a C++ Makefile # use: ":1,$ s/\.c/\.cpp/g" in vi SHELL=/bin/sh CC=gcc MAKE=make MAKE_DIR=$(PWD) SRC_DIR=$(MAKE_DIR)/ SRC_DIR+=/home/phm/src/ OBJ_DIR=$(MAKE_DIR)/obj/ LIB_DIR=$(MAKE_DIR)/ LIB_DIR+=/usr/lib/ INCLUDE_DIR=$(MAKE_DIR)/ INCLUDE_DIR+=$(MAKE_DIR)/../include/ DEBUG_DIR=$(MAKE_DIR)/debug/ RELEASE_DIR=$(MAKE_DIR)/release/ OUTPUT_DIR= OUTPUT_FILE=decaudio INCLUDE=$(foreach n,$(INCLUDE_DIR),-I$(n)) #INCLUDE+=$(foreach n,$(SRC_DIR),-I$(n)) #LIB=$(foreach n,$(LIB_DIR),-L$(n)) #LIB+=$(foreach n,$(LIB_DIR),-L$(wildcard $(n)lib*.a)) #LIB:=$(notdir $(LIB)) #LIB:=$(patsubst %*.a,%.ddd ,$(LIB)) #LIB:=$(patsubst lib%.a,-l% ,$(LIB) ) #LIB +=-L$(OBJ_DIR) LIB+=-lavformat LIB+=-lavcodec LIB+=-Lavutil LIB+=-lz vpath %.h $(INCLUDE_DIR) vpath %.c $(SRC_DIR) vpath %.o $(OBJ_DIR) vpath %.d $(OBJ_DIR) SRC_FILES:=$(foreach n,$(SRC_DIR),$(wildcard $(n)*.c)) SRC_FILES:=$(notdir $(SRC_FILES)) OBJ_FILES:=$(patsubst %.c,%.o,$(SRC_FILES) ) DEP_FILES:=$(patsubst %.c,%.d,$(SRC_FILES) ) FLAG_DEBUG=-g FLAG_COMPLE=-c FLAG_LINK= DEBUG=1 ifeq ($(DEBUG),1) OUTPUT_DIR:=$(DEBUG_DIR) FLAG_COMPLE:=$(FLAG_COMPLE) $(FLAG_DEBUG) FLAG_LINK:= else OUTPUT_DIR:=$(RELEASE_DIR) FLAG_COMPLE:=$(FLAG_COMPLE) -static FLAG_LINK:= endif OUT=$(OUTPUT_DIR)$(OUTPUT_FILE) $(OUT): $(OBJ_FILES) @echo -e "building: $(notdir $@) \n\t please wait ...\n" @$(CC) $(FLAG_LINK) $(addprefix $(OBJ_DIR),$(notdir $^)) $(LIB) -o $@ %.o:%.c %.d @echo -e "building: $(notdir $@) \n\t please wait ...\n" @$(CC) $(FLAG_COMPLE) $< $(INCLUDE) -o $(OBJ_DIR)$@ $(OBJ_DIR)%.d:%.c @echo -e "building: $(notdir $@) \n\t please wait ...\n" @$(CC) $< $(INCLUDE) -MM -MD -o $@ -include $(addprefix $(OBJ_DIR),$(DEP_FILES)) config: dir dir: # mkdir -p $(SRC_DIR) mkdir -p $(OBJ_DIR) # mkdir -p $(LIB_DIR) # mkdir -p $(INCLUDE_DIR) mkdir -p $(DEBUG_DIR) mkdir -p $(RELEASE_DIR) clean: @rm -f $(OBJ_DIR)* *.d *.o @rm -f $(OUT) @clear rebuild: clean all cleanall: @rm -f $(OBJ_DIR)* @rm -f $(RELEASE_DIR)* @rm -f $(DEBUG_DIR)* test: $(OUT) .PHONY: all config rebuild test cleanall .SUFFIXES: GlobalClean: @find . -type f -name "Makefile" |sed -n '2,$$p'|sed s/Makefile/\ \`pwd\`/g|awk ' {ECHO="echo"};{CD="cd "};{MAKE="&& make clean&&"};{print ECHO,CD,$$1,MAKE,CD,$$2 } ' |sh
【参考资料】[top]