Makefile学习与使用-一篇文章带你了解
 2021.01.08    |      嵌入式学习    |     AilsonJack    |     暂无评论    |     275 views
By: Ailson Jack
Date: 2021-01-08
个人博客: http://www.only2fire.com/
<p class="artical_littlestyle1">1.Makefile简介</p><p style="text-indent: 2em;">Makefile是和make工具一起配合使用的,用于组织管理项目源代码的编译和链接。<br/></p><p style="text-indent: 2em;">make工具用于找出修改过的文件,根据依赖关系,找出受影响的相关文件,最后按照规则单独编译这些文件。<br/></p><p style="text-indent: 2em;">Makefile文件则记录依赖关系和编译规则。<br/></p><p style="text-indent: 2em;">Makefile的本质:无论多么复杂的语法,都是为了更好地解决项目文件之间的依赖关系。<br/></p><p class="artical_littlestyle2">2.Makefile规则介绍</p><p style="text-indent: 2em;">Makefile的一个规则由目标、依赖、命令组成,其语法结构如下所示:<br/></p><p style="text-indent: 0em;">目标:依赖的文件或者是其他目标<br/>&lt;tab&gt;命令1<br/>&lt;tab&gt;命令2<br/>&lt;tab&gt;...<br/></p><p style="text-indent: 2em;">一个规则可以有多个命令行,每一条命令占一行。注意:每一个命令行必须以[Tab]字符开始,[Tab]字符告诉 make 此行是一个命令行。make 按照命令完成相应的动作。这也是书写 Makefile 中容易产生,而且比较隐蔽的错误。</p><p class="artical_littlestyle3">3.Makefile伪目标</p><p style="text-indent: 2em;">伪目标不代表一个真正的文件名,在执行 make 时可以指定这个目标来执行其所在规则定义的命令,有时也可以将一个伪目标称为标签。<br/></p><p style="text-indent: 2em;">使用伪目标有两点原因:</p><p style="text-indent: 2em;">(1).避免在我们的 Makefile 中定义的只执行命令的目标(此目标的目的是为了执行一系列命令,而不需要创建这个目标)和工作目录下的实际文件出现名字冲突。</p><p style="text-indent: 2em;">(2).提高执行 make 时的效率,特别是对于一个大型的工程来说,编译的效率也许你同样关心。<br/></p><p style="text-indent: 2em;"><strong>将一个目标声明为伪目标的方法是将它作为特殊目标&quot;.PHONY&quot;的依赖。在书写伪目标规则时,首先需要声明目标是一个伪目标,之后才是伪目标的规则定义。</strong>目标&quot;clean&quot;的书写格式应该如下:<br/></p><pre class="brush:plain;toolbar:false PrismJs">.PHONY:&nbsp;clean clean: &nbsp;&nbsp;&nbsp;&nbsp;rm&nbsp;*.o&nbsp;temp</pre><p class="artical_littlestyle4">4.Makefile的变量</p><p style="text-indent: 2em;">在 Makefile 中,变量是一个名字(像是 C 语言中的宏),代表一个文本字符串。在 Makefile 的目标、依赖、命令中引用变量的地方,变量会被它的值所取代。<br/></p><p style="text-indent: 2em;">变量名是不包括&quot;:&quot;、&quot;#&quot;、&quot;=&quot;、前置空白和尾空白的任何字符串。变量名最好使用字母、数字和下划线进行定义,对于其他的特殊符号不建议使用到变量名的定义中。<br/></p><p style="text-indent: 2em;">变量名是大小写敏感的。变量&quot;foo&quot;、&quot;Foo&quot;和&quot;FOO&quot;指的是三个不同的变量。<br/></p><p style="text-indent: 2em;">变量的引用方式是:&quot;$(VARIABLE_NAME)&quot;或者&quot;${VARIABLE_NAME}&quot;来引用一个变量的定义。<br/></p><p style="text-indent: 2em;">Makefile的变量包含系统环境变量,自定义变量和自动化变量。<br/></p><p style="text-indent: 0em;"><strong>4.1.系统环境变量</strong><br/></p><p style="text-indent: 2em;">使用系统环境变量需要注意以下几点:<br/></p><p style="text-indent: 2em;">(1).在 Makefile 中对一个变量的定义或者以 make 命令行形式对一个变量的定义,都将覆盖同名的系统环境变量(注意:它并不改变系统环境变量定义,被修改的环境变量只在 make 执行过程有效)。而 make 使用&quot;-e&quot;参数时,Makefile 和命令行定义的变量不会覆盖同名的环境变量,make 将使用系统环境变量中这些变量的定义值。<br/></p><p style="text-indent: 2em;">(2).make的递归调用中,所有的系统环境变量会被传递给下一级make。默认情况下,只有系统环境变量和通过命令行方式定义的变量才会被传递给子make进程。在Makefile中定义的普通变量需要传递给子make时需要使用&quot;export&quot;指示符来对它声明。</p><p><strong>4.2.自定义变量</strong><br/></p><p style="text-indent: 2em;">在Makefile中自定义变量比较简单,下面简单的区分下各个符号的含义:<br/></p><p style="text-indent: 2em;">(1).延迟赋值:&quot;=&quot;;<br/></p><p style="text-indent: 2em;">使用&quot;=&quot;号定义变量时,如果定义的变量存在对其他变量的引用,那么定义的变量并不会立即展开对其他变量的引用,而是在真正使用到该变量时才进行展开。例如下述例子中,只有在echo &quot;$(B)&quot;时才会去展开$(A),虽然在定义变量B之前A的值为123,但是在执行echo &quot;$(B)&quot;时,A的最终值为456,因此会输出456。<br/></p><p style="text-indent: 2em;">(2).立即赋值:&quot;:=&quot;;<br/></p><p style="text-indent: 2em;">立即赋值是相对于延迟赋值的,使用&quot;:=&quot;号定义变量时,如果定义的变量存在对其他变量的引用,那么定义的变量会立即展开对其他变量的引用。可以结合下面的例子进行理解。<br/></p><p style="text-indent: 2em;">(3).为空赋值(条件赋值):&quot;?=&quot;;<br/></p><p style="text-indent: 2em;">使用&quot;?=&quot;号定义变量时,只有定义的变量在之前没有赋值的情况下才会对这个变量进行赋值。可以结合下面的例子进行理解。<br/></p><p style="text-indent: 2em;">(4).追加赋值:&quot;+=&quot;;<br/></p><p style="text-indent: 2em;">使用&quot;+=&quot;符号,可以对变量的值进行追加。可以结合下面的例子进行理解。<br/>例子:</p><pre class="brush:plain;toolbar:false PrismJs">#&nbsp;延迟赋值 A=123 B=$(A) A=456 #&nbsp;立即赋值 C=123 D:=$(C) C=456 #&nbsp;空赋值 E?=123 E?=456 #&nbsp;追加赋值 F?=123 F+=456 .PHONY:all all: &nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;&quot;$(B)&quot;&nbsp;#结果:456 &nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;&quot;$(D)&quot;&nbsp;#结果:123 &nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;&quot;$(E)&quot;&nbsp;#结果:123 &nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;&quot;$(F)&quot;&nbsp;#结果:123&nbsp;456</pre><p><strong>4.3.自动化变量</strong><br/></p><p style="text-indent: 2em;">Makefile中常用的自动化变量如下描述:<br/>$&lt;:第一个依赖文件;<br/>$^:全部的依赖文件;<br/>$@:目标;</p><p class="artical_littlestyle1">5.Makefile条件分支</p><p style="text-indent: 2em;">条件分支语法如下:<br/></p><pre class="brush:plain;toolbar:false PrismJs">ifeq&nbsp;(var1,var2) &nbsp;&nbsp;&nbsp;&nbsp;... else &nbsp;&nbsp;&nbsp;&nbsp;... endif</pre><pre class="brush:plain;toolbar:false PrismJs">ifneq&nbsp;(var1,var2) &nbsp;&nbsp;&nbsp;&nbsp;... else &nbsp;&nbsp;&nbsp;&nbsp;... endif</pre><p style="text-indent: 2em;"><span style="color: rgb(51, 51, 51); font-family: &quot;Open Sans&quot;, &quot;Clear Sans&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 4; text-align: start; text-indent: 0px; text-transform: none; white-space: pre-wrap; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;">例子:</span></p><pre class="brush:plain;toolbar:false PrismJs">ARCH&nbsp;?=&nbsp;x86 ifeq&nbsp;($(ARCH),&nbsp;x86) &nbsp;&nbsp;&nbsp;&nbsp;CC=gcc else &nbsp;&nbsp;&nbsp;&nbsp;CC=arm-linux-gnueabihf-gcc endif</pre><p class="artical_littlestyle2">6.Makefile的常用函数</p><p style="text-indent: 2em;">Makefile还是提供了比较多的函数供我们使用,这里介绍下常用的几个函数。<br/><strong>6.1.patsubst</strong><br/><strong>$(patsubst PATTERN,REPLACEMENT,TEXT)</strong><br/></p><p style="text-indent: 2em;">函数名称:模式替换函数 - patsubst。<br/></p><p style="text-indent: 2em;">函数功能:搜索&quot;TEXT&quot;中以空格分开的单词,将符合模式&quot;PATTERN&quot;替换为&quot;REPLACEMENT&quot;。参数&quot;PATTERN&quot;中可以使用模式通配符&quot;%&quot;来代表一个单词中的若干字符。如果参数&quot;REPLACEMENT&quot;中也包含一个&quot;%&quot;,那么&quot;REPLACEMENT&quot;中的&quot;%&quot;将是&quot;PATTERN&quot;中的那个&quot;%&quot;所代表的字符串。在&quot;PATTERN&quot;和&quot;REPLACEMENT&quot;中,只有第一个&quot;%&quot;被作为模式字符来处理,之后出现的不再作模式字符(作为一个字符)。在参数中如果需要将第一个出现的&quot;%&quot;作为字符本身而不作为模式字符时,可使用反斜杠&quot;\&quot;进行转义处理。<br/></p><p style="text-indent: 2em;">返回值:替换后的新字符串。 函数说明:参数&quot;TEXT&quot;单词之间的多个空格在处理时被合并为一个空格,并忽略前导和结尾空格。<br/></p><p style="text-indent: 2em;">如果感觉上述的文字说明有点不太好理解,那么看看下面的示例吧,之后再结合示例看上面的文字说明应该就比较好理解了,示例如下:</p><pre class="brush:plain;toolbar:false PrismJs">.PHONY:all all: &nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;&quot;$(patsubst&nbsp;%.c,%.o,x.c.c&nbsp;bar.c)&quot;&nbsp;#结果:x.c.o&nbsp;bar.o</pre><p style="text-indent: 2em;"><span style="color: rgb(51, 51, 51); font-family: &quot;Open Sans&quot;, &quot;Clear Sans&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 4; text-align: start; text-indent: 0px; text-transform: none; white-space: pre-wrap; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;">上述示例的运行结果:把字串&quot;x.c.c bar.c&quot;中以.c 结尾的单词替换成以.o 结尾的字符。函数的返回结果是&quot;x.c.o bar.o&quot;。</span></p><p style="text-indent: 0em;"><strong>6.2.notdir<br/>$(notdir NAMES…)</strong><br/></p><p style="text-indent: 2em;">函数名称:取文件名函数 - notdir。</p><p style="text-indent: 2em;">函数功能:从文件名序列&quot;NAMES…&quot;中取出非目录部分。目录部分是指最后一个斜线(&quot;/&quot;)(包括斜线)之前的部分。删除所有文件名中的目录部分,只保留非目录部分。</p><p style="text-indent: 2em;">返回值:文件名序列&quot;NAMES…&quot;中每一个文件的非目录部分。</p><p style="text-indent: 2em;">函数说明:如果&quot;NAMES…&quot;中存在不包含斜线的文件名,则不改变这个文件名。以反斜线结尾的文件名,是用空串代替,因此当&quot;NAMES…&quot;中存在多个这样的文件名时,返回结果中分割各个文件名的空格数目将不确定!这是此函数的一个缺陷。<br/>示例:</p><pre class="brush:plain;toolbar:false PrismJs">.PHONY:all all: &nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;&quot;$(notdir&nbsp;src/foo.c&nbsp;hacks)&quot;&nbsp;#结果:foo.c&nbsp;hacks</pre><p style="text-indent: 0em;"><strong>6.3.wildcard<br/>$(wildcard PATTERN)</strong><br/></p><p style="text-indent: 2em;">函数名称:获取匹配模式文件名函数 - wildcard。</p><p style="text-indent: 2em;">函数功能:列出当前目录下所有符合模式&quot;PATTERN&quot;格式的文件名。</p><p style="text-indent: 2em;">返回值:空格分割的、存在当前目录下的所有符合模式&quot;PATTERN&quot;的文件名。</p><p style="text-indent: 2em;">函数说明:&quot;PATTERN&quot;使用shell可识别的通配符,包括&quot;?&quot;(单字符)、&quot;*&quot;(多字符)等。<br/>示例:</p><pre class="brush:plain;toolbar:false PrismJs">.PHONY:all all: &nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;&quot;$(wildcard&nbsp;*.c)&quot;&nbsp;#结果:当前目录下所有.c&nbsp;源文件列表</pre><p style="text-indent: 0em;"><strong>6.4.foreach<br/>$(foreach VAR,LIST,TEXT)</strong><br/></p><p style="text-indent: 2em;">函数&quot;foreach&quot;不同于其它函数。它是一个循环函数。类似于 Linux shell 中的for 语句。<br/></p><p style="text-indent: 2em;">函数名称:循环函数 - foreach。<br/></p><p style="text-indent: 2em;">函数功能:这个函数的工作过程是这样的:如果需要(存在变量或者函数的引用),首先展开变量&quot;VAR&quot;和&quot;LIST&quot;的引用;而表达式&quot;TEXT&quot;中的变量引用不展开。执行时把&quot;LIST&quot;中使用空格分割的单词依次取出赋值给变量&quot;VAR&quot;,然后执行&quot;TEXT&quot;表达式。重复直到&quot;LIST&quot;的最后一个单词(为空时结束)。&quot;TEXT&quot;中的变量或者函数引用在执行时才被展开,因此如果在&quot;TEXT&quot;中存在对&quot;VAR&quot;的引用,那么&quot;VAR&quot;的值在每一次展开式将会到的不同的值。<br/></p><p style="text-indent: 2em;">返回值:空格分割的多次表达式&quot;TEXT&quot;的计算的结果。<br/></p><p style="text-indent: 2em;">备注:函数中参数&quot;VAR&quot;是一个局部的临时变量,它只在&quot;foreach&quot;函数的上下文中有效,它的定义不会影响其它部分定义的同名&quot;VAR&quot;变量的值。<br/>示例:</p><pre class="brush:plain;toolbar:false PrismJs">.PHONY:all dirs&nbsp;:=&nbsp;a&nbsp;b&nbsp;c&nbsp;d files&nbsp;:=&nbsp;$(foreach&nbsp;dir,$(dirs),$(wildcard&nbsp;$(dir)/*)) all: &nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;&quot;$(files)&quot;&nbsp;#结果:files是a&nbsp;b&nbsp;c&nbsp;d&nbsp;4&nbsp;个目录下所有文件组成的文件列表.</pre><p style="text-indent: 2em;"><span class="md-plain md-expand" style="box-sizing: border-box; color: rgb(51, 51, 51); font-family: &quot;Open Sans&quot;, &quot;Clear Sans&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 4; text-align: start; text-indent: 0px; text-transform: none; white-space: pre-wrap; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">示例分析:例子中,&quot;TEXT&quot;的表达式为&quot;$(wildcard $(dir)/</span><span style="box-sizing: border-box; color: rgb(51, 51, 51); font-family: &quot;Open Sans&quot;, &quot;Clear Sans&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 4; text-align: start; text-indent: 0px; text-transform: none; white-space: pre-wrap; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">*</span><span class="md-plain" style="box-sizing: border-box; color: rgb(51, 51, 51); font-family: &quot;Open Sans&quot;, &quot;Clear Sans&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 4; text-align: start; text-indent: 0px; text-transform: none; white-space: pre-wrap; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">)&quot;。表达式第一次执行时将展开为&quot;$(wildcard a/</span><span style="box-sizing: border-box; color: rgb(51, 51, 51); font-family: &quot;Open Sans&quot;, &quot;Clear Sans&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 4; text-align: start; text-indent: 0px; text-transform: none; white-space: pre-wrap; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">*</span><span class="md-plain" style="box-sizing: border-box; color: rgb(51, 51, 51); font-family: &quot;Open Sans&quot;, &quot;Clear Sans&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 4; text-align: start; text-indent: 0px; text-transform: none; white-space: pre-wrap; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">)&quot;;第二次执行时将展开为&quot;$(wildcard b/</span><span style="box-sizing: border-box; color: rgb(51, 51, 51); font-family: &quot;Open Sans&quot;, &quot;Clear Sans&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 4; text-align: start; text-indent: 0px; text-transform: none; white-space: pre-wrap; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">*</span><span class="md-plain md-expand" style="box-sizing: border-box; color: rgb(51, 51, 51); font-family: &quot;Open Sans&quot;, &quot;Clear Sans&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 4; text-align: start; text-indent: 0px; text-transform: none; white-space: pre-wrap; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">)&quot;;第三次展开为&quot;$(wildcard c/*)&quot;; ...;以此类推。</span></p><p class="artical_littlestyle3">7.其他</p><p style="text-indent: 2em;">默认规则:.o文件默认使用对应的.c文件来进行编译。</p>
欢迎关注博主的公众号呀,精彩内容随时掌握:
热情邀请仔细浏览下博客中的广告,万一有对自己有用或感兴趣的呢。◕ᴗ◕。。
如果这篇文章对你有帮助,记得点赞和关注博主就行了^_^,当然了能够赞赏博主,那就非常感谢啦!
注: 转载请注明出处,谢谢!^_^
暂无评论,要不要来个沙发
发表评论

 
Copyright © 2015~2023  说好一起走   保留所有权利   |  百度统计  蜀ICP备15004292号