Makefile文件告诉make如何编译和链接一个程序。

make编译3条规则:

  • 如果源文件发生改变,那么该源文件必须重新编译。
  • 如果头文件发生改变,那么包含该头文件的源文件必须重新编译。每次编译都会产生一个源文件对应的目标文件。
  • 如果源文件重新编译,那么所有目标文件,无论是之前编译保留的还是新编译的,都必须重新链接生成新的可执行文件。

规则介绍

makefile由规则rules组成,rules模式如下所示:

target... : prerequisites...
    recipe
    ...
    ...
  • target通常是程序生成文件名,比如可执行文件或目标文件,也可以是要执行操作的名,比如clean
  • prerequisite通常作为生成target的输入文件。target通常依赖于多个文件。
  • recipemake要执行的动作。recipe可以有一行或多行命令。recipe的每行命令必须以tab开头。

通常recipe存在于拥有prerequisites的规则中,并且当prerequisites发生变化时重新生成target。然而recipe有时也存在于没有prerequisites的规则中,比如clean

规则rule诠释了合适以及如何执行命令来生成目标文件。make根据prerequisites执行recipe来生成或更新target

一个makefile也可以包含规制之外的文本,但是简单的makefile仅需要包含规则即可。

简单的Makefile

下面是一个简单的makefile文件,描述了一个由8个目标文件生成的可执行文件edit,这8个目标文件依赖于8个C源文件和3个头文件。

在这个例子中,每个C源文件都包含头文件defs.h,只有定义编辑命令的源文件包含头文件command.h,只有更改编辑器缓冲区的源文件包含头文件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

可以使用“\”将过长的行分为两行,以便于阅读。

在这个例子中,target包括可执行文件edit和所有目标文件。

prerequisites是类似于main.cdefs.h的文件。每个*.o文件既是target也是prerequisites

recipes包含cc -c main.c等。

没有任何prerequisites的动作名的target被称为伪目标.phony target,例如clean

make如何执行Makefile

默认情况下,make从第一个非“.”开头的target来处理。这个target被称为默认target。可以通过变量.DEFAULT_GOAL来指定默认target

在上节的例子中,默认的target就是可执行文件edit

当执行命令make时,make就会读取当前目录下的makefile并执行第一条规则。在上节的例子中,第一条规则时链接生成可执行文件edit,但是make在执行这条规则之前,必须执行其prerequisites的规则,这儿是目标文件。这些目标文件都有各自的规则,都是通过编译源文件来生成的目标文件。

如果prerequisites中的任何源文件或头文件比目标文件新,或者目标文件不存在,都需要重新编译生成目标文件。

执行其他规则的原因是这些规则是最终目标的prerequisites,如果这些规则不是最终目标的prerequisites,那么这些规则不会被执行,除非告诉make执行的目标,例如make clean

变量会使Makefile简化

在之前的例子中,有两次在规则中将所有的*.o目标文件列出来,这种重复很容易出错。可以定义字符串变量来替换这些目标文件。

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

在标准的Makefile中都会有一个名为objectsOBJECTSobjsOBJSobjOBJ的变量,这是所有目标文件名的列表。例如,

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

在需要使用目标文件列表的地方可以使用$(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 : 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自动推断recipes

没有必要详细写出编译每个C源文件的方法,因为make能够自动推断出:make有一条隐含规则即使用cc -c命令从相应名称的.c文件生成对应的.o文件。例如,recipe会使用命令cc -c main.c -o main.o编译main.c生成main.o。因此我们可以省略生成目标文件规则中的recipe

当使用这种方式自动生成.o文件时,相应名称的.c文件会自动被添加到prerequisites中,因此我们可以在prerequisites中省略掉相应的.c文件。

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的另一种风格

makefile中的目标文件都是通过隐含规则生成的,那么该makefile可以采用另一种风格。即可以通过prerequisites进行分组,而不是通过target

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

清理目录的规则

Makefile除了编译程序还可以做一些其他事情,比如删除所有目标文件和可执行文件,以使目录干净。

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

.PHONY可以防止和名为clean的文件产生混淆,因为clean不是editprerequisites,因此make不携带任何参数时是不会执行clean的,只有当make指定具体target,会在.PHONY中进行查找。

这种规则不能放在Makefile的头部,因为这种规则不能作为默认规则被执行。