Makefile的介绍与使用

1 target ... : prerequisites ... //目标文件:所依赖的文件
2     command   //该target要执行的命令(任意的shell命令)
3     ...
4     ...

这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件, 其生成规则定义在command中。说白一点就是说:

prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。

如果一个工程有3个头文件和8个c文件,为了完成前面所述的那三个规则,我们的makefile 应该是下面的这个样子的。

edit : main.o kbd.o command.o display.o \insert.o search.o files.o utils.occ -o edit main.o kbd.o command.o display.o \insert.o search.o files.o utils.omain.o : main.c defs.hcc -c main.c
kbd.o : kbd.c defs.h command.hcc -c kbd.c
command.o : command.c defs.h command.hcc -c command.c
display.o : display.c defs.h buffer.hcc -c display.c
insert.o : insert.c defs.h buffer.hcc -c insert.c
search.o : search.c defs.h buffer.hcc -c search.c
files.o : files.c defs.h buffer.h command.hcc -c files.c
utils.o : utils.c defs.hcc -c utils.c
clean :rm edit main.o kbd.o command.o display.o \insert.o search.o files.o utils.o

 

反斜杠( \ )是换行符的意思。这样比较便于makefile的阅读。我们可以把这个内容保存在名字 为“makefile”或“Makefile”的文件中,然后在该目录下直接输入命令 make 就可以生成执行文 件edit。如果要删除执行文件和所有的中间目标文件,那么,只要简单地执行一下 make clean 就 可以了。

在这个makefile中,目标文件(target)包含:执行文件edit和中间目标文件( *.o ),依赖文 件(prerequisites)就是冒号后面的那些 .c 文件和 .h 文件。每一个 .o 文件都有 一组依赖文件,而这些 .o 文件又是执行文件 edit 的依赖文件。依赖关系的实质就是说明了目 标文件是由哪些文件生成的,换言之,目标文件是哪些文件更新的。

在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个 Tab键 作为开头。记住,make并不管命令是怎么工作的,他只管执行所定义的命令。make会比较targets文件 和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或 者target不存在的话,那么,make就会执行后续定义的命令。

这里要说明一点的是, clean 不是一个文件,它只不过是一个动作名字,有点像c语言中的label一 样,其冒号后什么也没有,那么,make就不会自动去找它的依赖性,也就不会自动执行其后所定义的命令。 要执行其后的命令,就要在make命令后明显得指出这个label的名字。这样的方法非常有用,我们可以在一 个makefile中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。

make是如何工作的

在默认的方式下,也就是我们只输入 make 命令。那么,

  1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
  2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个 文件,并把这个文件作为最终的目标文件。
  3. 如果edit文件不存在,或是edit所依赖的后面的 .o 文件的文件修改时间要比 edit 这个 文件新,那么,他就会执行后面所定义的命令来生成 edit 这个文件。
  4. 如果 edit 所依赖的 .o 文件也不存在,那么make会在当前文件中找目标为 .o 文件 的依赖性,如果找到则再根据那一个规则生成 .o 文件。(这有点像一个堆栈的过程)
  5. 当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件生 成make的终极任务,也就是执行文件 edit 了。

这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在 找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所 定义的命令的错误,或是编译不成功,make根本不理。make只管文件的依赖性,即,如果在我找了依赖关系 之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。

通过上述分析,我们知道,像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命 令将不会被自动执行,不过,我们可以显示要make执行。即命令—— make clean ,以此来清除所有 的目标文件,以便重编译。

于是在我们编程中,如果这个工程已被编译过了,当我们修改了其中一个源文件,比如 file.c , 那么根据我们的依赖性,我们的目标 file.o 会被重编译(也就是在这个依性关系后面所定义的命令), 于是 file.o 的文件也是最新的啦,于是 file.o 的文件修改时间要比 edit 要新,所 以 edit 也会被重新链接了(详见 edit 目标文件后定义的命令)。

而如果我们改变了 command.h ,那么, kdb.o 、 command.o 和 files.o 都 会被重编译,并且, edit 会被重链接。

makefile中使用变量

在上面的例子中,先让我们看看edit的规则:

edit : main.o kbd.o command.o display.o \insert.o search.o files.o utils.occ -o edit main.o kbd.o command.o display.o \insert.o search.o files.o utils.o

 

我们可以看到 .o 文件的字符串被重复了两次,如果我们的工程需要加入一个新的 .o 文件, 那么我们需要在两个地方加(应该是三个地方,还有一个地方在clean中)。当然,我们的makefile并不复 杂,所以在两个地方加也不累,但如果makefile变得复杂,那么我们就有可能会忘掉一个需要加入的地方, 而导致编译失败。所以,为了makefile的易维护,在makefile中我们可以使用变量。makefile的变量也 就是一个字符串,理解成C语言中的宏可能会更好。

比如,我们声明一个变量,叫 objects , OBJECTS , objs , OBJS , obj 或是 OBJ ,反正不管什么啦,只要能够表示obj文件就行了。我们在makefile一开始就 这样定义:

objects = main.o kbd.o command.o display.o \insert.o search.o files.o utils.o

 

于是,我们就可以很方便地在我们的makefile中以 $(objects) 的方式来使用这个变量了,于是 我们的改良版makefile就变成下面这个样子:

objects = main.o kbd.o command.o display.o \insert.o search.o files.o utils.oedit : $(objects)cc -o edit $(objects)
main.o : main.c defs.hcc -c main.c
kbd.o : kbd.c defs.h command.hcc -c kbd.c
command.o : command.c defs.h command.hcc -c command.c
display.o : display.c defs.h buffer.hcc -c display.c
insert.o : insert.c defs.h buffer.hcc -c insert.c
search.o : search.c defs.h buffer.hcc -c search.c
files.o : files.c defs.h buffer.h command.hcc -c files.c
utils.o : utils.c defs.hcc -c utils.c
clean :rm edit $(objects)

 

于是如果有新的 .o 文件加入,我们只需简单地修改一下 objects 变量就可以了。

关于变量更多的话题,我会在后续给你一一道来。

让make自动推导

GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在每一个 .o 文件后都写上类似的命令,因为,我们的make会自动识别,并自己推导命令。

只要make看到一个 .o 文件,它就会自动的把 .c 文件加在依赖关系中,如果make找到一个whatever.o ,那么 whatever.c 就会是 whatever.o 的依赖文件。并且 cc -c whatever.c 也会被推导出来,于是,我们的makefile再也不用写得这么复杂。我们的 新makefile又出炉了。

objects = main.o kbd.o command.o display.o \insert.o search.o files.o utils.oedit : $(objects)cc -o edit $(objects)main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h.PHONY : clean
clean :rm edit $(objects)

 

这种方法,也就是make的“隐晦规则”。上面文件内容中, .PHONY 表示 clean 是个伪目标 文件。

关于更为详细的“隐晦规则”和“伪目标文件”,我会在后续给你一一道来。

另类风格的makefiles

既然我们的make可以自动推导命令,那么我看到那堆 .o 和 .h 的依赖就有点不爽,那么多的 重复的 .h ,能不能把其收拢起来,好吧,没有问题,这个对于make来说很容易,谁叫它提供了自动 推导命令和文件的功能呢?来看看最新风格的makefile吧。

objects = main.o kbd.o command.o display.o \insert.o search.o files.o utils.oedit : $(objects)cc -o edit $(objects)$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h.PHONY : clean
clean :rm edit $(objects)

 

这种风格,让我们的makefile变得很简单,但我们的文件依赖关系就显得有点凌乱了。鱼和熊掌不可兼得。 还看你的喜好了。我是不喜欢这种风格的,一是文件的依赖关系看不清楚,二是如果文件一多,要加入几个 新的 .o 文件,那就理不清楚了。

清空目标文件的规则

每个Makefile中都应该写一个清空目标文件( .o 和执行文件)的规则,这不仅便于重编译,也很 利于保持文件的清洁。这是一个“修养”(呵呵,还记得我的《编程修养》吗)。一般的风格都是:

clean:rm edit $(objects)

 

更为稳健的做法是:

.PHONY : clean
clean :-rm edit $(objects)

 

前面说过, .PHONY 表示 clean 是一个“伪目标”。而在 rm 命令前面加了一个小减号的 意思就是,也许某些文件出现问题,但不要管,继续做后面的事。当然, clean 的规则不要放在文件 的开头,不然,这就会变成make的默认目标,相信谁也不愿意这样。不成文的规矩是——“clean从来都是放 在文件的最后”。

上面就是一个makefile的概貌,也是makefile的基础,下面还有很多makefile的相关细节,准备好了 吗?准备好了就来。

Makefile里有什么?

Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。

  1. 显式规则。显式规则说明了如何生成一个或多个目标文件。这是由Makefile的书写者明显指出要生成的 文件、文件的依赖文件和生成的命令。
  2. 隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较简略地书写 Makefile,这是由make所支持的。
  3. 变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点像你C语言中的 宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
  4. 文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中 的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一 样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。
  5. 注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用 # 字符,这个就 像C/C++中的 // 一样。如果你要在你的Makefile中使用 # 字符,可以用反斜杠进行 转义,如: \# 。

最后,还值得一提的是,在Makefile中的命令,必须要以 Tab 键开始。

Makefile的文件名

默认的情况下,make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、 “makefile”、“Makefile”的文件,找到了解释这个文件。在这三个文件名中,最好使用“Makefile” 这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。最好不要用“GNUmakefile”, 这个文件是GNU的make识别的。有另外一些make只对全小写的“makefile”文件名敏感,但是基本上来说, 大多数的make都支持“makefile”和“Makefile”这两种默认文件名。

当然,你可以使用别的文件名来书写Makefile,比如:“Make.Linux”,“Make.Solaris” ,“Make.AIX”等,如果要指定特定的Makefile,你可以使用make的 -f 和 --file 参数, 如: make -f Make.Linux 或 make --file Make.AIX 。

引用其它的Makefile

在Makefile使用 include 关键字可以把别的Makefile包含进来,这很像C语言的 #include ,被包含的文件会原模原样的放在当前文件的包含位置。 include 的语法是:

include <filename>

filename 可以是当前操作系统Shell的文件模式(可以包含路径和通配符)。

在 include 前面可以有一些空字符,但是绝不能是 Tab 键开始。 include 和 <filename> 可以用一个或多个空格隔开。举个例子,你有这样几个Makefile: a.mk 、 b.mk 、 c.mk ,还有一个文件叫 foo.make ,以及一个变量 $(bar) ,其包含 了 e.mk 和 f.mk ,那么,下面的语句:

include foo.make *.mk $(bar)

等价于:

include foo.make a.mk b.mk c.mk e.mk f.mk

make命令开始时,会找寻 include 所指出的其它Makefile,并把其内容安置在当前的位置。就好 像C/C++的 #include 指令一样。如果文件都没有指定绝对路径或是相对路径的话,make会在当前目 录下首先寻找,如果当前目录下没有找到,那么,make还会在下面的几个目录下找:

  1. 如果make执行时,有 -I 或 --include-dir 参数,那么make就会在这个参数所指定的目 录下去寻找。
  2. 如果目录 <prefix>/include (一般是: /usr/local/bin 或 /usr/include )存在的话,make也会去找。

如果有文件没有找到的话,make会生成一条警告信息,但不会马上出现致命错误。它会继续载入其它的 文件,一旦完成makefile的读取,make会再重试这些没有找到,或是不能读取的文件,如果还是 不行,make才会出现一条致命信息。如果你想让make不理那些无法读取的文件,而继续执行,你可以 在include前加一个减号“-”。如:

-include <filename>

其表示,无论include过程中出现什么错误,都不要报错继续执行。和其它版本make兼容的相关命令 是sinclude,其作用和这一个是一样的。

环境变量MAKEFILES

如果你的当前环境中定义了环境变量 MAKEFILES ,那么,make会把这个变量中的值做一个类似于include 的动作。这个变量中的值是其它的Makefile,用空格分隔。只是,它和 include 不 同的是,从这个环境变量中引入的Makefile的“目标”不会起作用,如果环境变量中定义的文件发现 错误,make也会不理。

但是在这里我还是建议不要使用这个环境变量,因为只要这个变量一被定义,那么当你使用make时, 所有的Makefile都会受到它的影响,这绝不是你想看到的。在这里提这个事,只是为了告诉大家,也许 有时候你的Makefile出现了怪事,那么你可以看看当前环境中有没有定义这个变量。

make的工作方式

GNU的make工作时的执行步骤如下:(想来其它的make也是类似)

  1. 读入所有的Makefile。
  2. 读入被include的其它Makefile。
  3. 初始化文件中的变量。
  4. 推导隐晦规则,并分析所有规则。
  5. 为所有的目标文件创建依赖关系链。
  6. 根据依赖关系,决定哪些目标要重新生成。
  7. 执行生成命令。

1-5步为第一个阶段,6-7为第二个阶段。第一个阶段中,如果定义的变量被使用了,那么,make会把其展 开在使用的位置。但make并不会完全马上展开,make使用的是拖延战术,如果变量出现在依赖关系的规则 中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。

转载于:https://www.cnblogs.com/yoyolm2014/p/9360247.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/413686.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第11章-img特征,vertical-align,cursor,opacity

1.img特征 2.vertical-align垂直对齐方式 与text-align对比 3.cursor指针样式 default auto pointer move text wait help 4.opacity透明度 与rgba对比 span不支持text-align属性 IE8及以下不兼容:filter:alpha(opacity0~100);

[css] pseudo-class与pseudo-element有什么区别?

[css] pseudo-class与pseudo-element有什么区别&#xff1f; 伪类表示已存在的某个元素处于某种状态&#xff0c;但是通过dom树又无法表示这种状态&#xff0c;就可以通过伪类来为其添加样式。例如a元素的:hover, :active等伪元素主要是用来创建一些不存在原有dom结构树种的元…

iotop命令

iotop命令是一个用来监视磁盘I/O使用状况的top类工具&#xff0c;iotop具有与top相似的UI&#xff0c;其中包括pid、user、I/O、进程等相关信息等&#xff1b; //iotop安装&#xff1a; yum -y install iotop //或者编译安装 wget http://guichaz.free.fr/iotop/files/iotop-0.…

[css] 行内元素可以设置padding和margin吗?

[css] 行内元素可以设置padding和margin吗&#xff1f; 行内元素的纵向padding和margin都是不考虑的&#xff0c;这是css规范定义的。 inline元素确实可以设置垂直方向的 padding 和 margin 值&#xff0c;但是 inline 元素的 margin 和 padding 的垂直方向上不产生边距效果&a…

第10章-定位、层级关系

一.定位 1.static 默认属性。块级元素→矩形框&#xff0c;行级元素→行框 2.fixed 类似于absolute&#xff0c;但包含块是视窗本身 3.relative 原本所占的空间仍保留 4.absolute 生成一个块级框&#xff0c;变成块级元素。float也是 二.层级关系 z-index

ccf_201712-2

题目 问题描述有n个小朋友围成一圈玩游戏&#xff0c;小朋友从1至n编号&#xff0c;2号小朋友坐在1号小朋友的顺时针方向&#xff0c;3号小朋友坐在2号小朋友的顺时针方向&#xff0c;……&#xff0c;1号小朋友坐在n号小朋友的顺时针方向。游戏开始&#xff0c;从1号小朋友开始…

第9章-浮动

1.文档流 文档中可显示对象在排列时所占用的位置、空间 2.浮动的一般情况 3.浮动的特殊情况 4.浮动对文字的影响 5.float和inline-block的比较 6.清除浮动 &#xff08;1&#xff09;最后添加空标签 <div class"clear"><div> clear属性值:left r…

[css] translate3D有什么作用?

[css] translate3D有什么作用&#xff1f; 3d动画&#xff0c;启用GPU硬件加速个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题

[css] 如何形成BFC?

[css] 如何形成BFC&#xff1f; 根元素 float的值不为none overflow的值不为visible display的值为inline-block、table-cell、table-caption position的值为absolute或fixed个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很…

第8章-常用优先级和css3

一.优先级 1.选择器优先级 * < 标签 < class < id 2.样式优先级 行内样式 <div style""></div> 内部样式 <style type"text/css"></style> 外部样式 <link type"text/css" rel"stylesheet…

pandas 基础用法

pandas 是一个基于 Numpy 构建, 强大的数据分析工具包 主要功能 独特的数据结构 DataFrame, Series集成时间序列功能提供丰富的数学运算操作灵活处理缺失数据Series 一维数组 Series 是一种类似于一维数组的对象, 由一组数据和一组与之相关的数据标签(索引)组成 创建方式 pd.Se…

[css] 为什么float会导致父元素塌陷

[css] 为什么float会导致父元素塌陷 “当元素设置浮动后&#xff0c;会自动脱离文档流”&#xff0c; 翻译成白话就是说&#xff0c;元素浮动后&#xff0c;就不在整个文档流的管辖范围&#xff0c;那么它之前存在在父元素内的高度就随着浮动不复存在了&#xff0c;而此时父元…

Object Tracking using OpenCV (C++/Python)(使用OpenCV进行目标跟踪)

本博客翻译搬运自https://www.learnopencv.com/object-tracking-using-opencv-cpp-python&#xff0c;用于初入目标跟踪的新手学习&#xff0c;转贴请注明&#xff01; 使用OpenCV进行目标跟踪&#xff08;C/Python&#xff09; 在本教程里&#xff0c;我们将学习OpenCV3.0中引…

第7章-选择器+伪类

一.选择器 1.基础选择器 通配符选择器 标签/元素选择器 类选择器 id选择器 2.高级选择器 E,F (多元素选择器) 同时匹配所有E元素或F元素&#xff0c;E和F之间用逗号分隔 eg&#xff1a; div,p{width:100px;height:50px;} E F(后代选择器) 匹配所有属于E元素后代的F元…

[css] 什么是逐帧动画?

[css] 什么是逐帧动画&#xff1f; &#xff08;1&#xff09;相关联的不同图像&#xff0c;即动画帧&#xff1b;&#xff08;2&#xff09;连续播放。个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论…

第5章-css选择器初级和背景

一、ID选择器与Class选择器的区别 区别 1&#xff1a;ID选择器只能在文档中使用一次,. class类选择器可以多次使用。 区别 2&#xff1a; id的权重大于class类的权重 二.background背景样式 1.background-color:规定要使用的背景颜色十六进制值 #CC0066 #000英文单词…

POJ 4979 海贼王之伟大航路 【状压dp】【北大ACM/ICPC竞赛训练】

该死的题让我想起来艾斯之死... 首先想到dp(i)代表从1到【i表示的这些岛屿】所花的最小时间&#xff0c;然后每次枚举最后一个岛屿以此缩小范围&#xff0c;但发现枚举了最后一个岛屿后没有办法转移&#xff0c;因为不知道倒数第二个岛屿是什么&#xff0c;随着倒数第二个岛屿的…

[css] 举例说明BFC会与float元素相互覆盖吗?为什么?

[css] 举例说明BFC会与float元素相互覆盖吗&#xff1f;为什么&#xff1f; BFC的区域不会与float的元素区域重叠 计算BFC的高度时&#xff0c;浮动子元素也参与计算 BFC就是页面上的一个隔离的独立容器&#xff0c;容器里面的子元素不会影响到外面元素&#xff0c;反之亦然个…

Sublime Text 3 、WebStorm配置实时刷新

本文所用软件版本Sublime Text 3(Build 3143)、WebStorm 2017.2.4(Build #WS-172.4155.35)、Google Chrome v61.0.3163.100&#xff0c;其他版本软件配置过程可能不一样&#xff0c;请知悉&#xff01; 一.Google Chrome安装LiveReload插件 1.下载插件 LiveReload 2.1.0 链…

#0 scrapy爬虫学习中遇到的坑记录

python 基础学习中对于scrapy的使用遇到了一些问题。 首先进行的是对Amazon.cn的检索结果页进行爬取&#xff0c;很顺利&#xff0c;无碍。 下一个目标是对baidu的搜索结果进行爬取 1&#xff0c;反爬虫 1.1 我先对ROBOTSTXT_OBEY进行设置&#xff0c;结果找到了scrapy的默认参…