做LinuxC++,一个稳定的项目,Makefile很少改动。但是如果需要修改的话,Makefile的语法和用法一时半会想不起来了(原谅我记性不好。。。)。在此记录下我之前的Makefile学习笔记,也分享给大家。本文假定读者已经了解Makefile,因此主要用作备忘和快速参考。尖括号始终表示变量。本文地址:https://segmentfault.com/a/1190000012091117另外,速查系列中还有另一篇文章:正则表达式速查笔记Make介绍了Makefile的基本规则:target...:prerequisites。..命令。.....其中target是目标文件,可以有多个,可以是.o文件也可以是可执行文件,甚至可以是标签。Prerequisites是先决条件,可以是文件,也可以是其他目标。这就构成了一种依赖关系:目标的先决条件在representations中定义,它的生成规则由命令决定。如果包含多个规则,则第一个规则是整个Makefile的默认规则。Make的工作流程在当前目录中查找Makefile或makefile以文件中的第一条规则作为默认规则。如果目标不存在,它会查找相应的.o文件。如果.o文件不存在,它会寻找.o的依赖来生成它Makefile有很多默认的生成规则,但我们在本文中不关心它,因为大多数情况下,我们需要编写规则我们自己。这易于定制、易于移植、易于交叉编译、易于调试。Makefile中的变量定义和调用格式:name=value#注意变量的值是变量值中允许有空格的$(name)部分。可以使用换行符“\”来伪造一个换行符,将两行内容连成一行,从而缩短Makefile的宽度。Makefile概述Makefile中有什么?文件指令:在一个Makefile中,可以制定另一个makefile,类似于C的includeMakefile,也可以进行条件include动作,类似于#if。Makefile可以将变量定义为多行命令。Makefile中只有行注释,没有节注释。注释以#开头。如果要使用#字符,需要对其进行转义,写成“\#”。Makefile规则内容中的所有shell命令必须以制表符Tab开头。请注意,空格字符是不可接受的。默认的make文件名是:GNUmakefile,makefile,Makefile,当输入make命令时,会自动搜索这些文件。惯例是使用最后一个。参考其他Makefile语法:includefilename...#Donotallowincludefailures-includefilename...#Allowincludefailures包含路径或通配符,一行可以包含多个文件。如果没有指定绝对路径或相对路径,那么make会按照以下顺序查找:make时在当前目录下查找-I参数下的/include(通常是/usr/local)或者--include-dir/binor/usr/include)建议手动指定,自动搜索可能会出现太多意外。这里的环境变量MAKEFILES主要是提醒:不要设置这个环境变量,否则会影响全局的makeinclude动作。Make如何使用通配符Make支持三种通配符:*、?、[...]。可以在规则或变量中使用。Pseudo-targetPseudo-target是Makefile中常见的.PH??ONY标记,如:“.PHONY:clean”,表示规则名并不代表真正需要生成的文件名,只是一个纯粹的规则.真实目标的特点是:如果目标不存在,就会执行。假目标的特点是:无论目标是否存在,都必须执行。除了makeclean,falsetarget还有一个使用场景,就是makeaction。在上生成了多个目标。例如:.PHONY:allall:exeinstall#包含生成的目标文件和安装动作的多目标规则。冒号前可以有多个target,表示多个target共享这条规则。自动生成依赖如果我们使用标准的makefile编写方式,就必须为每一个源文件编写头文件依赖,这样当头文件更新时,依赖这个头文件的源文件就可以自动重新编译。这实在是太麻烦了。幸运的是,gcc中有一个-MM(不是“-M”)选项,可以分析.c文件所依赖的头文件并打印出来。因此,在制作Makefile时,可以利用这个特性来自动生成依赖。有很多方法可以实现它。下面是我自己使用的例子。也可以参考我的项目代码:EXCLUDE_C_SRCS=#C_SRCS=$(filter-out$(EXCLUDE_C_SRCS),$(wildcard*.c))C_OBJS=$(C_SRCS:.c=.o)$(C_OBJS):$(C_OBJS:.o=.c)$(CC)-c$(CFLAGS)$*.c-o$*.o@$(CC)-MM$(CFLAGS)$*.c>$*.d@mv-f$*.d$*.d.tmp@sed-e's|.*:|$*.o:|'<$*.d。tmp>$*.d@sed-e's/.*://'-e's/\\$$//'<$*.d.tmp|fmt-1|sed-e's/^*//'-e's/$$/:/'>>$*.d@rm-f$*.d.tmp写命令这里的命令是指“命令”部分在Makefile规则中。命令执行在命令前面加上“@”,表示实际执行时不会打印命令语句,可以节省屏幕内容,减少垃圾信息(尤其是我自动生成的依赖命令。调优后,就是一堆无用信息)。如果“-”放在命令前,则表示忽略该命令的返回值(0)。如果上一个命令的结果需要用于下一个命令,则这些命令需要写在一行中。推荐使用“\”分隔。这最典型的是cd命令和它后面的命令链。在make中加上-n选项或者--just-print选项,意味着make不会被执行,只是打印出过程。make的嵌套执行在Makefile中,可以到另外一个目录下执行make。执行方式与普通命令调用类似,但特别的是make可以识别这是一个嵌套的make指令,所以打印“specialWherewheremake”提示语法为:subsystem:$(MAKE)-Csubdir主要优点这种方法的最大好处是可以将变量传递给下层的Makefile或者语法:exportVARIABLE...#使相应的变量成为当前make操作的全局变量直接指定变量的值也是恶意的:exportVARIABLE=value如果要传递所有变量(不推荐),直接写export即可。请注意,始终传递两个系统变量SHELL和MAKEFLAGS。另外,还有一个全局变量MAKELEVEL,用来表示当前的嵌套层级。定义命令包命令包类似于宏、子函数等。使用define来定义,以endif结束,例如:definerun-yaccyacc$(firstword$^)mvy.tab.c$@endif注意,如果是命令,需要以tab开头。调用这个命令包的方式是:$(run-yacc)使用变量变量赋值时,必须在定义变量时赋值,至少赋一个空值(只有等号,右边什么都没有)等号)。虽然不强制使用变量,但是为了保险起见,变量应该用圆括号或方括号括起来。如果要使用字符“$”,使用“$$”转义赋值,等号右边可以有一个未定义的变量,变量的内容直到实际使用才展开.但是这会导致循环引用。为避免这种情况,您可以使用“:=”符号来避免使用未定义的变量“+=”具有“追加”值的效果。如果等号右边有变量未定义,则相当于":=""?="作用是:如果等号左边的变量未定义,则使用右边的内容定义的等号,即:ifeq($(some_var),undefined)some_var=some_valendif另外:$@表示当前规则的编译目标$^表示当前规则的所有依赖文件$$<表示当前规则的第一个依赖项。定义一个空格变量NULL_STR:=#SPACE_STR:=$(NULL_STR)#endofline注意第二行注释和“)”之间有一个空格。注释的“#”必须有,否则不会定义空格。第一种变量替换方法是:$(var:.o=.c),意思是用等号右边的字符替换等号左边的字符。第二种方法就是所谓的“静态模式”:$(var:%.o=%.c)将变量值作为变量使用这与指针非常相似,只是地址值变成了变量值。可以使用变量值来生成变量名,如:a:=$($(var))或$($(var)_$(idx))等。override在命令行调用make时,可以直接指定变量的全局值,使其在整个make过程中保持不变。为了防止这个特性,可以使用这个关键字来处理:override=也可以使用等号:=and?=targetvariable(localvariable)如果一个约束不想使用一个定义的全局变量,可以这样写:prog:CFLAGS=-gprog:a.ob.o$(CC)$(CFLAGS)a.ob.o条件判断语法<条件语句><真执行语句>else<假执行语句>endif其中条件语句有四种情况:1.表示是否相等ifeq(,)#推荐ifeq''''ifeq""""2.表示是否不相等,将上面的ifeq换成ifneq3,ifdef4,ifndef所有使用make函数的函数都是内置函数,不能自己定义(命令包除外)。下面列出常用的函数,不明白的请详细查阅。常规函数字符串替换$(subst,,)模式字符串替换$(patsubst,,)去除前导和尾随空格$(strip)查找string$(findstring,)anti-filter$(filter-out,)sort(wordascendingorder)$(sort)takeword$(word,)取词串$(wordlist,,)统计词的个数$(words)去掉每个词最后的文件名部分,只留下目录部分$(dir)去掉每个单词的目录部分,只留下文件名部分$(notdir)读取每个文件名的后缀$(suffix)plussuffix$(addsuffix,)plusprefixplussuffix动态创建局部变量很有用$(addprefix,)connectionstring$(join,)forloop$(foreach,,)这其实是一个函数,作用是:将list中的单词一个一个取出来,放入变量中由var指定,然后执行text的表达式。返回值为文本的最终执行值。Shell函数执行shell命令,返回stdout作为返回值,如:contents:=$(shellls-la)controlsmakeoutput$(error)$(warning)这也是调试和定位make的好方法。判断文件是否存在ifeq($(FILE),$(wildcard$(FILE)))...endif