[make] 第二章 makefile 总述


​ 默认的情况下,make会在工作目录(执行make的目录)下按照文件名顺序寻找makefile文件读取并执行,查找的文件名顺序为:“GNUmakefile”,“makefile”、“Makefile”。以"GNUmakefile"命名的文件只有“GNU make”才可以识别,其他版本的make程序只会在工作目录下执行“makefile”和“Makefile”两个文件,一般使用“Makefile”作为文件名,与README进行统一。

​ 在make是可以通过“-f”选项指定需要执行的makefile文件,当指定后,make就不会在执行目录对上述三个文件名进行检查执行。

workDir>> make -f ./dir/Makefile
# 当执行上述语句时,默认只会在workDir下寻找依赖文件而不是在“./dir/”下

1. Makefile 的内容

完整的Makefile包含:显示规则、隐含规则、变量定义、指示符和注释——5类内容。

  • 显示规则:描述了在何种情况下如何更新一个或者多个被称为目标的文件。书写Makefile时需要明确地给出目标文件、目标的依赖文件列表以及更新目标文件所需要的命令
  • 隐含规则:此类规则是make根据一类目标文件(典型的是根据文件名的后缀)而自动推导出来的规则。make根据目标文件的名,自动产生目标的依赖文件并使用默认的命令来对目标进行更新。
  • 变量定义:使用一个字符或字符串代表一段文本串,当定义了一个变量以后,Makefile后续在需要使用此文本串的地方,通过引用这个变量来实现对文本串的使用。
  • Makefile指示符:指示符指明在make程序读取makefile文件过程中所要执行的一个动作其中包括:
    • 读取一个文件,读取给定文件名的文件,将其内容作为makefile文件的一部分
    • 决定(通常是根据一个变量的值)处理或者忽略Makefile中的某一特定部分
    • 定义一个多行变量
  • 注释:Makefile中#”字符后的内容被作为注释内容处理。如果慈航的第一个非空字符为“#”,那么此行为注释行。注释行的结尾如果存在反斜线(\),那么下一行也被作为注释行。其中反斜线可以用作字符转义使用,如“\#”表示字符#而不是注释标记

​ Makefile中第一个规则之后的所有以[Tab]字符开始的行,make程序都会将其交给系统shell程序去解释执行。因此,以[Tab]字符开始的注释行也会被交给shell来处理。

​ Makefile中变量的引用和C语言中宏类似,对一个变量引用的地方make所做的就是将这个变量根据定义进行基于文本的展开,展开变量的过程不涉及到任何变量的具体含义和功能分析。

2. 包含其他makefile文件

Makefile中包含其他文件所需要使用的关键字是“include”。“include”指示符告诉make暂停读取当前的Makefile,而去读取“include”指定的一个或多个文件,完成后再继续当前Makefile的读取。

include FILENAME...
# FILENAME是shell所支持的文件名(可以使用通配符)
# include所在的行可以以一个或多个空格开始(make程序在处理时会忽略这些空格),但是不能以[Tab]开始
# 当有多个问件时,可以使用空格或[Tab]进行隔开
# 行末尾的空格也会被忽略
# 如果指示符包含进来的Makefile中,如果存在变量或者函数的引用,则会在使用指示符的Makefile中被展开

include的应用场合:

  • 有多个不同的程序,由不同目录下的几个独立的Makefile来描述其重建规则,它们需要使用一组通用的变量定义或者模式规则。通用的做法是将这些共同使用的变量或者模式规则定义在一个文件中,在需要使用的Makefile中使用指示符来包含此文件
  • 当根据源文件自动产生依赖时;我们可以将自动产生的依赖关系保存在另一个文件中,主Makefile使用指示符“include”包含这些文件。

如果指示符“include”指定的文件不是绝对路径,而是当前目录下不存在的文件,make将根据文件名视图在以下几个目录下查找:

  • 首先,查找使用命令行选项“-I”或者“-include-dir”指定的目录
  • 如果在上述路径下未找到,则继续搜索以下几个目录
    • /usr/gnu/include
    • /usr/local/include
    • /usr/include

当make在上述行为后,都没有找到include的文件,则make会提示一个包含文件未找到的警告提示,但不会退出,继续执行Makefile的后续内容。当执行完当前的Makefile时,make程序将试图使用规则(如果有的话)来创建通过指示符指定的但未找到的文件。当不能创建时,make将提示fatal error并退出。

可以通过在include前加上“-”来忽略有include造成的错误

3. makefile文件中的特殊变量

3.1 变量MAKEFILES

如果在当前环境定义了一个“MAKEFILES”环境变量,make 执行时首先将此变量的值作为需要读入的 Makefile 文件,多个文件之间使用空格分开。类似使用指示符“include”包含其它 Makefile文件一样,如果文件名非绝对路径而且当前目录也不存在此文件,make 会在一些默认的目录去寻找。

与include的区别:

  • 环境变量指定的makefile文件中的“目标”不会被作为make执行的“终极目标”,如果当前目录下,没有有效的Makefile文件,则会报错误;否则,会将当前目录下的Makefile中的目标作为终极目标
  • 环境变量中的文件列表,如果make没找到不会提示错误。
  • make在执行时,首先读取的是环境变量“MAKEFILES”所指定的文件列表,之后才是工作目录下的makefile文件

MAKEFLIES的使用场景:

  • 主要用于make的递归调用过程中的通信,类似于全局变量

3.2 变量MAKEFILE_LIST

make 程序在读取多个 makefile 文件时,包括由环境变量“MAKEFILES”指定、命令行指定、当前工作下的默认的以及使用指示符“include”指定包含的,在对这些文件进行解析执行之前 make读取的文件名将会被自动依次追加到变量“MAKEFILE_LIST”的定义域中

可以通过测试此变量的最后一个字来获取当前 make 程序正在处理的 makefile 文件名

4. make如何解析makefile文件

GUN make的执行过程分为来两个阶段

  • 第一阶段:读取所有的makefile文件(包含“MAKEFILES”,指示符“include”指定的,命令行选项“-f”指定的),内建所有的变量、明确规则和隐含规则,并建立所有目标和依赖之间的依赖关系结构链表
    • 在该阶段,如果变量和函数被展开,那么称此展开为“立即”的
  • 第二阶段:根据第一阶段已经建立的依赖关系结构链表决定那些目标需要更新,并使用对应的规则来重建这些目标

4.1 变量取值

IMMEDIATE = DEFERRED
IMMEDIATE ?= DEFERRED
IMMEDIATE := IMMEDIATE
IMMEDIATE += DEFERRED or IMMEDIATE	# 如果追加的变量是一个简单变量(使用:=定义的),则该变量会被									  立即展开,其他情况都被认为是“延后”的
define IMMEDIATE
DEFERRED
Endef

4.2 条件语句

所有使用到条件语句在产生分支的地方,make程序会根据预设条件将正确的分支展开。即:条件分支的展开是立即的。其中包括:“ifdef”、“ifeq”、“ifndef”、“ifneq”所确定的所有分支命令

4.3 规则的定义

所有的规则在make执行时,都会按照下列要求展开:

  • 规则中目标和依赖如果引用其他的变量,则被立即展开
  • 规则的命令行中的变量引用会被延后展开

上述定义适合所有的规则,其中包括:明确规则,模式规则、后缀规则、静态模式规则

4.4 make的执行过程

  1. 依次读取环境变量“MAKEFILES”定义的makefile文件列表
  2. 读取工作目录下的makefile文件(根据命名的查找顺序“GNUmakefile”、“makefile”、“Makefile”,首次找到哪个就读哪个)
  3. 依次读取工作目录makefile文件中使用指示符“include”包含的文件(在make程序解析makefile文件时,到所在的命令行才会读取)
  4. 查找重建所有已经读取的makefile文件的规则(如果存在一个目标是是当前读取的某一个makefile文件,则执行此规则重建此makefile文件,完成后从第一步开始重新执行)
  5. 初始化变量值并展开那些需要立即展开的变量和函数并根据预设条件确定执行分支
  6. 根据“终极目标“以及其他目标的依赖关系建立依赖关系链表
  7. 执行除”终极目标“以外的所有的目标的规则(规则中如果依赖文件中人一个文件的时间戳比目标文件新,则使用规则所定义的命令重建目标文件)
  8. 执行”终极目标“所在的规则

4.5 规则的执行过程

对于一个存在的规则(明确规则和隐含规则),首先,make程序将比较目标文件和所有依赖文件的时间戳,如果目标的时间戳比所有依赖文件的时间戳更新,则什么也不做;否则,去执行规则所定义的命令来重建目标文件。

原文地址:https://www.cnblogs.com/aaronbin/p/15563729.html