免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1525 | 回复: 0
打印 上一主题 下一主题

[GNU make中文1.6]第三章 Makefile 总述 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-07-01 00:13 |只看该作者 |倒序浏览
第三章:Makefile 总述

1.1 Makefile的内容

一个完整的Makefile中,包含了5个东西:显式规则、隐含规则、变量定义、指示符和注释。关于“规则”、“变量”和“Makefile指示符”后续有详细讨论。本章讨论一些基本概念。

  • 显式规则:它描述了在何种情况下如何更新一个或者多个被称为目标的文件(Makefile的目标文件)。书写Makefile规则时需明确地给出目标文件、目标的依赖文件列表以及更新目标文件所需要的命令(有些规则没有命令,这样的规则只是纯粹的描述了文件之间的依赖关系)。显式规则就是在Makefile中被明确地给出的所有规则。
  • 隐含规则:它是make根据某一类文件的特征(典型地,根据文件名后缀)按照特定的对应关系,为创建(或者更新)这个文件而自动推导出来的规则。make根据文件名,将其作为目标文件,并按照特定的对应关系为它找出依赖文件,使用make创建此类文件默认的命令行来建立这样一个规则。关于隐含规则可参考 第十章 make的隐含规则
  • 变量定义:使用一个字符或字符串代表一段文本串。当定义了一个变量后,Makefile后续在需要使用此文本串的地方,通过引用这个变量来实现对文本串的使用。第一章的例子中,我们就定义了一个变量“objects”来表示一个.o文件列表。关于变量的详细讨论可参考 第六章 Makefile中的变量
  • Makefile指示符:一个指示符用来指明在make读取makefile文件过程中所要执行的一个动作。其中包括:

    • 让make程序根据文件名读取指定文件的内容,并将其内容作为当前Makefile的内容来处理。
    • 决定(通常是根据一个变量的取值)处理还是忽略Makefile中的某一部分内容。参考 第七章Makefile的条件执行
    • 定义一个多行变量。参考 6.8 多行定义 一节

  • 注释:Makefile中以字符“#”开始的行为注释行;在一行中字符“#”之后的内容也被看做为注释(和shell脚本一样)。注释行结尾如果使用反斜线(\),那么此行之下的一行(下一行)也被看做为注释行。在书写Makefile时推荐将注释作为一个独立的行,而不要和Makefile的有效内容放在同一行。当Makefile中需要使用字符“#”时,可使用反斜线加“#”(\#)来实现(对具有特殊含义字符“#”进行转义),其表示 “#”在这里是作为一字符而不是注释的开始标志。

需要注意的地方:
Makefile中第一个规则之后所有以[Tab]字符开始的的行,make都会将其交给系统shell程序去解释。因此,以[Tab]字符开始的注释行也会被交给shell,此命令行是否需要被执行由系统shell做判决。因此,以字符“#”开始的命令行shell将不执行它。
另外,在使用指示符“define”定义一个多行的变量或者命令包时,其定义体(“define”和“endef”之间的内容)会被完整的展开到Makefile中引用此变量的地方(包含定义体中的注释行);make在执行时才会对已展开的变量定义体进行处理,决定定义体的某一行到底是注释还是有效内容。Makefile中变量的引用和C语言中的宏类似(但是其实质却不同,后续将会详细讨论)。一个变量在其被引用的地方make所做的就是将这个变量根据定义进行基于文本的展开,展开变量的过程不涉及任何对此变量的具体含义分析和实际功能分析。
1.2 makefile文件的命名
默认情况下,执行make时,make在工作目录(执行make的目录)下按照顺序依次寻找如下命名的makefile文件读取并执行它,文件名的查找顺序为:“GNUmakefile”、“makefile”、“Makefile”。
通常使用“makefile”或者“Makefile”作为makefile的文件名(推荐使用“Makefile”,首字母大写而显著,首字母大写的文件在目录中和当前目录的一些重要文件,诸如README,Changelist相靠近,这样更容易被寻找)。而“GNUmakefile”是不推荐使用的文件命名,因为以此命名的文件只能被“GNU make”识别,不能被其它版本的make识别,其它版本的make程序只会在工作目录下寻找“makefile”和“Makefile”这两个文件。
如果make在工作目录下无法找到以上三个文件中任意一个,它将不会读取其它的文件作为解析对象,而是提示错误。但根据make隐含规则的特性,在没有makefile文件的目录下,执行make时通过命令行指定目标,如果当前目录下存在符合此目标所依赖的文件,那么命令行所指定的目标文件也将会被创建或者更新,参见注释。(详细可参考 第十章 make的隐含规则)
当makefile文件的命名不是这三个中的一个时,可通过make的“-f”或者“--file”选项来指定一个文件名,将其作为make解析的makefile文件。给make指定makefile文件的格式为:“-f NAME”或者“—file=NAME”,它指定文件“NAME”作为执行make时读取的makefile文件。也可以通过多个“-f”或者“--file”选项来指定多个需要读取的makefile文件,多个makefile文件将会被按照给定的顺序被make读取并被make解析执行。当通过“-f”或者“--file”指定makefile文件时,make将不会尝试寻找默认的makefile文件(以这三个标准命名的文件)。

注释:执行make时命令行指定目标使用make的隐含规则:
如若当前目录下不存在以GNUmakefile”、“makefile”、“Makefile”命名的任一文件,
1. 当前目录下存在一个源文件foo.c,可以使用“make foo.o”来使用make的隐含规则自动生成foo.o。当执行“make foo.o”时。可以看到其执行的命令为:
cc –c –o foo.o foo.c
之后,foo.o将会被创建或者更新。
2. 当前目录下没有foo.c文件,就是说make的隐含规则中目标为.o文件的依赖文件不存在。执行命令“make foo.o”将会得到提示:
make: *** No rule to make target ‘foo.o’. Stop.
3. 直接执行命令“make”将得到提示信息:
make: *** No targets specified and no makefile found. Stop.
1.3 包含其它makefile文件
本节我们讨论如何在一个Makefile中包含其它的makefile文件。在Makefile中包含其它文件,需要使用的关键字“include”,和c源文件包含头文件的方式类似。
make在读取(解析)一个Makefile过程中,如果遇到指示符(关键字)“include”,那么make将暂停读取当前Makefile,而转去并继续读取指示符“include”指定的一个或者多个文件(多个文件时,按照“include”罗列的从左到右依次读取),完成对这些文件的读取之后再继续当前Makefile的读取。Makefile中使用指示符“include”时,须将其书写在独立的一行,其形式如下:

include FILENAMES...

FILENAMES是shell所支持的文件名(可以使用通配符)。
指示符“include”所在的行可以一个或多个空格(make在处理时将忽略这些空格)开始,切记此行不可以[Tab]字符开始(以[Tab]字符开始的行,make会将当作为命令行来处理);书写时指示符“include”和文件名之间、多个文件之间可使用空格或者[Tab]隔开;此行可以空白字符结尾,make处理时忽略行尾的空白字符。使用指示符“include”被包含进来的文件(makefile文件)中,如存在变量或者函数引用,这些引用将在包含它们的Makefile(使用指示符“include”指定包含文件的Makefile)中被展开(细节可参考 第六章 Makefile中的变量)。
看一个例子,存在三个.mk文件a.mk、b.mk、c.mk,同时变量引用“$(bar)”在这里被扩展为“bish bash”。则

include foo *.mk $(bar)

等价于

include foo a.mk b.mk c.mk bish bash

之前提到过make在遇到指示符include时,将暂停读取这个使用指示符“include”的makefile文件,转去依此读取由“include”指定的文件列表。在完成了对所有这些文件的读取后,回过头继续读取指示符“include”所在的makefile文件(从指示符“include”行之后一行开始)。
通常指示符“include”用在以下场景:
1. 有多个不同的程序,由不同目录下的几个独立的Makefile来描述其重建规则。它们需要使用一组通用的变量定义(可参考 6.5 如何设置变量 一节)或者模式规则定义(可参考 10.5 模式规则 一节)。通用的做法是将这些共同使用的变量或者模式规则定义在一个文件中(没有具体的文件命名限制),在需要使用这些定义的Makefile中使用指示符“include”来包含此文件。
2. 当根据源文件自动产生依赖文件时;我们可以将根据源文件自动产生的依赖关系保存在一个(多个)文件中,主Makefile可使用指示符“include”包含这些由源文件产生的依赖描述文件。如此做法相比直接在主Makefile中手工追加依赖文件方式要更为妥善、方便。其它版本的make大多使用这种方式来处理。(参考 4.14 自动产生依赖 一节)
如果指示符“include”指定的文件不是以斜线开始(绝对路径,如/usr/src/Makefile...),并且在当前目录下不存在这个文件;make将根据文件名尝试在以下几个目录进行查找:首先,查找命令行选项“-I”或者“--include-dir”指定的目录,如果找到指定的文件,就读取这个文件;否则继续依次搜索以下几个目录(如果其存在):“/usr/gnu/include”、“/usr/local/include”和“/usr/include”。
当在这些目录下都没有找到“include”指定的文件时,make将产生一个告警,提示包含文件不能找到,但不会立即退出。而继续处理Makefile的后续内容。当完整读取整个Makefile后,make将尝试寻找规则来创建通过指示符“include”指定的但未找到的文件,在无法创建这个文件时(未能找到一个规则来创建它),make将提示致命错误并退出。给出类似如下的错误提示:

Makefile:错误的行数:未找到文件名:提示信息(No such file or directory
Make *** No rule to make target ‘’. Stop

Makefile中可使用“-include”来代替“include”,让make忽略由于包含文件不存在、或者无法创建这个包含文件所导致的错误(“-”的意思是告诉make,忽略此操作的错误,继续执行)。像下边这样:

-include FILENAMES...

使用这种方式,当所要包含的文件不存在时不会有任何错误提示、当然make也不会退出;除此之外,这种方式和第一种方式效果相同。以下是这两种方式的比较:
使用“include FILENAMES...”的情况,make在处理Makefile过程中,如果“FILENAMES”列表中的任何一个文件不存在(不能被正常读取)、也无法由一个规则来创建这个文件,那么make就提示错误并退出。
使用“-include FILENAMES...”的情况,即使所包含的文件不存在、而且这个文件也无法由规则来创建,make也将继续执行。只有在最后,不能正确完成终极目标重建的情况下(无法在当前已读取的makefile文件中为必需的目标文件找到正确的重建规则),make才提示致命错误并退出。
为了和其它版本的make兼容。Makefile中可使用“sinclude”来代替“-include”(GNU所支持的方式)。

1.4 变量 MAKEFILES
如果当前工作环境中存在一个命名为“MAKEFILES”的变量,那么make在执行时首先会读取此变量所指定的文件(空格分割多个文件),将其内容作为所要处理内容的一部分。对此变量指定文件的读取过程和指示符“include”指定文件的读取过程相同,如果文件名非绝对路径并且无法在当前目录下找到这个文件,make会在一些默认的目录去寻找(参考 3.3 包含其它makefile文件 一节)。它和使用“include”的区别:
1. 环境变量指定的makefile文件中的“目标”不被作为make的“终极目标”。就是说,这些文件中所定义规则的目标,make不会将其作为“终极目标”来处理。如果在执行make的工作目录下没有一个名为“Makefile”、“makefile”或者“GNUmakefile”的文件,执行make将会得到错误会提示“make: *** No targets specified and no makefile found. Stop.”;相反,在执行make的目录下如果已经存在一个makefile文件(命名为“Makefile”、“makefile”或者“GNUmakefile”),那么make在执行时的“终极目标”就是当前目录下Makefile中定义的第一个目标。
2. 环境变量指定的文件列表(多个文件使用空格分开),执行make时,即使make无法读取其中某一个(多个)文件(文件不存在而且无法创建它),make也不会提示错误,也不退出。就是说环境变量“MAKEFILES”指定的需要包含的文件存在与否不影响make的执行,即就是指定的文件不存在也不会导致make错误退出。这一点是在使用此变量前需要了解的。
3. make在执行时,首先读取的是环境变量“MAKEFILES”所指定的文件列表,之后才是工作目录下的makefile文件,“include”所指定的文件是在make发现此关键字的时、暂停正在读取的文件而转去读取“include”所指定的文件。
变量“MAKEFILES”用在“make”递归调用过程的通信中(参考5.6 make的递归执行 一节)。实际应用很少使用此变量,因为一旦在系统环境中设置了此变量,在多级make调用时;每一级make过程都会读取“MAKEFILES”变量所指定的文件,这样导致执行混乱,得到无法预料的结果。不过,我们可以使用此环境变量来指定一个包含make通用“隐含规则”、以及系统全局变量定义的文件。譬如,我们在一个文件中设置了默认搜索路径,并将变量“MAKEFILES”指向这个文件,那么所有的make进程都能够使用我们所设置的默认搜索路径。可以看出:使用这种方法所设置的“隐含规则”和变量定义可以被所有在当前系统上执行的make进程所使用,变量“MAKEFILES”指向文件所包含的所有东西会对任何一次make执行过程有效。
关于这个变量的使用,大家可能会有这样的想法:让login程序自动的在自己的工作环境中设置此环境变量,指向一个包含了特殊通用规则定义的文件(例如自动生成.c文件依赖.p文件的规则),那么书写的所有Makefile都能够使用这个规则,而不需要在每一个Makefile中都明确给出这个规则。可以说,这不是一个好想法。因为那些在你的工作环境可被make正确执行的Makefile,在其它人的工作环境中却未必能被make正确执行。因为他人的工作环境中未必就同样定义有“MAKEFILES”变量,即就是存在定义,变量的取值也未必相同、所指向文件的内容也未必相同。依赖于变量“MAKEFILES”的Makefile不具备通用性。
我们推荐在Makefile中使用指示符“include”来包含特定文件,而避免使用变量“MAKEFILES”来指定包含文件。

1.5 变量 MAKEFILE_LIST
make在读取多个makefile文件(包括环境变量“MAKEFILES”指定的、执行make时命令行指定的、工作目录下默认的、以及某一个makefile文件中使用指示符“include”指定包含的)时,在完成一个文件的读取后,在对这个文件进行解析执行之前,会自动将这个已经被读取的文件名追加到变量“MAKEFILE_LIST”的定义域中。
可以通过变量“MAKEFILE_LIST”定义的最后一个字来获取make当前正在处理的makefile文件名。就是说在一个Makefile中,如果使用指示符“include”包含了另外一个文件,变量“MAKEFILE_LIST”的最后一个字只可能是指示符“include”指定所要包含的那个文件的名字。
一个makefile的内容如下:

name1 := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))

include inc.mk

name2 := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))

all:
@echo name1 = $(name1)
@echo name2 = $(name2)

执行make之后,将看到以下结果:
name1 = Makefile
name2 = inc.mk
此例中涉及了make的函数的以及变量的定义方式,函数和变量定义方式将在后续章节进行详细讨论。
1.6 其他特殊变量
GNU make支持一个特殊变量,此变量不能通过任何途经给它赋值。在引用此变量的地方,它被展开为一个特定的值。一个重要的特殊的变量是“.VARIABLES”。在引用点它被展开为:此引用点之前makefile文件中所定义的所有全局变量列表,这些变量包括:空变量(未赋值的变量)和make的内嵌变量,但不包含目标指定的变量,目标指定变量只在特定目标的上下文有效(目标所在规则的上下文)。关于目标变量可参考 6.10 目标指定变量 一节
1.7 makefile文件的重建
Makefile可由其它文件生成,例如RCS或SCCS文件。如果Makefile由其它文件重建,那么在make在开始解析这个Makefile时需要重新读取更新后的Makefile、而不是之前的Makefile。make的处理过程是这样的:
make在读入所有makefile文件之后,首先将所读取的每个makefile作为一个目标,寻找更新它的规则。如果存在更新某一个makefile文件的明确规则或者隐含规则,就去更新对应的makefile文件。完成这一动作后,make会判断之前已经读取的makefile文件是否被更新(之前所读取的makefile内容是否过期),如果有文件被更新,那么make就清除本次执行的状态并重新读取所有的makefile文件(此过程中,同样会在读取完成以后尝试去更新所读取的makefile文件,通常这些文件不会被再次重建,因为它们在时间戳上已经是最新的),读取完成以后开始解析已经读入的makefile文件并开始执行必要的动作。
实际应用中,一般会明确给出makefile文件,并不需要make自动重建它们。但是make在每一次执行时总会尝试重建已经读取的makefile文件,寻找合适的隐含规则来更新它。如果觉得这样会影响make执行的效率,可以使用一种方式来避免make在执行时为试图重建makefile而寻找隐含规则。可以书写一个明确的规则,以makefile文件作为目标,命令为空。这样就可以防止make执行时为重建此makefile文件而寻找合适的隐含规则。
在一个Makefile中,如果存在一个没有依赖而只有命令行的双冒号规则,那么每次执行make时,此规则的目标文件将会被无条件的更新(此规则定义的命令会被无条件执行)。如果这样一个规则的目标是make需要读取的makefile文件,那么在执行make时,这个makefile文件(双冒号规则的目标)总会被无条件更新,使得make的执行陷入到一个死循环(make读取这个文件之后,此文件被无条件更新、导致make重新读取、读取之后再更新、更新之后再读取的过程)。为了防止这种情况的发生,make在遇到一个目标是makefile文件的双冒号规则时,将忽略对这个规则的执行(其中包括了使用“MAKEFILES”指定、make命令行指定、指示符“include”指定的需要make读取的所有makefile文件中定义的这一类双冒号规则)。
执行make时,如果没有使用“-f(--file)”选项指定一个文件,make程序将读取缺省的文件。和使用“-f(--file)”选项不同,make无法确定工作目录下是否存在缺省名称的makefile文件。如果缺省makefile文件不存在,但可以通过一个规则来创建它(此规则是隐含规则),则会自动创建缺省makefile文件,之后重新读取它并开始执行。
因此,如果在当前目录下不存在一个缺省的makefile文件,make将会按照搜索makefile文件的名称顺序去尝试创建它,直到创建成功或者超越其缺省的命名顺序。需要明确的一点是:执行make时,如果不能成功地创建缺省的makefile文件,并不一定会导致错误。makefile文件(缺省命名的或者可被创建的)并不是make正确运行的前提(通过指定终极目标,使用make隐含规则也可以正确执行make)。
当使用“-t(--touch)”选项来更新Makefile的目标文件(更新规则目标文件的时间戳)时,对于哪些是makefile文件的目标是无效的,这些目标文件(makefile文件)的时间戳并不会被更新。就是说即使在执行make时使用了选项“-t”,那些目标是makefile文件的规则同样也会被执行(重建这些makefile文件,而其它的规则不会被执行,make只是简单的更新规则目标文件的时间戳);类似还有选项“-q(—question)”和“-n(—just-print) ”,这主要是因为一个过时的makefile文件对其它目标的重建规则在当前看来可能是错误的。正因为如此,在执行命令“make –f mfile –n foo”时,首先make会试图重建“mfile文件”、如有必要则重新读取它的内容,之后会打印出更新目标“foo”所要执行的命令(但不会真正的执行这些命令)。
在这种情况时,如果不希望重建makefile文件,需要在执行make时通过命令行将这个makefile文件指定为最终目标,这样选项“–t”和其它选项就对这个指定的makefile文件目标有效,从而防止make执行以这个makefile文件为目标的规则(如果是“-t”参数,则是简单的更新这个makefile文件的时间戳)。命令“make –f mfile –n mfile foo”执行时会读取文件“mfile”,打印出重建文件“mfile”的命令、重建“foo”的命令但不去执这些命令。所打印的、用于更新“foo”目标的命令是选项“-f”指定的、没有被重建的“mfile”文件中所定义的命令;文件“mfile”在此次make执行过程中并没有被重建。
1.8 重载另外一个makefile
某些情况下,存在两个相类似的Makefile。其中一个(makefile-A)需要使用定义在另外一个(makefile-B)中规则。能够想到的是在“makefile-A”中使用指示符“include”包含“mkaefile-B”,达到此目的。但是,假设在makefile-A中为重建文件“foo.o”定义了一个规则,同样在makefile-B中也为文件foo.o定义了另外一个重建规则;这样,在makefile-A中包含了makefile-B,当make在处理makefile-A时会发现同一个目标文件“foo.o”同时存在两个不同重建规则,这是make所不允许的。因此,处理这种情况,使用指示符“include”显然是行不通的。为了实现此目的GNU make提供另外一种机制。具体做法如下:
在需要使用其它makefile规则定义的Makefile(使用指示符“include”包含其它文件)中,定义一个称之为“所有匹配模式”的规则。这个“所有匹配模式”被用来创建那些在Makefile中没有给出明确创建规则的目标文件。就是说,如果在当前Makefile文件中不能找到一个合适的规则来重建某一个目标文件,就使用这个“所有匹配模式”规则来重建这个目标。
看一个例子,假设存在一个命名为“Makefile”的makefile文件,其中包含了目标“bar”的重建规则和其它一些规则,同时存在一个命名为“GNUmakefile”的文件,此文件内容如下:

#sample GNUmakefile
foo:
frobnicate > foo

%: force
@$(MAKE) -f Makefile $@
force: ;

执行“make foo”时,make将处理工作目录下的“GNUmakefile”并执行目标“foo”所在的规则,创建(重建)目标文件“foo”的命令是:“frobnicate > foo”(在“GNUmakefile”中给出了创建它的规则)。如果执行另外一个命令“make bar”,由于目标 “bar” 没有在“GUNmakefile”中给出创建(重建)规则,那么make将使用“所有匹配模式”规则来创建(重建)它”。在“GUNmakefile”中此规则所定义的命令为:“$(MAKE) -f Makefile bar”,这个命令将再次执行“make”并将文件“Makefile”作为此次需要处理的makefile文件,而在文件“Makefile”中定义了目标“bar”的重建规则,因此make就使用这个规则来重建目标“bar”。此过程同样适用于其它在 “GNUmakefile”中没有给出重建规则的目标。此方式灵活之处在于:如果在“Makefile”文件中同样存在更新文件“foo”的规则,由于make执行时只会处理文件“GUNmakefile”并在其中能够找到目标“foo”的重建规则,所以make不会去执行这个“所有模式匹配规则”(例子中目标“%”所在的规则)。这样的做法避免了由于使用指示符“include”包含一个makefile文件,所可能带来的目标规则重复定义问题。
注意到在文件“GNUmakefile”,模式规则的模式只使用了单独的“%”(因此称它为“所有模式匹配规则”),它匹配任何一个目标;它的依赖是“force”,“force”这个依赖保证了即使所要重建的目标文件已经存在于当前目录,这个规则也会被执行(在普通规则中,如果目标文件已存在,则需要根据规则依赖文件的修改情况决定是否重建规则的目标文件);“force”所在规则的定义使用空命令,目的为了防止make执行时试图为重建这个目标(“force”)而寻找规则,否则为了重建目标“force”,make将会使用模式规则“%: force”,导致make执行过程陷入死循环。
1.9 make如何解析makefile文件
GUN make的执行过程分为两个阶段。
第一阶段:读取所有makefile文件(包括“MAKIFILES”变量指定的、命令行选项“-f(--file)”指定的、以及在主Makeifle中使用指示符“include”指定包含的),内建所有包含的变量、明确规则和隐含规则,并建立所有目标和依赖之间的依赖关系结构链表。
在第二阶段:根据第一阶段已经建立的依赖关系结构链表决定哪些目标需要被重建,并使用对应的规则按照一定的顺序依次重建这些目标。
理解make执行过程的两个阶段是很重要的。理解这一过程将使我们了解make在执行过程中,变量以及函数在什么时机、如何被展开。变量和函数的展开时机和机制决定Makefile如何被执行,这也是书写Makefile需要关注的地方。本节将对make执行时,变量引用的展开时机、以及不同阶段变量引用的展开过程进行简单总结(理解变量和函数引用的展开阶段,对正确使用变量非常有帮助)。首先,明确两个概念:
1. 变量函数的立即展开:在make执行的第一阶段中如果变量和函数的引用被展开,那么称此展开是“立即”的。在make执行的第一阶段,根据规则建立整体依赖关系结构链表时,首先会对规则中一些地方所引用的变量进行展开(建立正确的整体依赖关系链表,对规则某些地方变量和函数引用的展开是先决条件)。
2. 变量函数的延后展开:变量、函数不是在make执行的第一阶段展开的,称为展开是“延后”的。被“延后”展开的变量和函数,其展开发生在引用此变量的规则真正被执行时(规则命令中引用变量或者函数),或是make处理的第二阶段。
在这里通过简单的语言描述,可能比较晦涩,不容易理解。没有关系,通过后续章节内容的阅读,将会一步步地熟悉make的执行过程。阅读过程中可以回过头来参考本节内容。相信在阅读完本书之后,对make的整个执行过程,会有比较清晰地理解。

1.9.1 变量取值
变量定义解析的规则如下:

IMMEDIATE = DEFERRED
IMMEDIATE ?= DEFERRED
IMMEDIATE := IMMEDIATE
IMMEDIATE += DEFERRED or IMMEDIATE
define IMMEDIATE
DEFERRED
Endef

对于使用追加符“+=”赋值的变量,如果此前这个变量被定义为一个简单变量(使用 :=定义的)则它是立即展开的变量,其它情况都是“延后”展开的变量。
1.9.2 条件语句
所有使用到条件语句在产生分支的地方,make执行时,首先根据预设条件将正确地分支展开作为所要解析执行的内容。就是说条件分支的展开是“立即”的。其中包括:“ifdef”、“ifeq”、“ifndef”和“ifneq”所确定的所有分支。
1.9.3 规则的定义
所有的规则在make执行时,都按照如下的模式展开:

IMMEDIATE : IMMEDIATE ; DEFERRED
DEFERRED

其中,规则的目标和依赖列表中引用的变量,是立即展开变量。而规则的命令行中引用的变量是延后展开的变量。此模式对所有的规则有效,包括明确规则、模式规则、后缀规则、静态模式规则。
1.10 总结
make的执行过程如下:
1. 依次读取变量“MAKEFILES”指定的makefile文件列表;
2. 读取工作目录下的makefile文件(根据命名的查找顺序“GNUmakefile”,“makefile”,“Makefile”,首先找到那个就读取那个);
3. 依次读取工作目录makefile文件中使用指示符“include”指定包含的文件;
4. 为已读取的所有makefile文件寻找重建规则,尝试更新已读取的makefile文件。在已经读取的所有规则中,如果存在这样一个规则,其目标文件是一个已读取的makefile文件,那么就执行这个规则来更新对应的makefile文件。如果任何一个已读取的makefile在此步骤被更新,那么从第一步开始重新执行;
5. 初始化变量值并展开那些需要立即展开的变量和函数,并根据预设条件确定执行分支;
6. 根据“终极目标”以及其它目标(可能是终极目标的依赖、或者终极目标依赖的依赖)的依赖关系建立整体依赖关系链表;
7. 依照上一步所建立的整体依赖关系链表,按照顺序依次执行 “终极目标”所有依赖(或者它的依赖)的更新规则(如果规则的依赖文件中任一个的时间戳比目标文件新,则使用规则定义的命令重建规则的目标文件);
8. 执行“终极目标”所在的规则。

说明:
执行一个规则的过程是这样的:
对于一个存在的规则(明确规则和隐含规则)首先,make程序将比较目标文件和所有的依赖文件的时间戳。如果目标的时间戳比所有依赖文件的时间戳更新(依赖文件在上一次执行make之后没有被修改),那么什么也不做。否则(依赖文件中的某一个或者全部在上一次执行make后已经被修改过),规则所定义的重建目标的命令将会被执行。这是make工作的基础,也是其执行规制所定义命令的依据(后续讨论规则时将会对此详细地说明)。



本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/9858/showart_1070257.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP