上一篇我们说明了为什么需要
make
,以及make
是怎么解决程序构建痛点的。
【Makefile】 - GNU make序&缘起
该篇仍然会进一步介绍Makefile
,希望通过这两篇能让你对make
、Makefile
有一个感性的认识。
Makefile内容
Makefile
中主要包含以下内容:
-
显式规则。显式规则由我们明确给出,例如:如何生成目标文件,文件的依赖文件有哪些,生成的命令等均属于该范畴。
-
隐晦规则。即上一篇讲的隐含规则;由于
make
具有自动推导功能,隐晦规则可以帮助我们更快的写出makefile
。 -
变量。上一篇涉及到的宏;,当
makefile
被执行时,makefile
中的变量都会被扩展到相应的引用位置上。 -
Makefile指示符。它指明在
make程序
读取makefile
过程中所要执行的动作。包含以下三部分内容:-
读取给定文件名的文件,将其作为
makefile
的一部分(类似C include
)。 -
根据条件忽略
makefile
中的一部分(类似C
条件编译#if
) -
定义多行变量(多行定义)
-
-
注解。
makefile
中只有行注释,采用#字符
,若注释行结尾存在反斜杠\
,则下一行也被视为注释行。如果我们需要使用#字符
,则需要转义 \#
。
注意:
makefile
中第一个规则之后的所有以 Tab
开始的行,make
程序将送由系统的 shell
程序解释执行。所以如果是 Tab
开头的注释行也会送至 shell
处理。
Makefile文件名
默认情况下,make
会在工作目录下查找 makefile
,查找遵循以下顺序:
GNUmakefile > makefile > Makefile
GNUmakefile
只有 GNUmake
才能识别,因此不推荐。一般使用 Makefile
(比较惹眼)。
如果在工作路径下找不到 makefile
文件,则它将不会读取任何文件作为解析对象,make将报错
。
通过 help
我们发现 make
指令有一个 -f
选项,可以指定 Makefile
,这时指定的文件可以是任意的。
包含其他Makefile
在 Makefile
中使用 include
关键字可以把别的 Makefile
包含进来。被包含的文件会原封不动的替换到 include的位置
。
include <filename>
make
指令开始时,会寻找 include
所指出的 Makefile
进行替换。若无指定绝对路径,则优先在当前路径下寻找,如果没有找到,则会在 make
执行时"-I"/"-- include-dir"
参数指定的路径下寻找(若有);如果没有找到,目录 <prefix>/include
存在的话也会取寻找(一般是 /usr/local/bin
或 /usr/include
)。
如果都没有找到,则会生成警告。待 makefile
文件读取完成,make
会再次尝试寻找这些未找到/无法读取的文件,如果还是无法读取,才会 报错并停止运行。
如果我们想让 make
不理会那些无法读取的文件继续执行,只需在 include
前面加上 -
即可。
-include <filename>
变量
MAKEFILES
如果我们定义了环境变量 MAKEFILES
,那么在 make
执行之前,MAKEFILES
指定的文件将会被 include
。那么它和 include
的区别是什么呢?
- 从这个环境变量引入的
makefile
目标不会起作用,如果在工作目录或默认路径下找不到makefile
文件,同样会报错 - 如果环境变量中定义的文件发送错误,
make
也不理会(既不退出也不报错,因此是比较危险的) make
执行时,先读取MAKEFILES
指定的文件,随后才在工作目录下查找makefile
。而include
所指定的文件,是在make
解析到时停止当前makefile
解析转而读取include
指定的文件。
变量 MAKEFILES
主要用在 make
的递归调用过程中的的通信。
注意:
由于设置该环境变量,对所有 make
都生效。这样很容易出现混乱,而且也不利于移植,因此尽量少用。
嘿,如果 make
出现了一些奇怪的问题,不妨查看下是否与该环境变量有关。
MAKEFILE_LIST
make程序在读取多个makefile文件时,包括MAKEFILES指定的,命令行指定的,当前工作目录下默认的以及使用指示符include包含的…在对这些文件进行解析执行之前,make读取的文件名将会被自动依次追加到变量MAKEFILE_LIST的定义域中。
其他特色变量
GNU make
支持一个特殊的变量,此变量不能通过任何方法赋值。这个变量是 .VARIABLES
。它被展开以后是此引用点之前、makefile
文件中所定义的所以全局变量列表。包括:空变量和 make
的隐含变量,但不包含目标指定的变量,目标指定变量值在特定目标的上下文有效。
make工作方式(如何解析Makefile)
GNU make
在工作的时候分为两个阶段:
- 第一阶段。读取所有的
makefile
(包括被include
的makefile
),初始化文件中的变量;推导隐含规则,并分析所有规则。完成这些以后,就可以构建目标文件的 依赖关系链。 - 第二阶段。 通过依赖关系链决定哪些目标需要被更新,并使用对应的规则来重建这些目标。
若进一步细分,则可以拆分为以下步骤:
- 依次读取变量
MAKEFILES
定义的makefile
文件列表 - 读取工作目录下的
makefile
文件(根据命名的查找顺序GNUmakefile
,makefile
,Makefile
,首先找到那个就读取那个) - 依次读取工作目录
makefile
文件中使用指示符include
包含的文件 - 查找重建所有已读取的
makefile
文件的规则(如果存在一个目标是当前读取的某一个makefile
文件,则执行此规则重建此makefile
文件完成以后从第一步开始重新执行) - 初始化变量值并展开那些需要立即展开的变量和函数并根据预设条件确定执行分支
- 根据 终极目标 以及其他目标的依赖关系建立依赖关系链表
- 执行除 终极目标 以外的所有的目标的规则(规则中如果依赖文件中任一个文件的时间戳比目标文件新,则使用规则所定义的命令重建目文件)
- 执行 终极目标 所在的规则
参考鸣谢
小声: 内容大同小异…