手册目录

make简介

编辑

首先要有一个名为“Makefile”的文件告诉make需要作什么。绝大多数情况下,Makefile主要让make完成编译与链接程序的工作。

本章我们讨论一个简单的Makefile,这个Makefile会编译与链接一个由八个C源代码文件和三个头文件组成的文本编辑器程序。该 Makefile还会告诉make如何在有明确指示的时候运行各种各样的命令(比如说接到清除指示时,make运行删除某些文件的命令)。要阅读更复杂的 Makefile代码,翻至附录C 一个复杂的Makefile实例

当make重新编译该文本编辑器时,所有的C源代码文件都会被重新编译。如果一个头文件被修改,那么为了保证整体程序无损,包含该头文件的所有C源代码文件都必须要重新编译。源代码文件每经过一次编译都会有一个与自己对应的OBJ文件生成。最后,若有源代码文件被重新编译,所有OBJ文件不管是以前生成的还是新生成的,都会被用来链接生成新的可执行文本编辑器程序。

规则大概是什么样的

编辑

一个简单的Makefile所包含的“规则”形式如下

目标 ...:先决条件
     命令
     ...
     ...

目标的名称一般与生成的文件名称一致(像可执行程序和OBJ文件)。目标的名称 也可以是要进行的动作,比如说“clean”(参见第四章第五节 伪目标)。

先决条件是用以生成目标的输入文件,而一个目标往往依赖于多个文件。

命令是make要执行的动作,而一个规则往往要多行命令。请注意:在每个命令前都必须输入一个Tab!一般粗心大意的人容易在这个问题上犯错。

通常有先决条件规则的命令会在先决条件被改动之后执行。但也有一些执行特殊命令的规则没有先决条件,比如说目标“clean”所在的规则就没有先决条件,该规则含有删除命令。

接下来规则会说明与特定规则关联的文件应该何时被操作,如何被操作。然后make执行先决条件中的命令,用以生成或更新目标。此外,规则还可以对何时与如何进行一个动作下令。参见第四章 编写规则

除了规则之外,Makefile文件也可以有其他内容,但一个简单的Makefile只需有规则即可。规则写出来会比范例中所写的略显复杂,但或多或少含有与其一致的部分。

一份简单的Makefile文件

编辑

底下有一份简单易懂的Makefile文件,该文件描述了各种依赖关系:可执行程序edit依赖于八个OBJ档,八个OBJ档依赖于八个C原始程式码和三个标头档。

在本例中,所有C原始程式码都包含了“defs.h”标头档,只有定义了编辑命令的C原始程式码才包含“command.h”标头档,而只有只有能改变编辑器缓冲区的低层C原始程式码才包含“buffer.h”标头档。

     edit : main.o kbd.o command.o display.o \
            insert.o search.o files.o utils.o
             cc -o edit main.o kbd.o command.o display.o \
                        insert.o search.o files.o utils.o
     
     main.o : main.c defs.h
             cc -c main.c
     kbd.o : kbd.c defs.h command.h
             cc -c kbd.c
     command.o : command.c defs.h command.h
             cc -c command.c
     display.o : display.c defs.h buffer.h
             cc -c display.c
     insert.o : insert.c defs.h buffer.h
             cc -c insert.c
     search.o : search.c defs.h buffer.h
             cc -c search.c
     files.o : files.c defs.h buffer.h command.h
             cc -c files.c
     utils.o : utils.c defs.h
             cc -c utils.c
     clean :
             rm edit main.o kbd.o command.o display.o \
                insert.o search.o files.o utils.o


本例用反斜线“\”将每个长行都分成两行,如此功能上与长行相同,而且减少了阅读长行的难度。

要用此Makefile生成可执行程序edit,请键入:

make

要用此Makefile删除目录中所有OBJ文件和可执行程序“edit”,请键入:

make clean

在本例中,目标包括,可执行程序“edit”以及OBJ文件“main.o”与“kdb.o”;先决条件也有许多,如“main.c”与“defs.h”;而每个“.o”文件都既为目标,又是先决条件;命令包括“cc -c main.c”与“cc -c kbd.c”。

若目标为文件,则该文件会在其先决条件被改动后重编译与链接。另外,会自动生成的先决条件首先更新。本例中,“edit”依赖于八个OBJ文件;OBJ文件“main.o”依赖于源代码文件“main.c”与头文件“defs.h”。

Shell命令跟随在目标与先决条件的下一行,该命令控制更新目标文件。每个命令行首必须要有Tab键入,以此区分命令行与Makefile中的其他行。(想想看,make全然不知命令是如何工作的,必须由程序员提供命令来更新目标。make所能干的只有执行程序员给出命令,然后再根据命令判断目标文件是否需要更新;如需要,更新之)。

目标“clean”不是一个文件,仅仅是动作的名称而已。因为这个规则不包含其他动作,而且也不是其他任何规则的先决条件,所以,make不会执行此处操作,除非被特别指定。请注意,此规则既非其他规则之先决条件,亦无先决条件,故此规则唯一目的为运行特定命令。目标若只含命令而与其他文件不相关联,则称此目标为伪目标。可参阅第四章第五节 伪目标,了解详情。另外,请参阅第五章第五节 命令中的报错信息,了解如何忽略rm或者其他命令的报错信息。

make如何执行一个Makefile的指令

编辑

在默认情况下,make的工作始于第一个目标(只要这个目标不是以“。”开头的)。此目标称为默认最终目标(如果不想使用这个默认规定,可以在命令行中输入“make 目标名”(参见第九章第二节 用以确定最终目标的参数)或者更改变量(参见第三章第六节 其它的特殊变量)“.DEFAULT_GOAL”以改变默认最终目标)。

上一节简单示例中,默认最终目标是更新可执行程序“edit”,故将其列入规则首位。

因此,输入以下命令:

make

make首先读取本目录的Makefile,而后开始运行第一个规则。在此例中,规则用以重链接“edit”;但在执行这条规则之前,make必须完成所有“edit”依赖的规则,这些规则就是那些OBJ文件;而每个OBJ文件都会运行自己的一套规则,这些规则会通过编译各自源代码的方式来更新每个“.o”文件。如果头文件或者源代码文件的修改时间比依赖其的OBJ文件要晚,或者OBJ文件不存在,那么重新编译就必须要进行。

那些非最终目标之所以会被运行,是因为最终目标与之相关。而与最终目标无关的规则就不会被执行,除非被特指(如“make clean”)。

在重编译一个OBJ文件之前,make会先更新其先决条件,先决条件包括源代码文件与头文件。此例中的Makefile并没有针对源代码文件与头文件的操作,另外这些“.c”与“.h”文件也不是规则的名称,故make在此不做任何事情。但make会自动更新自动生成的C程序,比如说由Bison或Yacc生成的,由此次规则生成的。

在编译完所有需要的OBJ文件后,make会决定是否重链接“edit”。如果“edit”文件不存在或者OBJ文件比“edit”文件要新,则重链接工作一定要作。如果OBJ文件是刚编译出的,其时间晚于“edit”文件,故此时“edit”文件要被重链接。

因此,如果更改了“insert.c”文件再运行make;make会先编译“insert.c”文件生成“insert.o”文件,再链接生成“edit”文件。若更改的是“command.h”,再运行make;make会编译生成“kdb.o”、“command.o”、“files.o”再链接生成“edit”。

用变量简化Makefile

编辑

在上例中,生成“edit”的OBJ文件组被列举了两次(这里在重复一次):

     edit : main.o kbd.o command.o display.o \
                   insert.o search.o files.o utils.o
             cc -o edit main.o kbd.o command.o display.o \
                        insert.o search.o files.o utils.o


如此重复书写,容易造成错误;另外,若有新OBJ文件加入系统,本应可以只添加新文件而不考虑原有的OBJ文件。若采用变量协助工作,则既可减少出错几率,亦能简化Makefile。变量允许先对一段文本字符串定义,以后需要书写诸OBJ文件时,可以只输入变量而不需要把所有OBJ文件列出(参见第六章 如何使用变量)。

此处,一个习惯性做法是,在每个Makefile中加入名为“objects”、“OBJECTS”、“objs”、“OBJS”、“obj”或者“OBJ”的变量,用以列出全部OBJ文件的名称。在Makefile中,可以用如下方式定义变量“objects”:

 objects = main.o kbd.o command.o display.o \
               insert.o search.o files.o utils.o


定义之后,文件时需要列出诸OBJ文件处可用“$(objects)”代替(参见第六章 如何使用变量)。

下面就是用变量来描述诸OBJ文件的Makefile,简单而完整:

objects = main.o kbd.o command.o display.o \
               insert.o search.o files.o utils.o
     
     edit : $(objects)
             cc -o edit $(objects)
     main.o : main.c defs.h
             cc -c main.c
     kbd.o : kbd.c defs.h command.h
             cc -c kbd.c
     command.o : command.c defs.h command.h
             cc -c command.c
     display.o : display.c defs.h buffer.h
             cc -c display.c
     insert.o : insert.c defs.h buffer.h
             cc -c insert.c
     search.o : search.c defs.h buffer.h
             cc -c search.c
     files.o : files.c defs.h buffer.h command.h
             cc -c files.c
     utils.o : utils.c defs.h
             cc -c utils.c
     clean :
             rm edit $(objects)

让make自己推算出要执行的命令

编辑

不必将每个编译C源代码文件的命令全部写出,因为make可以构造出这些命令:make有一个隐规则可通过命令“cc -c”生成与“.c”文件前缀名一致的“.o”文件的命令,比如将“main.c”编译成“main.o”的命令“cc -c main.c -o main.o”。此处可以把命令中写“.o”的部分省略。参阅第十章 使用隐规则

当“.c”文件以下例方法自动被使用时,“.c”文件会自动加入到先决条件中。在先决条件中省略“.c”文件的同时,编译命令也被省略了。

下面是个完整的例子。例子中包括了上面两种省略方法,而且也使用了上一节所说的“objects”变量。

  objects = main.o kbd.o command.o display.o \
               insert.o search.o files.o utils.o
     
     edit : $(objects)
             cc -o edit $(objects)
     
     main.o : defs.h
     kbd.o : defs.h command.h
     command.o : defs.h command.h
     display.o : defs.h buffer.h
     insert.o : defs.h buffer.h
     search.o : defs.h buffer.h
     files.o : defs.h buffer.h command.h
     utils.o : defs.h
     
     .PHONY : clean
     clean :
             rm edit $(objects)


本例与实际应用中Makefile相差无几(复杂化的“clean”规则在其他地方有所讨论。参见第四章第五节 伪目标第五章第五节 命令中的报错信息)。

隐规则使用简便,故尤为重要。读者会频繁见到隐规则的使用。

另一种格式的Makefile

编辑

当组成Makefile的全为规则时,可以用另一种形式的Makefile代替前面的。下面这种形式的Makefile全部以先决条件代替,此为全文:

     objects = main.o kbd.o command.o display.o \
               insert.o search.o files.o utils.o
     
     edit : $(objects)
             cc -o edit $(objects)
     
     $(objects) : defs.h
     kbd.o command.o files.o : command.h
     display.o insert.o search.o files.o : buffer.h


这里,“defs.h”为所有OBJ文件的先决条件;“command.h”与“buffer.h”为特定OBJ文件的先决条件。

不知这样紧凑的写法是否为一些人的爱好,但还是有一些人不喜欢这种写法,认为将一个目标跟与之对应的信息写在一起显得比较清楚。

清理目录的规则

编辑

编写Makefile所要写的不仅仅编译程序。Makefile还能做一些编译外的事情,比如说,如何将一个目录里的OBJ文件与可执行文件清理干净。

下面就是一例,演示如何用make以完成清除工作:

     clean:
             rm edit $(objects)


在实际应用中Makefile可能会写得更复杂,以应对突发事件,下为例:

.PHONY : clean
     clean :
             -rm edit $(objects)


本例的Makefile,可在有文件名为“clean”时避免混淆,还可在rm报错时仍旧执行。(参见第四章第五节 伪目标第五章第五节 命令中的报错信息

此类规则不可置于Makefile文首,这是由于一般不愿让其作为默认规则执行!故在前几节的完整Makefile示例中,用“edit”为默认规则,以便编译与重编译。

因为“clean”并非“edit”的先决条件,故此规则在make没有参数时不会运行。若要运行此规则,键入命令“make clean”(参见第九章 如何运行make)。