300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > makefile 学习笔记 九:makefile中使用隐式规则

makefile 学习笔记 九:makefile中使用隐式规则

时间:2023-07-28 22:32:51

相关推荐

makefile 学习笔记 九:makefile中使用隐式规则

详细内容见 《GNU make》 10 Using Implicit Rules 章节。

某些重制目标文件的标准方法经常被使用。例如,创建目标文件的一种习惯方法是使用 C 编译器 cc 从 C 源文件中获取目标文件。

隐式规则告诉 A 如何使用习惯技巧,这样当你想要使用它们时,就不必详细地指定它们。例如,有一个用于C编译的隐式规则。文件名确定运行哪些隐式规则。例如,C编译通常接受 .c 文件并生成 .o 文件。因此,当 make 看到这个文件名结尾的组合时,它应用了 C 编译的隐式规则。

一系列隐式规则可以按顺序应用;例如,make 将通过 .c 文件从 .y 文件重制 .o 文件。

内置隐式规则在其配方中使用多个变量,因此,通过更改变量的值,您可以更改隐式规则的工作方式。例如,变量 CFLAGS 通过 C 编译的隐式规则控制给 C 编译器的标志。

您可以通过编写模式规则来定义自己的隐式规则

后缀规则是定义隐式规则的一种更有限的方式模式规则更加通用和清晰,但是后缀规则为了兼容性而保留

一、用隐式规则

要让 make 找到更新目标文件的习惯方法,您所要做的就是避免自己指定配方要么写一个没有配方的规则,要么根本不写规则然后 make 会根据存在或可以创建的源文件类型,找出使用哪种隐式规则

例如,假设makefile是这样的:

foo : foo.o bar.occ -o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)

因为你提到了 foo.o 但没有给它一个规则,make 将自动寻找一个隐式规则来告诉如何更新它。无论文件 foo.o 当前是否存在,都会发生这种情况。

如果找到隐式规则,它可以同时提供配方和一个或多个先决条件(源文件)。如果您需要指定隐含规则不能提供的额外先决条件,比如头文件,那么您可能希望为 foo.o 编写没有配方的规则。

每个隐式规则都有一个目标模式和先决模式。可能有许多隐式规则具有相同的目标模式。例如,许多规则组成 ‘.o’ 文件:第一种,用 C 编译器从 ‘.c’ 文件生成;第二种,用Pascal编译器从 ‘.p’ 文件;等等。所以,如果你有一个文件 foo.c,make将运行C编译器;否则,如果你有一个文件 foo.p,make 将运行 Pascal 编译器;等等。

当然,在编写 makefile 时,您知道希望 make 使用哪个隐式规则,也知道它会选择那个,因为您知道应该存在哪些可能的先决条件文件

在上面,我们说过,如果所需的先决条件"存在或可以制定",则适用隐含规则。如果在makefile中显式地提到文件作为目标或先决条件,或者可以递归地找到隐式规则来创建它,则可以创建该文件。当一个隐式先决条件是另一个隐式规则的结果时,我们就说发生了链接。

通常,为每个目标和每个没有配方的双冒号规则搜索隐式规则只作为先决条件提及的文件被认为是一个目标,它的规则没有指定任何内容,因此会对它进行隐式规则搜索

注意显式先决条件不会影响隐式规则搜索。例如,请考虑以下显式规则:

foo.o: foo.p

foo.p 的先决条件并不一定意味着 make 将根据隐式规则重制 foo.o,从Pascal源文件到 .p 文件,生成一个对象文件,一个 .o 文件。

如果不希望将隐式规则用于没有配方的目标,则可以通过编写分号为该目标指定一个空配方

二、内置规则目录

下面是预定义的隐式规则的目录,除非 makefile 显式覆盖或取消它们,否则这些规则始终可用。‘-r’ 或 ‘–no-builtin-rules’ 选项取消所有预定义规则。

本手册仅记录基于 posix 的操作系统上可用的默认规则。其他操作系统,如 VMS、Windows、OS/2 等。可能具有不同的默认规则集。要查看 GNU make 版本中可用的缺省规则和变量的完整列表,请在没有 makefile 的目录中运行 ‘make -p’。

并不是所有这些规则都会被定义,即使 ‘-r’ 选项没有被给出。许多预定义的隐式规则在 make 中作为后缀规则实现,因此将定义哪些规则取决于后缀列表(特殊目标 .SUFFIXES 的先决条件列表)。默认后缀列表为:

.out, .a, .ln, .o, .c, .cc, .C, .cpp, .p, .f, .F, .m, .r, .y, .l, .ym, .lm, .s, .S, .mod, .sym, .def, .h, .info, .dvi, .tex, .texinfo, .texi, .txinfo, .w, .ch .web, .sh, .elc, .el.

下面描述的所有隐式规则(其先决条件具有这些后缀之一)实际上都是后缀规则如果修改后缀列表,则唯一有效的预定义后缀规则将是由您指定的列表中的一个或两个后缀命名的规则;后缀未在列表中的规则将被禁用

1、编译 C 程序

n.o 是用 ‘$(CC) $(CPPFLAGS) $(CFLAGS) -c’ 形式的配方自动从 n.c 生成的。

2、编译 C++ 程序

n.o 是用 ‘$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c’ 形式的配方自动从 、n.cpp 或 n.C 生成的。我们鼓励您使用后缀 ‘.cc’ 来表示c++源文件,而不是后缀 ‘.C’。

3、编译 Pascal 程序

n.o 是用 ‘$(PC) $(PFLAGS) -c’ 形式的配方自动从 n.p 生成的。

4、编译 Fortran 和 Ratfor 程序

n.o 是通过运行Fortran编译器自动从 n.r、n.F 或 n.f 生成的。使用的精确配方如下:

‘.f’:‘$(FC) $(FFLAGS) -c’

‘.F’:‘$(FC) $(FFLAGS) $(CPPFLAGS) -c’、

‘.r’:‘(FC) $(FFLAGS) $(RFLAGS) -c’

5、预处理Fortran和Ratfor程序

n.f 是由 n.r 或 n.F自动合成的。此规则仅运行预处理器,以将 Ratfor 或预处理的 Fortran 程序转换为严格的 Fortran 程序。使用的精确配方如下:

‘.F’:‘$(FC) $(CPPFLAGS) $(FFLAGS) -F’‘.r’:‘$(FC) $(FFLAGS) $(RFLAGS) -F’

6、编译 Modula-2 程序

n.sym 由 n.def 制成,配方为$(M2C) $(M2FLAGS) $(DEFFLAGS)。n.o 由 n.mod 制成;形式是:'$(M2C) $(M2FLAGS) $(MODFLAGS)'

7、链接单个目标文件

n 通过 C 编译器运行链接器(通常称为ld)从 n.o 自动生成。使用的精确配方是 ‘$(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)’。

这个规则对于只有一个源文件的简单程序来说是正确的。如果有多个目标文件(可能来自不同的其他源文件),其中一个具有与可执行文件匹配的名称,它也会做正确的事情。因此:

x: y.o z.o

当 x.c、y.c 和 z.c 全部存在时将执行:

cc -c x.c -o x.occ -c y.c -o y.occ -c z.c -o z.occ x.o y.o z.o -o xrm -f x.orm -f y.orm -f z.o

在更复杂的情况下,例如当没有对象文件的名称来自可执行文件名时,您必须为链接编写一个显式的配方。

每一种自动生成为 ‘.o’ 对象文件的文件都会被编译器自动链接(‘$(CC)’、‘$(FC)’ 或 ‘$(PC)’;C编译器用 ‘$(CC)’ 汇编 ‘.s’ 文件)而没有 ‘-c’ 选项。这可以通过使用 ‘.o’ 对象文件作为中间体来完成,但是编译和链接在一个步骤中更快,所以这就是它的方法。

8、用于C程序的Yacc

n.c 是通过使用配方 ‘$(YACC) $(YFLAGS)’ 运行 Yacc 从 n.y 自动生成的。

9、用于C程序的Lex

n.c 是通过运行Lex从 n.l 自动生成的。实际的配方是 ‘$(LEX) $(LFLAGS)’。

10、用于Ratfor程序的Lex

n.r 是通过运行 Lex 从 n.l 自动生成的。实际配方是 ‘$(LEX) $(LFLAGS)’。

对所有 Lex 文件使用相同的后缀 ‘.l’,无论它们生成 C 代码还是 Ratfor 代码,这使得 make 无法自动确定在任何特定情况下使用的两种语言中的哪一种。如果调用 make 从 ‘.l’ 文件重制对象文件,它必须猜测要使用的编译器。它会猜测 C 编译器,因为这更常见。如果您使用的是Ratfor,请在makefile中提到 ‘n.r’,确保 makefile 知道这一点。或者,如果你只使用Ratfor,不使用C文件,从隐含的规则后缀列表中删除 ‘.c’:

.SUFFIXES:.SUFFIXES: .o .r .f .l …

11、从C、Yacc或Lex程序中创建Lint库

n.ln 是通过运行 lint 从 n.c 构成的。精确的配方是 ‘$(LINT) $(LINTFLAGS) $(CPPFLAGS) -i’。从 n.y 或 n.l 生成的 C 代码使用相同的配方。

12、TEX 和 Web

n.dvi 由配方$(TEX)的 n.tex 制成。n.tex 是由 n.web 和 ‘$(WEAVE)’ 构成的,或者是由 n.w (如果 n.ch 存在或可以构成)和’$(CWEAVE)'构成的。n.p 是由 n.web 和 ‘$(TANGLE)’ 构成的,n.c 是由 n.w 和 ‘$(CTANGLE)’ 构成的(如果 n.ch 存在或可以构成的话,也可以由 n.ch 构成)。

13、RCS

如果需要,将从名为 n,v 或 RCS/n,v 的 RCS 文件中提取任何文件n。使用的精确配方是 ‘$(CO) $(COFLAGS)’。如果 RCS 已经存在,即使 RCS 文件更新,也不会从 RCS 中提取 n。RCS的规则是终端的,因此RCS文件不能从其他源生成;它们必须真实存在。

14、SCCS

如果需要,将从名为 s.n 或 SCCS/s.n 的SCCS文件中提取任何文件 n。使用的精确配方是 ‘$(GET) $(GFLAGS)’。SCCS的规则是终端,因此SCCS文件不能从其他源文件生成;它们必须真实存在。

为了使用SCCS,可以从 n.sh 复制文件n并使其可执行(由每个人执行)。这适用于校验 SCCS 的 shell 脚本。因为 RCS 保留了文件的执行权限,所以不需要在 RCS 中使用这个特性。

我们建议您避免使用SCCS。CS被广泛认为是更好的,而且是免费的。通过选择自由软件来代替可比(或劣)的专有软件,你就支持了自由软件运动。

通常,您只想更改上表中列出的变量,这些变量将在下一节中进行说明。

但是,内置隐式规则中的配方实际上使用了诸如 COMPILE.c、LINK.p 和 PREPROCESS.S 这样的变量,其值包含上面列出的配方。

make 遵循规则的约定,即编译a源文件时使用变量B。类似地,从 .x 文件生成可执行文件的规则使用 LINK.x;预处理 .x 文件的规则使用 PREPROCESS.x。

每个生成目标文件的规则都使用变量 OUTPUT_OPTION。make 将此变量定义为包含 ‘-o $@’ 或为空,具体取决于编译时选项。当源文件位于不同的目录时,您需要 ‘-o’ 选项来确保输出进入正确的文件,就像使用VPATH时一样。然而,某些系统上的编译器不接受对象文件的 ‘-o’ 开关。如果您使用这样的系统,并且使用VPATH,一些编译将把它们的输出放在错误的位置。这个问题的一个可能的解决办法是给 OUTPUT_OPTION 一个值 ‘; mv $*.o $@’。

三、隐式规则使用的变量

详细内容见《makefile 变量》章节。

四、隐式规则链

有时,一个文件可以通过一系列隐式规则来创建。例如,可以先运行Yacc,然后运行cc,从 n.y 生成文件 n.o。这样的序列称为链

如果文件 n.c 存在,或者在 makefile 中提到了,则不需要进行特殊的搜索:make 发现对象文件可以通过C编译从 n.c 生成;稍后,在考虑如何创建 n.c 时,将使用运行Yacc的规则。最终,n.c 和 n.o 都会更新。

然而,即使 n.c 不存在,也没有被提及,make 知道如何把它想象成 n.o 或 n.y 之间缺失的一环!在这种情况下,n.c 称为中间文件。一旦 make 决定使用中间文件,它就会被输入到数据库中,就好像在 makefile 中提到过一样,以及说明如何创建它的隐式规则。

中间文件像所有其他文件一样,使用它们的规则进行重制。但是中间文件有两种不同的处理方式。

第一个区别是如果中间文件不存在会发生什么。如果普通文件 b 不存在,并且 make 考虑了依赖于 b 的目标,则它总是创建 b,然后从 b 更新目标。但是如果b是一个中间文件,那么 make 就可以不管它了。它不会麻烦更新b或最终目标,除非b的某些先决条件比那个目标更新,或者有其他原因更新那个目标。

第二个区别是,如果 make 确实创建了 b 以更新其他内容,则在不再需要 b 之后会将其删除。因此,在 make 之前不存在的中间文件在 make 之后也不存在。make 通过打印 ‘rm -f’ 命令来显示它正在删除哪个文件,从而向您报告删除操作。

通常,如果 makefile 中提到一个文件作为目标或先决条件,那么它就不能是中间文件。但是,您可以通过将文件列为特殊目标 .INTERMEDIATE 的先决条件来显式地将其标记为中间文件。即使以其他方式显式地提到该文件,该命令也会生效。

通过将中间文件标记为辅助文件,可以防止自动删除该文件。为此,将它列为特殊目标 .SECONDARY 的先决条件。当文件是辅助文件时,make 不会仅仅因为该文件尚不存在而创建该文件,但 make 不会自动删除该文件。将文件标记为辅助文件也会将其标记为中间文件。

您可以列出隐式规则(如 ‘%.o’ )的目标模式,作为特殊目标 .PRECIOUS 的先决条件,以保留由目标模式与文件名称匹配的隐式规则生成的中间文件;

一条链可以包含两个以上的隐式规则。例如,通过运行RCS、Yacc和cc,可以从 RCS/foo.y,v 创建一个foo文件。然后 foo.y 和 foo.c 都是中间文件,在最后被删除。

一个隐式规则在一个链中不能出现多次。这意味着 make 甚至不会考虑像通过运行链接器两次从 foo.o.o 制作 foo 这样荒谬的事情。这个约束还有一个额外的好处,那就是防止在搜索隐式规则链时出现无限循环。

有一些特殊的隐式规则来优化某些情况,否则这些情况将由规则链处理。例如,从 foo.c 生成 foo 可以通过编译和使用单独的链式规则链接来处理,使用 foo.o 作为中间文件。但是实际发生的情况是,在这种情况下,使用一个cc命令进行编译和链接的特殊规则。优化后的规则优先于分步链使用,因为它在规则的排序中更早。

最后,出于性能原因,make 在搜索规则以构建隐式规则的先决条件时,不会考虑非终结匹配任何规则(例如:‘%:’)。

五、定义和重新定义模式规则

您可以通过编写模式规则来定义隐式规则。模式规则看起来像普通规则,除了它的目标包含字符 ‘%’ (恰好是其中一个)。目标被认为是匹配文件名的模式;‘%’ 可以匹配任何非空的子字符串,而其他字符只能匹配自己。先决条件同样使用 ‘%’ 来显示它们的名称与目标名称之间的关系。

因此,模式规则 ‘%.o : %.c’ 说明了如何从另一个文件 stem.c 生成任何文件 stem.o。

请注意,在模式规则中使用 ‘%’ 进行展开时,在任何变量或函数展开之后进行展开,这些展开发生在读取 makefile 时。

1、模式规则简介

模式规则包含目标中的字符 ‘%’(恰好是其中一个);否则,它看起来就像一个普通的规则。目标是一个匹配文件名的模式;‘%’ 匹配任何非空的子字符串,而其他字符只匹配它们自己。

例如,A作为模式匹配以B结尾的任何文件名。‘s.%.c’ 作为模式匹配任何以 ‘s.’ 开头,以 ‘.c’ 结尾并且至少有5个字符长的文件名(必须至少有一个字符匹配 ‘%’)。‘%’ 匹配的子字符串称为词干。

在模式规则的前提下,‘%’ 代表的是与目标中的 ‘%’ 匹配的同一干。为了应用模式规则,它的目标模式必须匹配所考虑的文件名,并且它的所有先决条件(在模式替换之后)必须命名存在的或可以创建的文件。这些文件成为目标的先决条件。

因此,规则的形式:

%.o : %.c ; recipe…

指定如何创建文件 n.o,前提是 n.c 存在或可以创建另一个文件 n.c。

也可能有不使用 ‘%’ 的先决条件;这样的先决条件附加到该模式规则创建的每个文件。这些不变的先决条件有时是有用的。

模式规则不需要任何包含 ‘%’ 的先决条件,或者实际上根本不需要任何先决条件。这样的规则实际上是通用通配符。它提供了一种方法来创建任何与目标模式匹配的文件。

多个模式规则可以匹配一个目标。在这种情况下,make将选择"最合适"规则。

模式规则可能有多个目标;但是,每个目标必须包含一个 ‘%’ 字符。无论模式规则是否使用 ‘:’ 或 ‘&:’ 分隔符,它们总是被视为分组目标。

2、模式规则示例

下面是一些在 make 中实际预定义的模式规则的例子。首先,将 ‘.c’ 文件编译成 ‘.o’ 文件的规则:

%.o : %.c$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

定义一个规则,该规则可以从 x.o 创建任何文件 x.c。配方使用自动变量 ‘ @ ′ 和 ′ @' 和 ' @′和′<’ 在应用规则的每种情况下替换目标文件和源文件的名称。

下面是第二个内置规则:

% :: RCS/%,v$(CO) $(COFLAGS) $<

定义了一个规则,它可以从子目录RCS中的相应文件 x,v 中创建任何文件 x。因为目标是 ‘%’,所以只要存在适当的先决条件文件,该规则将适用于任何文件。双冒号使规则成为终端,这意味着它的先决条件可能不是中间文件。

此模式规则有两个目标:

%.tab.c %.tab.h: %.ybison -d $<

这告诉 make ,配方 ‘bison -d x.y’ 可以同时做出 x.tab.c 和 x.tab.h。

这告诉 make ,配方 ‘bison -d x.y’ 可以同时做出 x.tab.c 和 x.tab.h。如果 foo 文件依赖于 parse.tab.o 和 scan.o 文件,scan.o 文件依赖于 parse.tab.h 文件,当E被改变时,配方 ‘bison -d parse.y’ 只会被执行一次,同时满足 parse.tab.o 和 scan.o 的前提条件。假设文件 parse.tab.o 将从 parse.tab.c 重新编译,文件 scan.o 将从 scan.c 重新编译,而foo将从 parse.tab.o、scan.o 和它的其他先决条件链接,然后它将愉快地执行。

3、自动变量

详细信息见 《makefile 变量》 章节。

4、多模式匹配

目标模式由前缀和后缀之间的 ‘%’ 组成,其中一个或两个都可以为空。只有当文件名以前缀开头,后缀结尾,且没有重叠时,模式才匹配文件名。前缀和后缀之间的文本称为词干。因此,当模式 ‘%.o’ 与文件名 test.o 匹配时,词干为 ‘test’。模式规则的先决条件通过替换字符 ‘%’ 来转换为实际的文件名。因此,如果在同一个示例中,其中一个先决条件写为 ‘%.c’,则扩展为 ‘test.c’。

当目标模式不包含斜杠(它通常不包含斜杠)时,在文件名与目标前缀和后缀进行比较之前,将从文件名中删除文件名中的目录名。在文件名与目标模式进行比较之后,目录名以及以它们结尾的斜杠将被添加到由模式规则的先决模式和文件名生成的先决文件名中。只有在寻找要使用的隐式规则时才会忽略目录,而不是在该规则的应用中。因此,‘e%t’ 与文件名 src/eat 匹配,以 src/a 作为词干。当先决条件转换为文件名时,将主干中的目录添加到前面,而词干的其余部分将替换为 ‘%’。具有先决条件模式 ‘c%r’ 的词干 ‘src/a’ 给出了文件名 src/car。

只有当存在与文件名匹配的目标模式,且该规则中的所有先决条件存在或可以构建时,才能使用模式规则来构建给定的文件。您编写的规则优先于那些内置的规则。但是请注意,前提条件实际存在或被提到的规则总是优先于前提条件必须通过链接其他隐式规则来实现的规则。

可能会有多个模式规则满足这些标准。在这种情况下,make 将选择具有最短茎的规则(即最具体匹配的模式)。如果多个模式规则具有最短的词干,则 make 将选择在 makefile 中找到的第一个词干。

这种算法的结果是更具体的规则优于更通用的规则;例如:

%.o: %.c$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@%.o : %.f$(COMPILE.F) $(OUTPUT_OPTION) $<lib/%.o: lib/%.c$(CC) -fPIC -c $(CFLAGS) $(CPPFLAGS) $< -o $@

给定这些规则并被要求构建 bar.c 和 bar.f 同时存在的 bar.o, make 将选择第一条规则并将 bar.c 编译成 bar.o。同样在 bar.c 不存在的情况下,make 会选择第二条规则,将 bar.f 编译成 bar.o。

如果要求 make 构建 lib/bar.o 并且 lib/bar.c 和 lib/bar.f 都存在,则将选择第三个规则,因为此规则 (‘bar’) 的词干比第一个规则 (‘lib/bar’) 的词干短。如果 lib/bar.c 不存在,则第三条规则不符合条件,将使用第二条规则,即使词干较长。

5、匹配任何模式规则

当模式规则的目标是 ‘%’ 时,它匹配任何文件名。我们称这些规则为任何匹配规则。它们非常有用,但是 make 需要花费大量的时间来考虑它们,因为它必须考虑作为目标或先决条件列出的每个文件名的每一个这样的规则。

假设makefile提到了A。对于这个目标,make 必须考虑通过链接文件 foo.c.o 来实现它,或者通过 C 编译和链接 foo.c.c 文件一步实现,或者通过 Pascal 编译和链接 foo.c.p 文件一步实现,和许多其他可能性。

我们知道这些可能性是荒谬的,因为 foo.c 是一个 C 源文件,而不是一个可执行文件。如果 make 确实考虑了这些可能性,它将最终拒绝它们,因为像 foo.c.o 和 foo.c.p 这样的文件将不存在。但这些可能性是如此之多,如果 make 必须考虑它们,它会运行得非常缓慢

为了提高速度,我们在 make 考虑匹配任何规则的方式上设置了各种限制。可以应用两种不同的约束,每次定义任何匹配规则时,都必须为该规则选择其中一种。

一种选择是通过使用双冒号定义匹配任何规则,将其标记为终端。当一个规则是终端时,除非它的先决条件确实存在,否则它不会被应用。使用其他隐式规则可以建立的先决条件还不够好。换句话说,不允许在终端规则之外进行进一步的链接。

例如,从RCS和SCCS文件中提取源的内置隐式规则是终端规则;因此,如果文件 foo.c,v 不存在, make 甚至不会考虑将其作为来自 foo.c,v.o 或 RCS/SCCS/s.foo.c,v 的中间文件。RCS和SCCS文件通常是最终源文件,不应该从任何其他文件重新创建;因此,make 可以通过不寻找重做它们的方法来节省时间。

如果未将匹配规则标记为终端,则它是非终端规则。非终端匹配规则不能应用于隐式规则的先决条件,也不能应用于指示特定数据类型的文件名。如果某些不匹配的隐式规则目标与某个数据源匹配,则文件名指示特定类型的数据。

例如,文件名 foo.c 匹配模式规则 ‘%.c : %.y’(运行Yacc的规则)的目标。无论该规则是否实际适用(仅当存在文件 foo.y 时才会发生),其目标匹配的事实足以避免考虑文件 foo.c 的任何非终结符匹配规则。因此,make甚至不会考虑尝试将 foo.c 作为 foo.c.o、foo.c.c、foo.c.p 等的可执行文件。

此约束的动机是,非终端匹配规则用于创建包含特定类型数据(如可执行文件)的文件,而具有可识别后缀的文件名表示某些其他特定类型的数据(如 C 源文件)。

提供特殊的内置虚拟模式规则仅用于识别某些文件名,以便不会考虑非终端匹配规则。这些虚拟规则没有先决条件,也没有配方,并且出于所有其他目的,它们将被忽略。例如,内置隐式规则:

%.p :

存在以确保 Pascal 源文件(如 foo.p)与特定的目标模式匹配,从而防止浪费时间查找 foo.p.o 或 foo.p.c。

6、取消隐式规则

您可以通过定义具有相同目标和先决条件但不同配方的新模式规则来覆盖内置的隐式规则(或您自己定义的规则)。定义新规则后,内置规则将被替换。新规则在隐式规则序列中的位置由您编写新规则的位置确定。

您可以通过定义具有相同目标和先决条件但没有配方的模式规则来取消内置的隐式规则。例如,以下命令将取消运行汇编程序的规则:

%.o : %.s

六、定义最后的默认规则

你可以通过编写一个终端匹配任何东西的模式规则来定义一个最后的隐式规则。这和其他模式规则一样;它唯一的特殊之处在于它可以匹配任何目标。因此,这样的规则配方将用于所有没有自己配方且没有其他隐式规则适用的目标和先决条件。

例如,在测试makefile时,您可能不关心源文件是否包含真正的数据,而只关心它们是否存在。然后你可以这样做:

%::touch $@

使所需的所有源文件(作为先决条件)自动创建

相反,您可以定义一个配方,用于根本没有规则的目标,甚至那些没有指定配方的目标。你可以通过为目标 .DEFAULT 编写规则来做到这一点。这样的规则配方用于所有在任何显式规则中不作为目标出现的前提条件,并且没有任何隐式规则适用于这些前提条件。当然,除非你自己写一个规则,否则是没有规则的。

如果你使用 .DEFAULT 没有配方或先决条件:

.DEFAULT:

清除以前为 .DEFAULT 存储的配方。然后做出动作,就好像你从来没有定义过 .DEFAULT 一样。

如果您不希望目标从匹配任何模式规则或 .DEFAULT 获取配方,但也不希望为目标运行任何配方,则可以为其提供一个空配方。

您可以使用最后的方法来覆盖另一个makefile的一部分。

七、老式的后缀规则

后缀规则是为 make 定义隐式规则的老式方法。后缀规则已经过时了,因为模式规则更通用、更清晰。为了与旧的makefile兼容,GNU make 支持它们。它们有两种类型:双后缀和单后缀。

双后缀规则由一对后缀定义:目标后缀和源后缀。它匹配任何名称以目标后缀结尾的文件。相应的隐式先决条件是用文件名中的源后缀替换目标后缀。双后缀规则 ‘.c.o’(它的目标和源后缀是’.o’ 和 ‘.c’)等价于模式规则 ‘%.o : %.c’。

单后缀规则由单个后缀定义,即源后缀。它匹配任何文件名,通过附加源后缀来创建相应的隐式先决条件名。源后缀为 ‘.c’ 的单后缀规则等价于模式规则 ‘% : %.c’。

通过将每个规则的目标与已定义的已知后缀列表进行比较,可以识别后缀规则定义。当 make 看到一个目标为已知后缀的规则时,该规则被认为是单后缀规则。当 make 看到一个规则的目标是连接在一起的两个已知后缀时,该规则被视为双后缀规则。

以下是定义编译 C 源文件的规则的老式方法:

.c.o:$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<

后缀规则本身不能有任何先决条件。如果有,则将它们视为具有有趣名称的普通文件,而不是后缀规则。因此,规则:

.c.o: foo.h$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<

说明如何从先决文件 foo.h 创建文件 .c.o,与模式规则完全不同:

%.o: %.c foo.h$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<

它告诉如何从 ‘.c’ 文件生成 .o 文件,并使所有使用此模式规则的 ‘.o’ 文件也依赖于 foo.h。

没有配方的后缀规则也是毫无意义的。它们不会像没有配方的模式规则那样删除以前的规则。它们只需输入作为数据库中的目标连接起来的后缀或后缀对。

已知的后缀只是特殊目标 .SUFFIXES 的先决条件的名称。您可以通过为 .SUFFIXES 编写添加更多先决条件的规则来添加自己的后缀,如:

.SUFFIXES: .hack .win

它将 ‘.hack’ 和 ‘.win’ 添加到后缀列表的末尾。

如果您希望消除默认的已知后缀,而不是仅仅添加它们,那么为 .SUFFIXES 编写一个没有先决条件的规则。通过特殊分配,这消除了 .SUFFIXES 的所有现有先决条件。然后,您可以编写另一个规则来添加您想要的后缀。例如:

.SUFFIXES: # Delete the default suffixes.SUFFIXES: .c .o .h # Define our suffix list

‘-r’ 或 ‘–no-builtin-rules’ 标志导致默认的后缀列表为空。

变量 SENDIX 被定义为默认的后缀列表,然后再 make 读取任何生成文件。您可以使用特殊目标 .SUFFIXES 的规则更改后缀列表,但这不会更改此变量。

八、隐式规则搜索算法

下面是 make 用于搜索目标 t 的隐式规则的过程。对于没有配方的每个双冒号规则、没有配方的普通规则的每个目标以及不是任何规则目标的每个前提条件,都遵循这个过程。在搜索规则链时,对于来自隐式规则的先决条件,也会递归地遵循它。

在此算法中没有提到后缀规则,因为一旦读入makefile,后缀规则就会转换为等效的模式规则。

对于形式为 ‘archive(member)’ 的归档成员目标,运行两次以下算法,第一次使用整个目标名称t,如果第一次运行没有发现规则,则第二次使用 ‘(member)’ 作为目标t。

1、将 t 分割成一个目录部分,称为d,其余部分称为n。例如,如果 t 是 ‘src/foo.o’,那么 d 是 ‘src/’, n 是 ‘foo.o’。

2、列出其中一个目标匹配t或n的所有模式规则。如果目标模式包含斜杠,则匹配t;否则,针对 n。

3、如果该列表中的任何规则不是任何匹配规则,或者t是隐式规则的先决条件,则从该列表中删除所有非终结符任何匹配规则。

4、从列表中移除所有没有配方的规则。

5、对于列表中的每个模式规则:

找出目标模式中与 ‘%’ 匹配的 t 或 n 的非空部分 s。

通过将 s 替换为 ‘%’ 来计算先决条件名称;如果目标模式不包含斜杠,则将其附加到每个先决条件名称的前面。

测试所有先决条件是否存在或应该存在(如果在makefile中提到一个文件名作为目标或显式的先决条件,那么我们就说它应该存在)。如果所有先决条件都存在或应该存在,或者没有先决条件,则适用此规则。

6、如果到目前为止尚未找到模式规则,请更努力地尝试。对于列表中的每个模式规则:

如果规则是终端,请忽略它并继续执行下一个规则。

与前面一样计算先决条件名称。

对于每个不存在的先决条件,请递归地遵循此算法,以查看是否可以通过隐式规则创建先决条件。

如果所有先决条件都存在,应该存在,或者可以通过隐式规则实现,则此规则适用。

如果没有隐含规则适用,则适用 .DEFAULT 的规则(如果有的话)。在这种情况下,给t一个和 .DEFAULT 一样的配方。否则,没有 t 的配方。

规则适用于被发现后,为每个目标模式匹配的规则以外的一个t或n,模式中的一个被替换为年代和合成文件名存储在配方重塑目标文件执行。在执行配方之后,将这些存储的文件名输入到数据库中,并将其标记为已更新,并且具有与文件t相同的更新状态。

当为t执行模式规则的配方时,自动变量被设置为对应于目标和先决条件。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。
相关阅读
Makefile学习笔记-备忘

Makefile学习笔记-备忘

2023-09-08

Makefile学习笔记4

Makefile学习笔记4

2022-12-07

makefile学习笔记

makefile学习笔记

2023-11-27

Makefile 学习笔记

Makefile 学习笔记

2023-07-18