GNU Make 使用手册(于凤昌中译版)

GNU Make 使用手册(中译版)
翻译:于凤昌

GNU make Version 3.79
April 2000
Richard M. Stallman and Roland McGrath

1 Make 概述

Make 可自动决定一个大程序中哪些文件需要重新编译,并发布重新编译它们的命令。本版本GNU Make使用手册由Richard M. Stallman and Roland McGrath编著,是从Paul D. Smith撰写的V3.76版本发展过来的。

GNU Make符合IEEE Standard 1003.2-1992 (POSIX.2) 6.2章节的规定。

因为C语言程序更具有代表性,所以我们的例子基于C语言程序,但Make并不是仅仅能够处理C语言程序,它可以处理那些编译器能够在Shell命令下运行的的各种语言的程序。事实上,GNU Make不仅仅限于程序,它可以适用于任何如果一些文件变化导致另外一些文件必须更新的任务。

如果要使用Make,必须先写一个称为Makefile的文件,该文件描述程序中各个文件之间的相互关系,并且提供每一个文件的更新命令。在一个程序中,可执行程序文件的更新依靠OBJ文件,而OBJ文件是由源文件编译得来的。

一旦合适的Makefile文件存在,每次更改一些源文件,在shell命令下简单的键入就能执行所有的必要的重新编译任务。Make程序根据Makefile文件中的数据和每个文件更改的时间戳决定哪些文件需要更新。对于这些需要更新的文件,Make基于Makefile文件发布命令进行更新,进行更新的方式由提供的命令行参数控制。具体操作请看运行Make章节。

1.1怎样阅读本手册

如果您现在对Make一无所知或者您仅需要了解对make 的普通性介绍,请查阅前几章内容,略过后面的章节。前几章节是普通介绍性内容,后面的章节是具体的专业、技术内容。

如果您对其它Make程序十分熟悉,请参阅GNU Make的特点和不兼容性和失去的特点部分,GNU Make的特点这一章列出了GNU Make对make程序的扩展,不兼容和失去的特点一章解释了其它Make程序有的特征而GNU Make缺乏的原因。

对于快速浏览者,请参阅选项概要、快速参考和内建的特殊目标名部分。

1.2问题和BUG

如果您有关于GNU Make的问题或者您认为您发现了一个BUG,请向开发者报告;我们不能许诺我们能干什么,但我们会尽力修正它。在报告BUG之前,请确定您是否真正发现了BUG,仔细研究文档后确认它是否真的按您的指令运行。如果文档不能清楚的告诉您怎么做,也要报告它,这是文档的一个BUG。

在您报告或者自己亲自修正BUG之前,请把它分离出来,即在使问题暴露的前提下尽可能的缩小Makefile文件。然后把这个Makefile文件和Make给出的精确结果发给我们。同时请说明您希望得到什么,这可以帮助我们确定问题是否出在文档上。

一旦您找到一个精确的问题,请给我们发E-mail,我们的E-mail地址是:

bug-make@gnu.org

在邮件中请包含您使用的GNU Make的版本号。您可以利用命令‘make–version’得到版本号。同时希望您提供您的机器型号和操作系统类型,如有可能的话,希望同时提供config.h文件(该文件有配置过程产生)。

2 Makefile文件介绍

Make程序需要一个所谓的Makefile文件来告诉它干什么。在大多数情况下,Makefile文件告诉Make怎样编译和连接成一个程序。

本章我们将讨论一个简单的Makefile文件,该文件描述怎样将8个C源程序文件和3个头文件编译和连接成为一个文本编辑器。Makefile文件可以同时告诉Make怎样运行所需要的杂乱无章的命令(例如,清除操作时删除特定的文件)。如果要看更详细、复杂的Makefile文件例子,请参阅复杂的Makefile文件例子一章。

当Make重新编译这个编辑器时,所有改动的C语言源文件必须重新编译。如果一个头文件改变,每一个包含该头文件的C语言源文件必须重新编译,这样才能保证生成的编辑器是所有源文件更新后的编辑器。每一个C语言源文件编译后产生一个对应的OBJ文件,如果一个源文件重新编译,所有的OBJ文件无论是刚刚编译得到的或原来编译得到的必须从新连接,形成一个新的可执行文件。

2.1 规则的格式

一个简单的Makefile文件包含一系列的“规则”,其样式如下:

目标(target)通常是要产生的文件的名称,目标的例子是可执行文件或OBJ文件。目标也可是一个执行的动作名称,诸如‘clean’(详细内容请参阅假想目标一节)。

依赖是用来输入从而产生目标的文件,一个目标经常有几个依赖。

命令是Make执行的动作,一个规则可以含有几个命令,每个命令占一行。注意:每个命令行前面必须是一个Tab字符,即命令行第一个字符是Tab。这是不小心容易出错的地方。

通常,如果一个依赖发生变化,则需要规则调用命令对相应依赖和服务进行处理从而更新或创建目标。但是,指定命令更新目标的规则并不都需要依赖,例如,包含和目标‘clern’相联系的删除命令的规则就没有依赖。

规则一般是用于解释怎样和何时重建特定文件的,这些特定文件是这个详尽规则的目标。Make需首先调用命令对依赖进行处理,进而才能创建或更新目标。当然,一个规则也可以是用于解释怎样和何时执行一个动作,详见编写规则一章。

一个Makefile文件可以包含规则以外的其它文本,但一个简单的Makefile文件仅仅需要包含规则。虽然真正的规则比这里展示的例子复杂,但格式却是完全一样。

2.2一个简单的Makefile文件

一个简单的Makefile文件,该文件描述了一个称为文本编辑器(edit)的可执行文件生成方法,该文件依靠8个OBJ文件(.o文件),它们又依靠8个C源程序文件和3个头文件。

在这个例子中,所有的C语言源文件都包含 头文件,但仅仅定义编辑命令的源文件包含头文件,仅仅改变编辑器缓冲区的低层文件包含头文件。

我们把每一个长行使用反斜杠-新行法分裂为两行或多行,实际上它们相当于一行,这样做的意图仅仅是为了阅读方便。

使用Makefile文件创建可执行的称为‘edit’的文件,键入:

使用Makefile文件从目录中删除可执行文件和目标,键入:

在这个Makefile文件例子中,目标包括可执行文件‘edit’和OBJ文件‘main.o’及‘kdb.o’。依赖是C语言源文件和C语言头文件如‘main.c’和‘def.h’等。事实上,每一个OBJ文件即是目标也是依赖。所以命令行包括和。

当目标是一个文件时,如果它的任一个依赖发生变化,目标必须重新编译和连接。任何命令行的第一个字符必须是‘Tab’字符,这样可以把Makefile文件中的命令行与其它行分别开来。(一定要牢记:Make并不知道命令是如何工作的,它仅仅能向您提供保证目标的合适更新的命令。Make的全部工作是当目标需要更新时,按照您制定的具体规则执行命令。)

目标‘clean’不是一个文件,仅仅是一个动作的名称。正常情况下,在规则中‘clean’这个动作并不执行,目标‘clean’也不需要任何依赖。一般情况下,除非特意告诉make执行‘clean’命令,否则‘clean’命令永远不会执行。注意这样的规则不需要任何依赖,它们存在的目的仅仅是执行一些特殊的命令。象这些不需要依赖仅仅表达动作的目标称为假想目标。详细内容参见假想目标;参阅命令错误可以了解rm或其它命令是怎样导致make忽略错误的。

2.3 make处理makefile文件的过程

缺省情况下,make开始于第一个目标(假想目标的名称前带‘.’)。这个目标称为缺省最终目标(即make最终更新的目标,具体内容请看指定最终目标的参数一节)。

在上节的简单例子中,缺省最终目标是更新可执行文件‘edit’,所以我们将该规则设为第一规则。这样,一旦您给出命令:,make就会读当前目录下的makefile文件,并开始处理第一条规则。在本例中,第一条规则是连接生成‘edit’,但在make全部完成本规则工作之前,必须先处理‘edit’所依靠的OBJ文件。这些OBJ文件按照各自的规则被处理更新,每个OBJ文件的更新规则是编译其源文件。重新编译根据其依靠的源文件或头文件是否比现存的OBJ文件更‘新’,或者OBJ文件是否存在来判断。

其它规则的处理根据它们的目标是否和缺省最终目标的依赖相关联来判断。如果一些规则和缺省最终目标无任何关联则这些规则不会被执行,除非告诉Make强制执行(如输入执行make clean命令)。

在OBJ文件重新编译之前,Make首先检查它的依赖C语言源文件和C语言头文件是否需要更新。如果这些C语言源文件和C语言头文件不是任何规则的目标,make将不会对它们做任何事情。Make也可以自动产生C语言源程序,这需要特定的规则,如可以根据Bison或Yacc产生C语言源程序。

在OBJ文件重新编译(如果需要的话)之后,make决定是否重新连接生成edit可执行文件。如果edit可执行文件不存在或任何一个OBJ文件比存在的edit可执行文件‘新’,则make重新连接生成edit可执行文件。

这样,如果我们修改了‘insert.c’文件,然后运行make,make将会编译‘insert.c’文件更新‘insert.o’文件,然后重新连接生成edit可执行文件。如果我们修改了‘command.h’文件,然后运行make,make将会重新编译‘kbd.o’和‘command.o’文件,然后重新连接生成edit可执行文件。

2.4使用变量简化makefile文件

在我们的例子中,我们在‘edit’的生成规则中把所有的OBJ文件列举了两次,这里再重复一遍:

这样的两次列举有出错的可能,例如在系统中加入一个新的OBJ文件,我们很有可能在一个需要列举的地方加入了,而在另外一个地方却忘记了。我们使用变量可以简化makefile文件并且排除这种出错的可能。变量是定义一个字符串一次,而能在多处替代该字符串使用(具体内容请阅读使用变量一节)。

在makefile文件中使用名为objects, OBJECTS, objs, OBJS, obj, 或 OBJ的变量代表所有OBJ文件已是约定成俗。在这个makefile文件我们定义了名为objects的变量,其定义格式如下:

然后,在每一个需要列举OBJ文件的地方,我们使用写为`$(objects)’形式的变量代替(具体内容请阅读使用变量一节)。下面是使用变量后的完整的makefile文件:

2.5 让make推断命令

编译单独的C语言源程序并不需要写出命令,因为make可以把它推断出来:make有一个使用‘CC –c’命令的把C语言源程序编译更新为相同文件名的OBJ文件的隐含规则。例如make可以自动使用‘cc -c main.c -o main.o’命令把‘main.c’编译 ‘main.o’。因此,我们可以省略OBJ文件的更新规则。详细内容请看使用隐含规则一节。

如果C语言源程序能够这样自动编译,则它同样能够自动加入到依赖中。所以我们可在依赖中省略C语言源程序,进而可以省略命令。下面是使用隐含规则和变量objects的完整makefile文件的例子:

这是我们实际编写makefile文件的例子。(和目标‘clean’联系的复杂情况在别处阐述。具体参见假想目标及命令错误两节内容。)因为隐含规则十分方便,所以它们非常重要,在makefile文件中经常使用它们。

2.6 另一种风格的makefile文件

当时在makefile文件中使用隐含规则创建OBJ文件时,采用另一种风格的makefile文件也是可行的。在这种风格的makefile文件中,可以依据依赖分组代替依据目标分组。下面是采用这种风格的makefile文件:

这里的defs.h是所有OBJ文件的共同的一个依赖;command.h和bufffer.h是具体列出的OBJ文件的共同依赖。

虽然采用这种风格编写makefile文件更具风味:makefile文件更加短小,但一部分人以为把每一个目标的信息放到一起更清晰易懂而不喜欢这种风格。

2.7 在目录中删除文件的规则

编译程序并不是编写make规则的唯一事情。Makefile文件可以告诉make去完成编译程序以外的其它任务,例如,怎样删除OBJ文件和可执行文件以保持目录的‘干净’等。下面是删除利用make规则编辑器的例子:

在实际应用中,应该编写较为复杂的规则以防不能预料的情况发生。更接近实用的规则样式如下:

这样可以防止make因为存在名为’clean’的文件而发生混乱,并且导致它在执行rm命令时发生错误(具体参见假想目标及命令错误两节内容)。

诸如这样的规则不能放在makefile文件的开始,因为我们不希望它变为缺省最终目标。应该象我们的makefile文件例子一样,把关于edit的规则放在前面,从而把编译更新edit可执行程序定为缺省最终目标。

3 编写makefile文件

make编译系统依据的信息来源于称为makefile文件的数据库。

3.1 makefile文件的内容

makefile文件包含5方面内容:具体规则、隐含规则、定义变量、指令和注释。规则、变量和指令将在后续章节介绍。

  1. 具体规则用于阐述什么时间或怎样重新生成称为规则目标的一个或多个文件的。它列举了目标所依靠的文件,这些文件称为该目标的依赖。具体规则可能同时提供了创建或更新该目标的命令。详细内容参阅编写规则一章。
  2. 隐含规则用于阐述什么时间或怎样重新生成同一文件名的一系列文件的。它描述的目标是根据和它名字相同的文件进行创建或更新的,同时提供了创建或更新该目标的命令。详细内容参阅使用隐含规则一节。
  3. 定义变量是为一个变量赋一个固定的字符串值,从而在以后的文件中能够使用该变量代替这个字符串。注意在makefile文件中定义变量占一独立行。在上一章的makefile文件例子中我们定义了代表所有OBJ文件的变量objects(详细内容参阅使用变量简化makefile文件一节)。
  4. 指令是make根据makefile文件执行一定任务的命令。这些包括如下几方面: 其它makefile文件(详细内容参见包含其它的makefile文件)。判定(根据变量的值)是否使用或忽略makefile文件的部分内容(详细内容参阅makefile文件的条件语句一节)。定义多行变量,即定义变量值可以包含多行字符的变量(详细内容参见定义多行变量一节)。
  5. 以‘#’开始的行是注释行。注释行在处理时将被make忽略,如果一个注释行在行尾是‘’则表示下一行继续为注释行,这样注释可以持续多行。除在define指令内部外,注释可以出现在makefile文件的任何地方,甚至在命令内部(这里shell决定什么是注释内容)。

3.2 makfile文件的命名

缺省情况下,当make寻找makefile文件时,它试图搜寻具有如下的名字的文件,按顺序:‘GNUmakefile’、‘makefile’和‘Makefile’。

通常情况下您应该把您的makefile文件命名为‘makefile’或‘Makefile’。(我们推荐使用‘Makefile’,因为它基本出现在目录列表的前面,后面挨着其它重要的文件如‘README’等。)。虽然首先搜寻‘GNUmakefile’,但我们并不推荐使用。除非您的makefile文件是特为GNU make编写的,在其它make版本上不能执行,您才应该使用‘GNUmakefile’作为您的makefile的文件名。

如果make不能发现具有上面所述名字的文件,它将不使用任何makefile文件。这样您必须使用命令参数给定目标,make试图利用内建的隐含规则确定如何重建目标。详细内容参见使用隐含规则一节。

如果您使用非标准名字makefile文件,您可以使用‘-f’或‘–file’参数指定您的makefile文件。参数‘-f name’或‘–file=name’能够告诉make读名字为‘name’的文件作为makefile文件。如果您使用 ‘-f’或‘–file’参数多于一个,意味着您指定了多个makefile文件,所有的makefile文件按具体的顺序发生作用。一旦您使用了‘-f’或‘–file’参数,将不再自动检查是否存在名为‘GNUmakefile’、‘makefile’或‘Makefile’的makefile文件。

3.3 包含其它的makefile文件

include指令告诉make暂停读取当前的makefile文件,先读完include指令指定的makefile文件后再继续。指令在makefile文件占单独一行,其格式如下:

filenames可以包含shell文件名的格式。

在include指令行,行开始处的多余的空格是允许的,但make处理时忽略这些空格,注意该行不能以Tab字符开始(因为,以Tab字符开始的行,make认为是命令行)。include和文件名之间以空格隔开,两个文件名之间也以空格隔开,多余的空格make处理时忽略,在该行的尾部可以加上以‘#’为起始的注释。文件名可以包含变量及函数调用,它们在处理时由make进行扩展(具体内容参阅使用变量一节)。

例如,有三个‘.mk’文件:‘a.mk’、‘b.mk’和‘c.mk’,变量$(bar)扩展为bish bash,则下面的表达是:

和等价。

当make遇见include指令时, make就暂停读取当前的makefile文件,依次读取列举的makefile文件,读完之后,make再继续读取当前makefile文件中include指令以后的内容。

使用include指令的一种情况是几个程序分别有单独的makefile文件,但它们需要一系列共同的变量定义(详细内容参阅设置变量),或者一系列共同的格式规则(详细内容参阅定义与重新定义格式规则)。

另一种使用include指令情况是需要自动从源文件为目标产生依赖的情况,此时,依赖在主makefile文件包含的文件中。这种方式比其它版本的make把依赖附加在主makefile文件后部的传统方式更显得简洁。具体内容参阅自动产生依赖。

如果makefile文件名不以‘/’开头,并且在当前目录下也不能找到,则需搜寻另外的目录。首先,搜寻以‘-|’或‘–include-dir’参数指定的目录,然后依次搜寻下面的目录(如果它们存在的话):‘prefix/include’ (通常为 ‘/usr/local/include’) ‘/usr/gnu/include’, ‘/usr/local/include’, ‘/usr/include’。

如果指定包含的makefile文件在上述所有的目录都不能找到,make将产生一个警告信息,注意这不是致命的错误。处理完include指令包含的makefile文件之后,继续处理当前的makefile文件。一旦完成makefile文件的读取操作,make将试图创建或更新过时的或不存在的makefile文件。详细内容参阅makefile文件重新生成的过程。只有在所有make寻求丢失的makefile文件的努力失败后,make才能断定丢失的makefile文件是一个致命的错误。

如果您希望对不存在且不能重新创建的makefile文件进行忽略,并且不产生错误信息,则使用-include指令代替include指令,格式如下:

这种指令的作用就是对于任何不存在的makefile文件都不会产生错误(即使警告信息也不会产生)。如果希望保持和其它版本的make兼容,使用sinclude指令代替-include指令。

3.4 变量MAKEFILES

如果定义了环境变量MAKEFILES,make认为该变量的值是一列附加的makefile文件名,文件名之间由空格隔开,并且这些makefile文件应首先读取。Make完成这个工作和上节完成include指令的方式基本相同,即在特定的目录中搜寻这些文件。值得注意的是,缺省最终目标不会出现在这些makefile文件中,而且如果一些makefile文件没有找到也不会出现任何错误信息。

环境变量MAKEFILES主要在make递归调用过程中起通讯作用(详细内容参阅递归调用make)。在make顶级调用之前设置环境变量并不是十分好的主意,因为这样容易将makefile文件与外界的关系弄的更加混乱。然而如果运行make而缺少makefile文件时,环境变量MAKEFILES中makefile文件可以使内置的隐含规则更好的发挥作用,如搜寻定义的路径等(详细内容参阅在目录中搜寻依赖)。

一些用户喜欢在登录时自动设置临时的环境变量MAKEFILES,而makefile文件在该变量指定的文件无效时才使用。这是非常糟糕的主意,应为许多makefile文件在这种情况下运行失效。最好的方法是直接在makefile文件中写出具体的include指令(详细内容参看上一节)。

3.5 makefile文件重新生成的过程

有时makefile文件可以由其它文件重新生成,如从RCS或SCCS文件生成等。如果一个makefile文件可以从其它文件重新生成,一定注意让make更新makefile文件之后再读取makefile文件。

完成读取所有的makefile文件之后,make检查每一个目标,并试图更新它。如果对于一个makefile文件有说明它怎样更新的规则(无论在当前的makefile文件中或其它makefile文件中),或者存在一条隐含规则说明它怎样更新(具体内容参见使用隐含规则),则在必要的时候该makefile文件将会自动更新。在所有的makefile文件检查之后,如果发现任何一个makefile文件发生变化,make就会清空所有记录,并重新读入所有makefile文件。(然后再次试图更新这些makefile文件,正常情况下,因为这些makefile文件已被更新,make将不会再更改它们。)

如果您知道您的一个或多个makefile文件不能重新创建,也许由于执行效率缘故,您不希望make按照隐含规则搜寻或重建它们,您应使用正常的方法阻止按照隐含规则检查它们。例如,您可以写一个具体的规则,把这些makefile文件当作目标,但不提供任何命令(详细内容参阅使用空命令)。

如果在makefile文件中指定依据双冒号规则使用命令重建一个文件,但没有提供依赖,则一旦make运行就会重建该文件(详细内容参见双冒号规则)。同样,如果在makefile文件中指定依据双冒号规则使用命令重建的一个makefile文件,并且不提供依赖,则一旦make运行就会重建该makefile文件,然后重新读入所有makefile文件,然后再重建该makefile文件,再重新读入所有makefile文件,如此往复陷入无限循环之中,致使make不能再完成别的任务。如果要避免上述情况的发生,一定注意不要依据双冒号规则使用命令并且不提供依赖重建任何makefile文件。

如果您没有使用‘-f’或‘–file’指定makefile文件,make将会使用缺省的makefile文件名(详细内容参见3.2节内容)。不象使用‘-f’或‘–file’选项指定具体的makefile文件,这时make不能确定makefile文件是否存在。如果缺省的makefile文件不存在,但可以由运行的make依据规则创建,您需要运行这些规则,创建要使用的makefile文件。

如果缺省的makefile文件不存在,make将会按照搜寻的次序将它们试着创建,一直到将makefile文件成功创建或make将所有的文件名都试过来。注意make不能找到或创建makefile文件不是错误,makefile文件并不是运行make必须的。

因为即使您使用‘-t’特别指定,‘-t’或‘–touch’选项对更新makefile文件不产生任何影响, makefile文件仍然会更新,所以当您使用‘-t’或‘–touch’选项时,您不要使用过时的makefile文件来决定‘touch’哪个目标(具体含义参阅代替执行命令)。同样,因为‘-q’ (或 ‘–question’) 和 ‘-n’ (或 ‘–just-print’)也能不阻止更新makefile文件,所以过时的makefile文件对其它的目标将产生错误的输出结果。如,‘make -f mfile -n foo’命令将这样执行:更新‘mfile’,然后读入,再输出更新‘foo’的命令和依赖,但并不执行更新‘foo’,注意,所有回显的更新‘foo’的命令是在更新后的‘mfile’中指定的。

在实际使用过程中,您一定会遇见确实希望阻止更新makefile文件的情况。如果这样,您可以在makefile文件命令行中将需要更新的makefile文件指定为目标,如此则可阻止更新makefile文件。一旦makefile文件名被明确指定为一个目标,选项‘-t’等将会对它发生作用。如这样设定,‘make -f mfile -n foo’命令将这样执行:读入‘mfile’,输出更新‘foo’的命令和依赖,但并不执行更新‘foo’。回显的更新‘foo’的命令包含在现存的‘mfile’中。

3.6 重载其它makefile文件

有时一个makefile文件和另一个makefile文件相近也是很有用的。您可以使用‘include’指令把更多的makefile文件包含进来,如此可加入更多的目标和定义的变量。然而如果两个makefile文件对相同的目标给出了不同的命令,make就会产生错误。

在主makefile文件(要包含其它makefile文件的那个)中,您可以使用通配符格式规则说明只有在依靠当前makefile文件中的信息不能重新创建目标时,make才搜寻其它的makefile文件,详细内容参见定义与重新定义格式规则。

例如:如果您有一个说明怎样创建目标‘foo’(和其它目标)的makefile文件称为‘Makefile’,您可以编写另外一个称为‘GNUmakefile’的makefile文件包含以下语句:

如果键入‘make foo’,make就会找到‘GNUmakefile’,读入,然后运行‘frobnicate > foo’。如果键入‘make bar’,make发现无法根据‘GNUmakefile’创建‘bar’,它将使用格式规则提供的命令:‘make –f Makefile bar’。如果在‘Makefile’中提供了‘bar’更新的规则,make就会使用该规则。对其它‘GNUmakefile’不提供怎样更新的目标make也会同样处理。这种工作的方式是使用了格式规则中的格式匹配符‘%’,它可以和任何目标匹配。该规则指定了一个依赖‘force’,用来保证命令一定要执行,无论目标文件是否存在。我们给出的目标‘force’时使用了空命令,这样可防止make按照隐含规则搜寻和创建它,否则,make将把同样的匹配规则应用到目标‘force’本身,从而陷入创建依赖的循环中。

3.7 make读取makefile文件的过程

GNU make把它的工作明显的分为两个阶段。在第一阶段,make读取makefile文件,包括makefile文件本身、内置变量及其值、隐含规则和具体规则、构造所有目标的依靠图表和它们的依赖等。在第二阶段,make使用这些内置的组织决定需要重新构造的目标以及使用必要的规则进行工作。

了解make两阶段的工作方式十分重要,因为它直接影响变量、函数扩展方式;而这也是编写makefile文件时导致一些错误的主要来源之一。下面我们将对makefile文件中不同结构的扩展方式进行总结。我们称在make工作第一阶段发生的扩展是立即扩展:在这种情况下,make对makefile文件进行语法分析时把变量和函数直接扩展为结构单元的一部分。我们把不能立即执行的扩展称为延时扩展。延时扩展结构直到它已出现在上下文结构中或make已进入到了第二工作阶段时才执行展开。

您可能对这一部分内容不熟悉。您可以先看完后面几章对这些知识熟悉后再参考本节内容。

变量赋值

变量的定义语法形式如下:

对于附加操作符‘+=’,右边变量如果在前面使用(:=)定义为简单扩展变量则是立即变量,其它均为延时变量。

条件语句

整体上讲,条件语句都按语法立即分析,常用的有:ifdef、ifeq、ifndef和inneq。

定义规则

规则不论其形式如何,都按相同的方式扩展。

immediate : immediate ; deferred
deferred
目标和依赖部分都立即扩展,用于构造目标的命令通常都是延时扩展。这个通用的规律对具体规则、格式规则、后缀规则、静态格式规则和简单依赖定义都适用。

4编写规则

makefile文件中的规则是用来说明何时以及怎样重建特定文件的,这些特定的文件称为该规则的目标(通常情况下,每个规则只有一个目标)。在规则中列举的其它文件称为目标的依赖,同时规则还给出了目标创建、更新的命令。一般情况下规则的次序无关紧要,但决定缺省最终目标时却是例外。缺省最终目标是您没有另外指定最终目标时,make认定的最终目标。缺省最终目标是makefile文件中的第一条规则的目标。如果第一条规则有多个目标,只有第一个目标被认为是缺省最终目标。有两种例外的情况:以句点(‘.’)开始的目标不是缺省最终目标(如果该目标包含一个或多个斜杠‘/’,则该目标也可能是缺省最终目标);另一种情况是格式规则定义的目标不是缺省最终目标(参阅定义与重新定义格式规则)。

所以,我们编写makefile文件时,通常将第一个规则的目标定为编译全部程序或是由makefile文件表述的所有程序(经常设定一个称为‘all’的目标)。参阅指定最终目标的参数。

4.1规则的语法

通常一条规则形式如下:

targets : prerequisites
command

或:

targets : prerequisites ; command
command

目标(target)是文件的名称,中间由空格隔开。通配符可以在文件名中使用(参阅在文件名中使用通配符),‘a(m)’形式的文件名表示成员m在文件a中(参阅档案成员目标)。一般情况下,一条规则只有一个目标,但偶尔由于其它原因一条规则有多个目标(参阅具有多个目标的规则)。

命令行以Tab字符开始,第一个命令可以和依赖在一行,命令和依赖之间用分号隔开,也可以在依赖下一行,以Tab字符为行的开始。这两种方法的效果一样,参阅在规则中使用命令。

因为美元符号已经用为变量引用的开始符,如果您真希望在规则中使用美元符号,您必须连写两次,‘$$’(参阅使用变量)。您可以把一长行在中间插入‘’使其分为两行,也就是说,一行的尾部是’’的话,表示下一行是本行的继续行。但这并不是必须的,make没有对makefile文件中行的长度进行限制。一条规则可以告诉make两件事情:何时目标已经过时,以及怎样在必要时更新它们。

判断目标过时的准则和依赖关系密切,依赖也由文件名构成,文件名之间由空格隔开,通配符和档案成员也允许在依赖中出现。一个目标如果不存在或它比其中一个依赖的修改时间早,则该目标已经过时。该思想来源于目标是根据依赖的信息计算得来的,因此一旦任何一个依赖发生变化,目标文件也就不再有效。目标的更新方式由命令决定。命令由shell解释执行,但也有一些另外的特点。参阅在规则中使用命令。

4.2 在文件名中使用通配符

一个简单的文件名可以通过使用通配符代表许多文件。Make中的通配符和Bourne shell中的通配符一样是‘’、‘和‘[…]’。例如:‘.C’指在当前目录中所有以‘.C’结尾的文件。

字符‘’在文件名的前面也有特殊的含义。如果字符‘’单独或后面跟一个斜杠‘/’,则代表您的home目录。如‘~/bin’扩展为‘/home/bin’。 如果字符‘’后面跟一个字,它扩展为home目录下以该字为名字的目录,如‘John/bin’表示‘home/John/bin’。在一些操作系统(如ms-dos,ms-windows)中不存在home目录,可以通过设置环境变量home来模拟。

在目标、依赖和命令中的通配符自动扩展。在其它上下文中,通配符只有在您明确表明调用通配符函数时才扩展。

通配符另一个特点是如果通配符前面是反斜杠‘’,则该通配符失去通配能力。如‘foo*bar’表示一个特定的文件其名字由‘foo’、‘*’和‘bar’构成。

4.2.1通配符例子

clean:

print: *.c
lpr -p t o u c h p r i n t 本 规 则 使 用 ‘ p t i n t ’ 作 为 一 个 空 目 标 文 件 ( 参 看 使 用 空 目 标 文 件 记 录 事 件 ) ; 自 动 变 量 ‘ touch print 本规则使用‘ptint’作为一个空目标文件(参看使用空目标文件记录事件);自动变量‘

来源:Leon_George

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年1月5日
下一篇 2022年1月5日

相关推荐