<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<atom:link href="http://gentoo-zh.org/extern.php?action=feed&amp;tid=304&amp;type=rss" rel="self" type="application/rss+xml" />
		<title><![CDATA[Gentoo中文社区 / makefile语法]]></title>
		<link>http://www.gentoo-zh.org/viewtopic.php?id=304</link>
		<description><![CDATA[makefile语法 最近发表的帖子。]]></description>
		<lastBuildDate>Fri, 26 Aug 2022 06:04:51 +0000</lastBuildDate>
		<generator>FluxBB</generator>
		<item>
			<title><![CDATA[makefile语法]]></title>
			<link>http://www.gentoo-zh.org/viewtopic.php?pid=310#p310</link>
			<description><![CDATA[<p>Makefile里主要包含了五个东西：显式规则、隐晦规则、变量定义、文件指示和注释<br />&#160; &#160; &#160; &#160; 显式规则。显式规则说明了，如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出，要生成的文件，文件的依赖文件，生成的命令。<br />&#160; &#160; &#160; &#160; 隐晦规则。由于我们的make有自动推导的功能，所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile，这是由make所支持的。<br />&#160; &#160; &#160; &#160; 变量的定义。在Makefile中我们要定义一系列的变量，变量一般都是字符串，这个有点你C语言中的宏，当Makefile被执行时，其中的变量都会被扩展到相应的引用位置上。<br />&#160; &#160; &#160; &#160; 文件指示。其包括了三个部分，一个是在一个Makefile中引用另一个Makefile，就像C语言中的include一样；另一个是指根据某些情况指定Makefile中的有效部分，就像C语言中的预编译#if一样；还有就是定义一个多行的命令。有关这一部分的内容，我会在后续的部分中讲述。<br />&#160; &#160; &#160; &#160; &#160;注释。Makefile中只有行注释，和UNIX的Shell脚本一样，其注释是用“#”字符，这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符，可以用反斜框进行转义，如：“\#”。<br />最后，还值得一提的是，在Makefile中的命令，必须要以[Tab]键开始。</p><br /><br /><p>= 赋值，可以引用后面变量 例子：<br />foo = $(bar)<br />bar = $(ugh)<br />ugh = Huh?<br />all:<br />echo $(foo)<br />我们执行“make all”将会打出变量$(foo)的值是“Huh?”（ $(foo)的值是$(bar)，$(bar)的值是$(ugh)，$(ugh)的值是“Huh?”）可见，变量是可以使用后面的变量来定义的。</p><p>:= 赋值，只能引用前面变量</p><p>?= 例子：<br />FOO ?= bar<br />其含义是，如果FOO没有被定义过，那么变量FOO的值就是“bar”，如果FOO先前被定义过，那么这条语将什么也不做，其等价于：</p><br /><p>三、变量高级用法：</p><p>第一种是变量值的替换。<br />示例：<br />foo := a.o b.o c.o<br />bar := $(foo:.o=.c)<br />这个示例中，我们先定义了一个“$(foo)”变量，而第二行的意思是把“$(foo)”中所有以“.o”字串“结尾”全部替换成“.c”，所以我们的“$(bar)”的值就是“a.c b.c c.c”。<br />另外一种变量替换的技术是以“静态模式”（参见前面章节）定义的，如：<br />oo := a.o b.o c.o<br />bar := $(foo:%.o=%.c)<br />这依赖于被替换字串中的有相同的模式，模式中必须包含一个“%”字符，这个例子同样让$(bar)变量的值为“a.c b.c c.c”。</p><p>第二种高级用法是——“把变量的值再当成变量”<br />x = y<br />y = z<br />a := $($(x))<br />在这个例子中，$(x)的值是“y”，所以$($(x))就是$(y)，于是$(a)的值就是“z”。（注意，是“x=y”，而不是“x=$(y)”）</p><br /><p>四、追加变量值：<br />+=如：<br />objects = main.o foo.o bar.o utils.o<br />objects += another.o<br />于是，我们的$(objects)值变成：“main.o foo.o bar.o utils.o another.o”（another.o被追加进去了）</p><br /><p>五、override 指示符:<br />如果有变量是通常make的命令行参数设置的，那么Makefile中对这个变量的赋值会被忽略。如果你想在Makefile中设置这类参数的值，那么，你可以使用“override”指示符。其语法是：<br />override &lt;variable&gt; = &lt;value&gt;<br />override &lt;variable&gt; := &lt;value&gt;<br />当然，你还可以追加：<br />override &lt;variable&gt; += &lt;more text&gt;<br />对于多行的变量定义，我们用define指示符，在define指示符前，也同样可以使用ovveride指示符，如：<br />override define foo<br />bar<br />endef</p><br /><p>六、多行变量<br />还有一种设置变量值的方法是使用define关键字。使用define关键字设置变量的值可以有换行，这有利于定义一系列的命令（前面我们讲过“命令包”的技术就是利用这个关键字）<br />define 指示符后面跟的是变量的名字，而重起一行定义变量的值，定义是以endef关键字结束。其工作方式和“=”操作符一样。变量的值可以包含函数、命令、文字，或是其它变量。因为命令需要以[Tab]键开头，所以如果你用define定义的命令变量中没有以[Tab]键开头，那么make就不会把其认为是命令。<br />下面的这个示例展示了define的用法：<br />define two-lines<br />echo foo<br />echo $(bar)<br />endef</p><br /><p>七、环境变量<br />make 运行时的系统环境变量可以在make开始运行时被载入到Makefile文件中，但是如果Makefile中已定义了这个变量，或是这个变量由make命令行带入，那么系统的环境变量的值将被覆盖。（如果make指定了“-e”参数，那么，系统环境变量将覆盖Makefile中定义的变量）</p><br /><p>八、目标变量<br />前面我们所讲的在Makefile中定义的变量都是“全局变量”，在整个文件，我们都可以访问这些变量。当然，“自动化变量”除外，如“$&lt;”等这种类量的自动化变量就属于“规则型变量”，这种变量的值依赖于规则的目标和依赖目标的定义。<br />用法：<br />&lt;target ...&gt; : &lt;variable-assignment&gt;<br />&lt;target ...&gt; : overide &lt;variable-assignment&gt;<br />如：<br />prog : CFLAGS = -g<br />prog : prog.o foo.o bar.o<br />$(CC) $(CFLAGS) prog.o foo.o bar.o</p><br /><p>九、模式变量<br />make的“模式”一般是至少含有一个“%”的<br />模式变量的语法和“目标变量”一样：<br />&lt;pattern ...&gt; : &lt;variable-assignment&gt;<br />&lt;pattern ...&gt; : override &lt;variable-assignment&gt;<br />使用条件判断<br />——————<br />一、示例<br />下面的例子，判断$(CC)变量是否“gcc”，如果是的话，则使用GNU函数编译目标。<br />libs_for_gcc = -lgnu<br />normal_libs =<br />foo: $(objects)<br />ifeq ($(CC),gcc)<br />$(CC) -o foo $(objects) $(libs_for_gcc)<br />else<br />$(CC) -o foo $(objects) $(normal_libs)<br />endif</p><p>二、语法<br />条件表达式的语法为：<br />&lt;conditional-directive&gt;<br />&lt;text-if-true&gt;<br />endif<br />以及：<br />&lt;conditional-directive&gt;<br />&lt;text-if-true&gt;<br />else<br />&lt;text-if-false&gt;<br />endif<br />其中&lt;conditional-directive&gt;表示条件关键字<br />第一个是“ifeq”&#160; &#160;比较参数“arg1”和“arg2”的值是否相同。<br />第二个是“ifneq”&#160; 其比较参数“arg1”和“arg2”的值是否相同，如果不同，则为真。和“ifeq”类似。<br />第三个是“ifdef”&#160; 如果变量&lt;variable-name&gt;的值非空，那到表达式为真。否则，表达式为假。<br />第四个是“ifndef” 和“ifdef”是相反的意思。</p><br /><p>使用函数<br />————<br />一、函数的调用语法<br />函数调用，很像变量的使用，也是以“$”来标识的，其语法如下：<br />$(&lt;function&gt; &lt;arguments&gt; )<br />或是<br />${&lt;function&gt; &lt;arguments&gt;}<br />这里，&lt;function&gt;就是函数名，make支持的函数不多。&lt;arguments&gt;是函数的参数，参数间以逗号“,”分隔，而函数名和参数之间以“空格”分隔。函数调用以“$”开头，以圆括号或花括号把函数名和参数括起。感觉很像一个变量，是不是？函数中的参数可以使用变量，为了风格的统一，函数和变量的括号最好一样，如使用“$(subst a,b,$(x))”这样的形式，而不是“$(subst a,b,${x})”的形式。因为统一会更清楚，也会减少一些不必要的麻烦。</p><p>二、字符串处理函数<br />$(subst &lt;from&gt;,&lt;to&gt;,&lt;text&gt; )<br />名称：字串替换函数——subst。<br />功能：把字串&lt;text&gt;中的&lt;from&gt;字符串替换成&lt;to&gt;。<br />返回：函数返回被替换过后的字符串。</p><br /><p>名称：模式字符串替换函数——patsubst。<br />功能：查找&lt;text&gt;中的单词（单词以“空格”、“Tab”或“回车”“换行”分隔）是否符合模式&lt;pattern&gt;，如果匹配的话，则以&lt;replacement&gt;替换。这里，&lt;pattern&gt;可以包括通配符“%”，表示任意长度的字串。如果&lt;replacement&gt;中也包含“%”，那么，&lt;replacement&gt;中的这个“%”将是&lt;pattern&gt;中的那个“%”所代表的字串。（可以用“\”来转义，以“\%”来表示真实含义的“%”字符）返回：函数返回被替换过后的字符串。</p><p>名称：去空格函数——strip。<br />功能：去掉&lt;string&gt;字串中开头和结尾的空字符。<br />返回：返回被去掉空格的字符串值。</p><p>名称：查找字符串函数——findstring。<br />功能：在字串&lt;in&gt;中查找&lt;find&gt;字串。<br />返回：如果找到，那么返回&lt;find&gt;，否则返回空字符串。</p><br /><p>名称：过滤函数——filter。<br />功能：以&lt;pattern&gt;模式过滤&lt;text&gt;字符串中的单词，保留符合模式&lt;pattern&gt;的单词。可<br />以有多个模式。<br />返回：返回符合模式&lt;pattern&gt;的字串。</p><p>名称：反过滤函数——filter-out。<br />功能：以&lt;pattern&gt;模式过滤&lt;text&gt;字符串中的单词，去除符合模式&lt;pattern&gt;的单词。可<br />以有多个模式。<br />返回：返回不符合模式&lt;pattern&gt;的字串。</p><p>名称：排序函数——sort。<br />功能：给字符串&lt;list&gt;中的单词排序（升序）。<br />返回：返回排序后的字符串。<br />示例：$(sort foo bar lose)返回“bar foo lose” 。</p><p>名称：取单词函数——word。<br />功能：取字符串&lt;text&gt;中第&lt;n&gt;个单词。（从一开始）<br />返回：返回字符串&lt;text&gt;中第&lt;n&gt;个单词。如果&lt;n&gt;比&lt;text&gt;中的单词数要大，那么返回空<br />字符串。</p><p>名称：取单词串函数——wordlist。<br />功能：从字符串&lt;text&gt;中取从&lt;s&gt;开始到&lt;e&gt;的单词串。&lt;s&gt;和&lt;e&gt;是一个数字。<br />返回：返回字符串&lt;text&gt;中从&lt;s&gt;到&lt;e&gt;的单词字串。如果&lt;s&gt;比&lt;text&gt;中的单词数要大，那<br />么返回空字符串。如果&lt;e&gt;大于&lt;text&gt;的单词数，那么返回从&lt;s&gt;开始，到&lt;text&gt;结束的单<br />词串。</p><p>名称：单词个数统计函数——words。<br />功能：统计&lt;text&gt;中字符串中的单词个数。<br />返回：返回&lt;text&gt;中的单词数。<br />示例：$(words, foo bar baz)返回值是“3”。<br />备注：如果我们要取&lt;text&gt;中最后的一个单词，我们可以这样：$(word $(words &lt;text&gt; <br />),&lt;text&gt; )。</p><p>名称：首单词函数——firstword。<br />功能：取字符串&lt;text&gt;中的第一个单词。<br />返回：返回字符串&lt;text&gt;的第一个单词。<br />示例：$(firstword foo bar)返回值是“foo”。<br />备注：这个函数可以用word函数来实现：$(word 1,&lt;text&gt; )。</p><p>三、文件名操作函数<br />$(dir &lt;names...&gt; )</p><p>名称：取目录函数——dir。<br />功能：从文件名序列&lt;names&gt;中取出目录部分。目录部分是指最后一个反斜杠（“/”）之<br />前的部分。如果没有反斜杠，那么返回“./”。<br />返回：返回文件名序列&lt;names&gt;的目录部分。<br />示例： $(dir src/foo.c hacks)返回值是“src/ ./”。</p><p>名称：取文件函数——notdir。<br />功能：从文件名序列&lt;names&gt;中取出非目录部分。非目录部分是指最后一个反斜杠（“/”<br />）之后的部分。<br />返回：返回文件名序列&lt;names&gt;的非目录部分。<br />示例： $(notdir src/foo.c hacks)返回值是“foo.c hacks”。</p><p>名称：取后缀函数——suffix。<br />功能：从文件名序列&lt;names&gt;中取出各个文件名的后缀。<br />返回：返回文件名序列&lt;names&gt;的后缀序列，如果文件没有后缀，则返回空字串。<br />示例：$(suffix src/foo.c src-1.0/bar.c hacks)返回值是“.c .c”。</p><p>名称：取前缀函数——basename。<br />功能：从文件名序列&lt;names&gt;中取出各个文件名的前缀部分。<br />返回：返回文件名序列&lt;names&gt;的前缀序列，如果文件没有前缀，则返回空字串。<br />示例：$(basename src/foo.c src-1.0/bar.c hacks)返回值是“src/foo src-1.0/bar h<br />acks”。</p><p>名称：加后缀函数——addsuffix。<br />功能：把后缀&lt;suffix&gt;加到&lt;names&gt;中的每个单词后面。<br />返回：返回加过后缀的文件名序列。<br />示例：$(addsuffix .c,foo bar)返回值是“foo.c bar.c”。</p><br /><p>名称：加前缀函数——addprefix。<br />功能：把前缀&lt;prefix&gt;加到&lt;names&gt;中的每个单词后面。<br />返回：返回加过前缀的文件名序列。<br />示例：$(addprefix src/,foo bar)返回值是“src/foo src/bar”。</p><p>名称：连接函数——join。<br />功能：把&lt;list2&gt;中的单词对应地加到&lt;list1&gt;的单词后面。如果&lt;list1&gt;的单词个数要比&lt;<br />list2&gt;的多，那么，&lt;list1&gt;中的多出来的单词将保持原样。如果&lt;list2&gt;的单词个数要比<br />&lt;list1&gt;多，那么，&lt;list2&gt;多出来的单词将被复制到&lt;list2&gt;中。<br />返回：返回连接过后的字符串。<br />示例：$(join aaa bbb , 111 222 333)返回值是“aaa111 bbb222 333”。</p><p>四、foreach 函数<br />foreach 函数和别的函数非常的不一样。因为这个函数是用来做循环用的，Makefile中的<br />foreach函数几乎是仿照于Unix标准Shell（/bin /sh）中的for语句，或是C-Shell（/bin<br />/csh）中的foreach语句而构建的。它的语法是：<br />$(foreach &lt;var&gt;,&lt;list&gt;,&lt;text&gt; )<br />这个函数的意思是，把参数&lt;list&gt;中的单词逐一取出放到参数&lt;var&gt;所指定的变量中，然后再执行&lt;text&gt;所包含的表达式。每一次&lt;text&gt;会返回一个字符串，循环过程中，&lt;text&gt;的所返回的每个字符串会以空格分隔，最后当整个循环结束时，&lt;text&gt;所返回的每个字符串所组成的整个字符串（以空格分隔）将会是foreach函数的返回值。</p><p>五、if 函数<br />if函数很像GNU的make所支持的条件语句——ifeq（参见前面所述的章节），if函数的语法是：<br />$(if &lt;condition&gt;,&lt;then-part&gt; )<br />或是<br />$(if &lt;condition&gt;,&lt;then-part&gt;,&lt;else-part&gt; )</p><p>六、call函数<br />call函数是唯一一个可以用来创建新的参数化的函数。你可以写一个非常复杂的表达式，这个表达式中，你可以定义许多参数，然后你可以用call函数来向这个表达式传递参数。其语法是：<br />$(call &lt;expression&gt;,&lt;parm1&gt;,&lt;parm2&gt;,&lt;parm3&gt;...)<br />当 make执行这个函数时，&lt;expression&gt;参数中的变量，如$(1)，$(2)，$(3)等，会被参数&lt;parm1&gt;，&lt;parm2&gt;，&lt;parm3&gt;依次取代。而&lt;expression&gt;的返回值就是 call函数的返回值。</p><p>七、origin函数<br />注意，&lt;variable&gt;是变量的名字，不应该是引用。所以你最好不要在&lt;variable&gt;中使用“$”字符。Origin函数会以其返回值来告诉你这个变量的“出生情况”，下面，是origin函<br />数的返回值:<br />“undefined”<br />如果&lt;variable&gt;从来没有定义过，origin函数返回这个值“undefined”。<br />“default”</p><p>八、shell函数<br />shell 函数也不像其它的函数。顾名思义，它的参数应该就是操作系统Shell的命令。它和反引号“`”是相同的功能。这就是说，shell函数把执行操作系统命令后的输出作为函数<br />返回。于是，我们可以用操作系统命令以及字符串处理命令awk，sed等等命令来生成一个变量，如：<br />contents := $(shell cat foo)<br />files := $(shell echo *.c)<br />注意，这个函数会新生成一个Shell程序来执行命令，所以你要注意其运行性能，如果你的Makefile中有一些比较复杂的规则，并大量使用了这个函数，那么对于你的系统性能是有害的。特别是Makefile的隐晦的规则可能会让你的shell函数执行的次数比你想像的多得多。</p><p>九、控制make的函数<br />make提供了一些函数来控制make的运行。通常，你需要检测一些运行Makefile时的运行时信息，并且根据这些信息来决定，你是让make继续执行，还是停止。<br />$(error &lt;text ...&gt; )<br />产生一个致命的错误，&lt;text ...&gt;是错误信息。注意，error函数不会在一被使用就会产生错误信息，所以如果你把其定义在某个变量中，并在后续的脚本中使用这个变量，那么也<br />是可以的。</p><p>make 的运行<br />一、make的退出码<br />make命令执行后有三个退出码：<br />&#160; &#160; 0 —— 表示成功执行。<br />&#160; &#160; 1 —— 如果make运行时出现任何错误，其返回1。<br />&#160; &#160; 2 —— 如果你使用了make的“-q”选项，并且make使得一些目标不需要更新，那么返回2。</p><p>二、指定Makefile<br />前面我们说过，GNU make找寻默认的Makefile的规则是在当前目录下依次找三个文件——“GNUmakefile”、“makefile”和“Makefile”。其按顺序找这三个文件，一旦找到，就<br />开始读取这个文件并执行。<br />当前，我们也可以给make命令指定一个特殊名字的Makefile。要达到这个功能，我们要使用make的“-f”或是“--file”参数（“-- makefile”参数也行）。</p><p>三、指定目标<br />一般来说，make的最终目标是makefile中的第一个目标，而其它目标一般是由这个目标连带出来的。这是make的默认行为。当然，一般来说，你的 makefile中的第一个目标是由许多个目标组成，你可以指示make，让其完成你所指定的目标。要达到这一目的很简单，需在make命令后直接跟目标的名字就可以完成（如前面提到的“make clean”形式）任何在makefile中的目标都可以被指定成终极目标，但是除了以“- ”打头，或是包含了“=”的目标，因为有这些字符的目标，会被解析成命令行参数或是变量。甚至没有被我们明确写出来的目标也可以成为make的终极目标，也就是说，只要make可以找到其隐含规则推导规则，那么这个隐含目标同样可以被指定成终极目标。<br />有一个make的环境变量叫“MAKECMDGOALS”，这个变量中会存放你所指定的终极目标的列表，如果在命令行上，你没有指定目标，那么，这个变量是空值。这个变量可以让你使用在一些比较特殊的情形下。<br />&#160; &#160;“all”&#160; &#160; &#160; &#160; &#160; &#160; &#160; 这个伪目标是所有目标的目标，其功能一般是编译所有的目标。<br />&#160; &#160;“clean”&#160; &#160; &#160; &#160;这个伪目标功能是删除所有被make创建的文件。<br />&#160; &#160;“install”&#160; &#160; &#160; &#160;这个伪目标功能是安装已编译好的程序，其实就是把目标执行文件拷贝到指定的目标中去。<br />&#160; &#160;“print”&#160; &#160; &#160; &#160; &#160;这个伪目标的功能是例出改变过的源文件。<br />&#160; &#160;“tar”&#160; &#160; &#160; &#160; &#160; &#160; &#160;这个伪目标功能是把源程序打包备份。也就是一个tar文件。<br />&#160; &#160;“dist”&#160; &#160; &#160; &#160; &#160; &#160;这个伪目标功能是创建一个压缩文件，一般是把tar文件压成Z文件。或是gz文件。<br />&#160; &#160;“TAGS”&#160; &#160; &#160; &#160; 这个伪目标功能是更新所有的目标，以备完整地重编译使用。<br />&#160; &#160;“check”和“test”&#160; &#160; 这两个伪目标一般用来测试makefile的流程。</p><p>四、检查规则<br />有时候，我们不想让我们的makefile中的规则执行起来，我们只想检查一下我们的命令，或是执行的序列。于是我们可以使用make命令的下述参数：<br />“-n”<br />“--just-print”<br />“--dry-run”<br />“--recon”<br />不执行参数，这些参数只是打印命令，不管目标是否更新，把规则和连带规则下的命令打印出来，但不执行，这些参数对于我们调试makefile很有用处。<br />“-t”<br />“--touch”<br />这个参数的意思就是把目标文件的时间更新，但不更改目标文件。也就是说，make假装编译目标，但不是真正的编译目标，只是把目标变成已编译过的状态。<br />“-q”<br />“--question”<br />这个参数的行为是找目标的意思，也就是说，如果目标存在，那么其什么也不会输出，当然也不会执行编译，如果目标不存在，其会打印出一条出错信息。<br />“-W &lt;file&gt;”<br />“--what-if=&lt;file&gt;”<br />“--assume-new=&lt;file&gt;”<br />“--new-file=&lt;file&gt;”<br />这个参数需要指定一个文件。一般是是源文件（或依赖文件），Make会根据规则推导来运行依赖于这个文件的命令，一般来说，可以和“-n”参数一同使用，来查看这个依赖文件<br />所发生的规则命令。<br />另外一个很有意思的用法是结合“-p”和“-v”来输出makefile被执行时的信息</p><p>五、make的参数<br />“-b”<br />“-m”<br />这两个参数的作用是忽略和其它版本make的兼容性。<br />“-B”<br />“--always-make”<br />认为所有的目标都需要更新（重编译）。<br />“-C &lt;dir&gt;”<br />“--directory=&lt;dir&gt;”<br />指定读取makefile的目录。如果有多个“-C”参数，make的解释是后面的路径以前面的作为相对路径，并以最后的目录作为被指定目录。如：“make –C ~hchen/test –C prog”<br />等价于“make –C ~hchen/test/prog”。<br />“—debug[=&lt;options&gt;]”<br />输出make的调试信息。它有几种不同的级别可供选择，如果没有参数，那就是输出最简单的调试信息。下面是&lt;options&gt;的取值：<br />&#160; &#160; a —— 也就是all，输出所有的调试信息。（会非常的多）<br />&#160; &#160; b —— 也就是basic，只输出简单的调试信息。即输出不需要重编译的目标。<br />&#160; &#160; v —— 也就是verbose，在b选项的级别之上。输出的信息包括哪个makefile被解析，不需要被重编译的依赖文件（或是依赖目标）等。<br />&#160; &#160; i —— 也就是implicit，输出所以的隐含规则。<br />&#160; &#160; j —— 也就是jobs，输出执行规则中命令的详细信息，如命令的PID、返回码等。<br />&#160; &#160; m —— 也就是makefile，输出make读取makefile，更新makefile，执行makefile的信息。<br />“-d”<br />相当于“--debug=a”。<br />“-e”<br />“--environment-overrides”<br />指明环境变量的值覆盖makefile中定义的变量的值。<br />“-f=&lt;file&gt;”<br />“--file=&lt;file&gt;”<br />“--makefile=&lt;file&gt;”<br />指定需要执行的makefile。<br />“-h”<br />“--help”<br />显示帮助信息。<br />“-i”<br />“--ignore-errors”<br />在执行时忽略所有的错误。<br />“-I &lt;dir&gt;”<br />“--include-dir=&lt;dir&gt;”<br />指定一个被包含makefile的搜索目标。可以使用多个“-I”参数来指定多个目录。<br />“-j [&lt;jobsnum&gt;]”<br />“--jobs[=&lt;jobsnum&gt;]”<br />指同时运行命令的个数。如果没有这个参数，make运行命令时能运行多少就运行多少。如果有一个以上的“-j”参数，那么仅最后一个“-j”才是有效的。（注意这个参数在MS-D<br />OS中是无用的）<br />“-k”<br />“--keep-going”<br />出错也不停止运行。如果生成一个目标失败了，那么依赖于其上的目标就不会被执行了。<br />“-l &lt;load&gt;”<br />“--load-average[=&lt;load]”<br />“—max-load[=&lt;load&gt;]”<br />指定make运行命令的负载。<br />“-n”<br />“--just-print”<br />“--dry-run”<br />“--recon”<br />仅输出执行过程中的命令序列，但并不执行。<br />“-o &lt;file&gt;”<br />“--old-file=&lt;file&gt;”<br />“--assume-old=&lt;file&gt;”<br />不重新生成的指定的&lt;file&gt;，即使这个目标的依赖文件新于它。<br />“-p”<br />“--print-data-base”<br />输出makefile中的所有数据，包括所有的规则和变量。这个参数会让一个简单的makefile都会输出一堆信息。如果你只是想输出信息而不想执行 makefile，你可以使用“make -q<br />p”命令。如果你想查看执行makefile前的预设变量和规则，你可以使用“make –p –f /dev/null”。这个参数输出的信息会包含着你的makefile文件的文件名和行号，所以，用<br />这个参数来调试你的makefile会是很有用的，特别是当你的环境变量很复杂的时候。<br />“-q”<br />“--question”<br />不运行命令，也不输出。仅仅是检查所指定的目标是否需要更新。如果是0则说明要更新，如果是2则说明有错误发生。<br />“-r”<br />“--no-builtin-rules”<br />禁止make使用任何隐含规则。<br />“-R”<br />“--no-builtin-variabes”<br />禁止make使用任何作用于变量上的隐含规则。<br />“-s”<br />“--silent”<br />“--quiet”<br />在命令运行时不输出命令的输出。<br />“-S”<br />“--no-keep-going”<br />“--stop”<br />取消“-k”选项的作用。因为有些时候，make的选项是从环境变量“MAKEFLAGS”中继承下来的。所以你可以在命令行中使用这个参数来让环境变量中的“-k”选项失效。<br />“-t”<br />“--touch”<br />相当于UNIX的touch命令，只是把目标的修改日期变成最新的，也就是阻止生成目标的命令运行。<br />“-v”<br />“--version”<br />输出make程序的版本、版权等关于make的信息。<br />“-w”<br />“--print-directory”<br />输出运行makefile之前和之后的信息。这个参数对于跟踪嵌套式调用make时很有用。<br />“--no-print-directory”<br />禁止“-w”选项。<br />“-W &lt;file&gt;”<br />“--what-if=&lt;file&gt;”<br />“--new-file=&lt;file&gt;”<br />“--assume-file=&lt;file&gt;”<br />假定目标&lt;file&gt;需要更新，如果和“-n”选项使用，那么这个参数会输出该目标更新时的运行动作。如果没有“-n”那么就像运行UNIX的“touch”命令一样，使得&lt;file&gt;的修改时<br />间为当前时间。<br />“--warn-undefined-variables”<br />只要make发现有未定义的变量，那么就输出警告信息。</p><br /><p>隐含规则<br />————<br />隐含规则”也就是一种惯例，make会按照这种“惯例”心照不喧地来运行，那怕我们的Makefile中没有书写这样的规则。</p><p>一、使用隐含规则<br />如果要使用隐含规则生成你需要的目标，你所需要做的就是不要写出这个目标的规则。那么，make会试图去自动推导产生这个目标的规则和命令，如果make可以自动推导生成这个目标的规则和命令，那么这个行为就是隐含规则的自动推导。当然，隐含规则是make事先约定好的一些东西。<br />make 会在自己的“隐含规则”库中寻找可以用的规则，如果找到，那么就会使用。如果找不到，那么就会报错。<br />还有，在make的“隐含规则库”中，每一条隐含规则都在库中有其顺序，越靠前的则是越被经常使用的，所以，这会导致我们有些时候即使我们显示地指定了目标依赖，make也不会管。</p><p>二、隐含规则一览<br />这里我们将讲述所有预先设置（也就是make内建）的隐含规则，如果我们不明确地写下规则，那么，make就会在这些规则中寻找所需要规则和命令。当然，我们也可以使用make的参数“-r”或“--no-builtin-rules”选项来取消所有的预设置的隐含规则。<br />当然，即使是我们指定了“-r”参数，某些隐含规则还是会生效，因为有许多的隐含规则都是使用了“后缀规则”来定义的，所以，只要隐含规则中有“后缀列表 ”（也就一系统<br />定义在目标.SUFFIXES的依赖目标），那么隐含规则就会生效。默认的后缀列表是：.out,.a, .ln, .o, .c, .cc, .C, .p, .f, .F, .r, .y, .l, .s, .S, .mod, .sym, .def, .<br />h, .info, .dvi, .tex, .texinfo, .texi, .txinfo, .w, .ch .web, .sh, .elc, .el。</p><p>三、隐含规则使用的变量<br />在隐含规则中的命令中，基本上都是使用了一些预先设置的变量。你可以在你的makefile中改变这些变量的值，或是在make的命令行中传入这些值，或是在你的环境变量中设置这些值，无论怎么样，只要设置了这些特定的变量，那么其就会对隐含规则起作用。当然，你也可以利用make的“-R”或“--no– builtin-variables”参数来取消你所定义的变量<br />对隐含规则的作用。</p><p>下面是所有隐含规则中会用到的变量：<br />1、关于命令的变量。<br />AR&#160; &#160;函数库打包程序。默认命令是“ar”。<br />AS<br />汇编语言编译程序。默认命令是“as”。<br />CC<br />C语言编译程序。默认命令是“cc”。<br />CXX<br />C++语言编译程序。默认命令是“g++”。<br />CO<br />从 RCS文件中扩展文件程序。默认命令是“co”。<br />CPP<br />C程序的预处理器（输出是标准输出设备）。默认命令是“$(CC) –E”。<br />FC<br />Fortran 和 Ratfor 的编译器和预处理程序。默认命令是“f77”。<br />GET<br />从SCCS文件中扩展文件的程序。默认命令是“get”。<br />LEX<br />Lex方法分析器程序（针对于C或Ratfor）。默认命令是“lex”。<br />PC<br />Pascal语言编译程序。默认命令是“pc”。<br />YACC<br />Yacc文法分析器（针对于C程序）。默认命令是“yacc”。<br />YACCR<br />Yacc文法分析器（针对于Ratfor程序）。默认命令是“yacc –r”。<br />MAKEINFO<br />转换Texinfo源文件（.texi）到Info文件程序。默认命令是“makeinfo”。<br />TEX<br />从TeX源文件创建TeX DVI文件的程序。默认命令是“tex”。<br />TEXI2DVI<br />从Texinfo源文件创建军TeX DVI 文件的程序。默认命令是“texi2dvi”。<br />WEAVE<br />转换Web到TeX的程序。默认命令是“weave”。<br />CWEAVE<br />转换C Web 到 TeX的程序。默认命令是“cweave”。<br />TANGLE<br />转换Web到Pascal语言的程序。默认命令是“tangle”。<br />CTANGLE<br />转换C Web 到 C。默认命令是“ctangle”。<br />RM<br />删除文件命令。默认命令是“rm –f”。</p><br /><p>2、关于命令参数的变量<br />下面的这些变量都是相关上面的命令的参数。如果没有指明其默认值，那么其默认值都是<br />空。<br />ARFLAGS<br />函数库打包程序AR命令的参数。默认值是“rv”。<br />ASFLAGS<br />汇编语言编译器参数。（当明显地调用“.s”或“.S”文件时）。<br />CFLAGS<br />C语言编译器参数。<br />CXXFLAGS<br />C++语言编译器参数。<br />COFLAGS<br />RCS命令参数。<br />CPPFLAGS<br />C预处理器参数。（ C 和 Fortran 编译器也会用到）。<br />FFLAGS<br />Fortran语言编译器参数。<br />GFLAGS<br />SCCS “get”程序参数。<br />LDFLAGS<br />链接器参数。（如：“ld”）<br />LFLAGS<br />Lex文法分析器参数。<br />PFLAGS<br />Pascal语言编译器参数。<br />RFLAGS<br />Ratfor 程序的Fortran 编译器参数。<br />YFLAGS<br />Yacc文法分析器参数。</p><p>四、隐含规则链<br />有些时候，一个目标可能被一系列的隐含规则所作用。例如，一个[.o]的文件生成，可能会是先被Yacc的[.y]文件先成[.c]，然后再被C的编译器生成。我们把这一系列的隐含规则<br />叫做“隐含规则链”</p><p>五、定义模式规则<br />你可以使用模式规则来定义一个隐含规则。一个模式规则就好像一个一般的规则，只是在规则中，目标的定义需要有&quot;%&quot;字符。&quot;%&quot;的意思是表示一个或多个任意字符。在依赖目标中同样可以使用&quot;%&quot;，只是依赖目标中的&quot;%&quot;的取值，取决于其目标。</p><p>1、模式规则介绍<br />模式规则中，至少在规则的目标定义中要包含&quot;%&quot;，否则，就是一般的规则。目标中的&quot;%&quot;定义表示对文件名的匹配，&quot;%&quot;表示长度任意的非空字符串。例如：&quot;%.c&quot;表示以&quot;.c&quot;结尾的文件名（文件名的长度至少为3），而&quot;s.%.c&quot;则表示以&quot;s.&quot;开头，&quot;.c&quot;结尾的文件名（文件名的长度至少为 5）</p><p>2、模式规则示例<br />%.o : %.c<br />$(CC) -c $(CFLAGS) $(CPPFLAGS) $&lt; -o $@<br />其中，&quot;$@&quot;表示所有的目标的挨个值，&quot;$&lt;&quot;表示了所有依赖目标的挨个值。这些奇怪的变<br />量我们叫&quot;自动化变量&quot;，后面会详细讲述</p><p>3、自动化变量<br />$@<br />表示规则中的目标文件集。在模式规则中，如果有多个目标，那么，&quot;$@&quot;就是匹配于目标中模式定义的集合。<br />$%<br />仅当目标是函数库文件中，表示规则中的目标成员名。例如，如果一个目标是&quot;foo.a(bar.o)&quot;，那么，&quot;$%&quot;就是&quot;bar.o&quot;，&quot;$@&quot;就是&quot;foo.a&quot;。如果目标不是函数库文件（Unix下是<br />[.a]，Windows下是[.lib]），那么，其值为空。</p><p>$&lt;<br />依赖目标中的第一个目标名字。如果依赖目标是以模式（即&quot;%&quot;）定义的，那么&quot;$&lt;&quot;将是符合模式的一系列的文件集。注意，其是一个一个取出来的。<br />$?<br />所有比目标新的依赖目标的集合。以空格分隔。<br />$^<br />所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的，那个这个变量会去除重复的依赖目标，只保留一份。<br />$+<br />这个变量很像&quot;$^&quot;，也是所有依赖目标的集合。只是它不去除重复的依赖目标。<br />$*<br />这个变量表示目标模式中&quot;%&quot;及其之前的部分。如果目标是&quot;dir/a.foo.b&quot;，并且目标的模式是&quot;a.%.b&quot;，那么，&quot;$*&quot;的值就是&quot;dir /a.foo&quot;。这个变量对于构造有关联的文件名是比<br />较有较。如果目标中没有模式的定义，那么&quot;$*&quot;也就不能被推导出，但是，如果目标文件的后缀是 make所识别的，那么&quot;$*&quot;就是除了后缀的那一部分。例如：如果目标是&quot;foo.c&quot;<br />，因为&quot;.c&quot;是make所能识别的后缀名，所以，&quot;$*&quot;的值就是&quot;foo&quot;。这个特性是GNU make的，很有可能不兼容于其它版本的make，所以，你应该尽量避免使用&quot;$*&quot;，除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的，那么&quot;$*&quot;就是空值。<br />当你希望只对更新过的依赖文件进行操作时，&quot;$?&quot;在显式规则中很有用，<br />$(@D)<br />表示&quot;$@&quot;的目录部分（不以斜杠作为结尾），如果&quot;$@&quot;值是&quot;dir/foo.o&quot;，那么&quot;$(@D)&quot;就是&quot;dir&quot;，而如果&quot;$@&quot;中没有包含斜杠的话，其值就是&quot;.&quot;（当前目录）。<br />$(@F)<br />表示&quot;$@&quot;的文件部分，如果&quot;$@&quot;值是&quot;dir/foo.o&quot;，那么&quot;$(@F)&quot;就是&quot;foo.o&quot;，&quot;$(@F)&quot;相当于函数&quot;$(notdir $@)&quot;。<br />&quot;$(*D)&quot;<br />&quot;$(*F)&quot;<br />和上面所述的同理，也是取文件的目录部分和文件部分。对于上面的那个例子，&quot;$(*D)&quot;返回&quot;dir&quot;，而&quot;$(*F)&quot;返回&quot;foo&quot;<br />&quot;$(%D)&quot;<br />&quot;$(%F)&quot;<br />分别表示了函数包文件成员的目录部分和文件部分。这对于形同&quot;archive(member)&quot;形式的目标中的&quot;member&quot;中包含了不同的目录很有用。<br />&quot;$(&lt;D)&quot;<br />&quot;$(&lt;F)&quot;<br />分别表示依赖文件的目录部分和文件部分。<br />&quot;$(^D)&quot;<br />&quot;$(^F)&quot;<br />分别表示所有依赖文件的目录部分和文件部分。（无相同的）<br />&quot;$(+D)&quot;<br />&quot;$(+F)&quot;<br />分别表示所有依赖文件的目录部分和文件部分。（可以有相同的）<br />&quot;$(?D)&quot;<br />&quot;$(?F)&quot;<br />分别表示被更新的依赖文件的目录部分和文件部分。<br />最后想提醒一下的是，对于&quot;$&lt;&quot;，为了避免产生不必要的麻烦，我们最好给$后面的那个特定字符都加上圆括号，比如，&quot;$(&lt; )&quot;就要比&quot;$&lt;&quot;要好一些。<br />还得要注意的是，这些变量只使用在规则的命令中，而且一般都是&quot;显式规则&quot;和&quot;静态模式规则&quot;（参见前面&quot;书写规则&quot;一章）。其在隐含规则中并没有意义。</p><p>4、模式的匹配<br />一般来说，一个目标的模式有一个有前缀或是后缀的&quot;%&quot;，或是没有前后缀，直接就是一个&quot;%&quot;。因为&quot;%&quot;代表一个或多个字符，所以在定义好了的模式中，我们把&quot;%&quot;所匹配的内容叫做&quot;茎&quot;，例如&quot;%.c&quot;所匹配的文件&quot;test.c&quot;中&quot;test&quot;就是&quot;茎&quot;。因为在目标和依赖目标中同时有&quot;%&quot;时，依赖目标的&quot;茎&quot;会传给目标，当做目标中的&quot;茎&quot;。<br />当一个模式匹配包含有斜杠（实际也不经常包含）的文件时，那么在进行模式匹配时，目录部分会首先被移开，然后进行匹配，成功后，再把目录加回去。在进行&quot;茎&quot;的传递时，我们需要知道这个步骤。例如有一个模式&quot;e%t&quot;，文件&quot;src/eat&quot; 匹配于该模式，于是&quot;src/a&quot;就是其&quot;茎&quot;，如果这个模式定义在依赖目标中，而被依赖于这个模式的目标中又有个模式&quot;c%r&quot;，那么，目标就是&quot;src/car&quot;。（&quot;茎&quot;被传递）</p><p>5、重载内建隐含规则<br />你可以重载内建的隐含规则（或是定义一个全新的），例如你可以重新构造和内建隐含规则不同的命令，如：<br />%.o : %.c<br />$(CC) -c $(CPPFLAGS) $(CFLAGS) -D$(date)<br />你可以取消内建的隐含规则，只要不在后面写命令就行。如：<br />%.o : %.s<br />同样，你也可以重新定义一个全新的隐含规则，其在隐含规则中的位置取决于你在哪里写下这个规则。朝前的位置就靠前。</p><p>六、老式风格的&quot;后缀规则&quot;<br />后缀规则是一个比较老式的定义隐含规则的方法。后缀规则会被模式规则逐步地取代。因为模式规则更强更清晰。为了和老版本的Makefile兼容，GNU make同样兼容于这些东西。后缀规则有两种方式：&quot;双后缀&quot;和&quot;单后缀&quot;。<br />双后缀规则定义了一对后缀：目标文件的后缀和依赖目标（源文件）的后缀。如&quot;.c.o&quot;相当于&quot;%o : %c&quot;。单后缀规则只定义一个后缀，也就是源文件的后缀。如&quot;.c&quot;相当于&quot;% : %.c&quot;。<br />后缀规则中所定义的后缀应该是make所认识的，如果一个后缀是make所认识的，那么这个规则就是单后缀规则，而如果两个连在一起的后缀都被make所认识，那就是双后缀规则。例如：&quot;.c&quot;和&quot;.o&quot;都是make所知道。因而，如果你定义了一个规则是&quot;.c.o&quot;那么其就是双后缀规则，意义就是&quot;.c&quot; 是源文件的后缀，&quot;.o&quot;是目标文件的后缀。</p><p>七、隐含规则搜索算法<br />比如我们有一个目标叫 T。下面是搜索目标T的规则的算法。请注意，在下面，我们没有提到后缀规则，原因是，所有的后缀规则在Makefile被载入内存时，会被转换成模式规则。如果目标是&quot;archive(member)&quot;的函数库文件模式，那么这个算法会被运行两次，第一次是找目标T，如果没有找到的话，那么进入第二次，第二次会把&quot;member&quot;当作T来搜索。<br />&#160; &#160; 1、把T的目录部分分离出来。叫D，而剩余部分叫N。（如：如果T是&quot;src/foo.o&quot;，那么，D就是&quot;src/&quot;，N就是&quot;foo.o&quot;）<br />&#160; &#160; 2、创建所有匹配于T或是N的模式规则列表。<br />&#160; &#160; 3、如果在模式规则列表中有匹配所有文件的模式，如&quot;%&quot;，那么从列表中移除其它的模式。<br />&#160; &#160; 4、移除列表中没有命令的规则。<br />&#160; &#160; 5、对于第一个在列表中的模式规则：<br />&#160; &#160; &#160; &#160; 1）推导其&quot;茎&quot;S，S应该是T或是N匹配于模式中&quot;%&quot;非空的部分。<br />&#160; &#160; &#160; &#160; 2）计算依赖文件。把依赖文件中的&quot;%&quot;都替换成&quot;茎&quot;S。如果目标模式中没有包含斜框字符，而把D加在第一个依赖文件的开头。<br />&#160; &#160; &#160; &#160; 3）测试是否所有的依赖文件都存在或是理当存在。（如果有一个文件被定义成另外一个规则的目标文件，或者是一个显式规则的依赖文件，那么这个文件就叫&quot;理当存在&quot;）<br />&#160; &#160; &#160; &#160; 4）如果所有的依赖文件存在或是理当存在，或是就没有依赖文件。那么这条规则将被采用，退出该算法。<br />&#160; &#160; 6、如果经过第5步，没有模式规则被找到，那么就做更进一步的搜索。对于存在于列表中的第一个模式规则：<br />&#160; &#160; &#160; &#160; 1）如果规则是终止规则，那就忽略它，继续下一条模式规则。<br />&#160; &#160; &#160; &#160; 2）计算依赖文件。（同第5步）<br />&#160; &#160; &#160; &#160; 3）测试所有的依赖文件是否存在或是理当存在。<br />&#160; &#160; &#160; &#160; 4）对于不存在的依赖文件，递归调用这个算法查找他是否可以被隐含规则找到。<br />&#160; &#160; &#160; &#160; 5）如果所有的依赖文件存在或是理当存在，或是就根本没有依赖文件。那么这条规则被采用，退出该算法。<br />&#160; &#160; 7、如果没有隐含规则可以使用，查看&quot;.DEFAULT&quot;规则，如果有，采用，把&quot;.DEFAULT&quot;的命令给T使用。<br />一旦规则被找到，就会执行其相当的命令，而此时，我们的自动化变量的值才会生成。</p><br /><p>使用make更新函数库文件<br />———————————<br />一、函数库文件的成员<br />一个函数库文件由多个文件组成。你可以以如下格式指定函数库文件及其组成：<br />archive(member)<br />这个不是一个命令，而一个目标和依赖的定义。一般来说，这种用法基本上就是为了&quot;ar&quot;命令来服务的。如：<br />foolib(hack.o) : hack.o<br />ar cr foolib hack.o<br />如果要指定多个member，那就以空格分开，如：<br />foolib(hack.o kludge.o)</p><p>二、函数库成员的隐含规则<br />当 make搜索一个目标的隐含规则时，一个特殊的特性是，如果这个目标是&quot;a(m)&quot;形式的，其会把目标变成&quot;(m)&quot;。于是，如果我们的成员是&quot;%.o&quot; 的模式定义，并且如果我们使用&quot;make foo.a(bar.o)&quot;的形式调用Makefile时，隐含规则会去找&quot;bar.o&quot;的规则，如果没有定义bar.o的规则，那么内建隐含规则生效，make会去找bar.c文件来生成bar.o，如果找得到的话，make执行的命令大致如下：<br />cc -c bar.c -o bar.o<br />ar r foo.a bar.o<br />rm -f bar.o<br />还有一个变量要注意的是&quot;$%&quot;，这是专属函数库文件的自动化变量，有关其说明请参见&quot;自动化变量&quot;一节。</p><p>三、函数库文件的后缀规则<br />你可以使用&quot;后缀规则&quot;和&quot;隐含规则&quot;来生成函数库打包文件，如：<br />.c.a:<br />$(CC) $(CFLAGS) $(CPPFLAGS) -c $&lt; -o $*.o<br />$(AR) r $@ $*.o<br />$(RM) $*.o<br />其等效于：<br />(%.o) : %.c<br />$(CC) $(CFLAGS) $(CPPFLAGS) -c $&lt; -o $*.o<br />$(AR) r $@ $*.o<br />$(RM) $*.o</p><p>四、注意事项<br />在进行函数库打包文件生成时，请小心使用make的并行机制（&quot;-j&quot;参数）。如果多个ar命令在同一时间运行在同一个函数库打包文件上，就很有可以损坏这个函数库文件。所以，在make未来的版本中，应该提供一种机制来避免并行操作发生在函数打包文件上。<br />但就目前而言，你还是应该不要尽量不要使用&quot;-j&quot;参数。</p>]]></description>
			<author><![CDATA[dummy@example.com (batsom)]]></author>
			<pubDate>Fri, 26 Aug 2022 06:04:51 +0000</pubDate>
			<guid>http://www.gentoo-zh.org/viewtopic.php?pid=310#p310</guid>
		</item>
	</channel>
</rss>
