文章目录
1. Makefile简介
您需要一个名为makefile的文件来告诉make该怎么做。通常,makefile会告诉make如何编译和链接程序。
如果头文件更改,则必须重新编译包含头文件的每个C源文件以确保安全。每次编译都会生成与源文件对应的对象文件。
1.1 规则简介
一个简单的makefile由具有以下格式的“规则(rule)”组成:
target … : prerequisites …
recipe
…
…
target(目标)通常是由程序生成的文件的名称; 目标的示例是可执行文件或目标文件。目标也可以是要执行的操作的名称,例如 ‘clean’。
prerequisite(先决条件)是一个文件,该文件作为输入用于创建目标。一个目标通常依赖于几个文件。
recipe(配方)是make执行的动作。配方可能在同一行或每行都具有多个命令。请注意:需要在每个配方行的开头添加一个Tab符(制表符)!如果希望在配方中使用制表符以外的其他字符作为前缀,则可以将 .RECIPEPREFIX 变量设置为备用字符。
通常,配方在具有先决条件的规则中。如果先决条件发生更改,配方可用于创建目标文件。但是,为目标指定配方的规则不需要先决条件。例如,规则包含delete命令,同时该规则与目标“clean”关联,则该规则没有先决条件。
然后,一条规则说明了如何以及何时重新制作作为特定规则目标的某些文件。make根据创建或更新目标的先决条件执行配方。规则还可以解释如何以及何时执行操作。
makefile可以包含规则之外的其他文本,但是一个简单的makefile只需要包含规则。规则看起来可能比此模板中显示的要复杂,但是所有规则或多或少都适合该模式。
1.2 一个简单Makefile示例
下面是一个简单的makefile,它描述了一个名为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
使用反斜杠 / 换行符将每一长行拆分为两行;这类似于使用一条长行,但更易于阅读。
在这个makefile示例中,目标包括可执行文件edit,对象文件main.o、kbd.o。先决条件是main.c、defs.h这样的文件。实际上,每一个‘.o’文件同时是先决条件和目标。配方包括 ‘cc -c main.c’ 和 'cc -c kbd.c’等。
要使用此makefile创建名为edit的可执行文件,请键入:
make
要使用此makefile从目录中删除可执行文件和所有对象文件,请键入:
make clean
当目标是文件时,如果其先决条件发生变化,则需要重新编译或重新链接。此外,任何自动生成的先决条件都应该首先被更新。在这个例子中,edit依赖8个对象文件中的每一个;对象文件main.o依赖源文件main.c和头文件defs.h。
配方在包含目标和先决条件的每一行后面。这些配方说明了如何更新目标文件。制表符(或 .RECIPEPREFIX 变量指定的任何字符)必须出现在配方中每一行的开头,以将配方与makefile中的其他行区分开。(请记住,make对于配方的工作方式一无所知。更新目标文件的正确与否取决于你提供的配方。make所做的只是在需要更新目标文件时执行指定的配方。)
目标’clean’不是文件,而是一个操作的名称。由于您通常不想执行此规则中的操作,因此,'clean’不是任何其他规则的先决条件。因此,make不会对它做任何事情,除非你明确地告诉它。请注意,此规则不仅不是先决条件,也没有任何先决条件,因此该规则的唯一目的是运行指定的配方。不引用文件但只是操作的目标称为伪目标。
1.3 make如何处理Makefile
默认情况下,make从第一个目标开始执行(不是名称以“.”开头的目标)。这被称为默认目标。(您可以使用命令行或使用 .DEFAULT_GOAL 特殊变量重写此行为。)
在上一部分的简单示例中,默认目标是更新可执行程序 edit。 因此,我们将该规则放在首位。
因此,当您发出命令:
make
make读取当前目录中的makefile,然后开始处理第一条规则。在示例中,此规则用于重新链接 edit;但是在make处理此规则之前,它必须处理所依赖的文件的规则,在本例中,这些文件是对象文件。每一个对象文件都按照自己的规则被处理。这些规则规定通过编译源文件来更新每个“.o”文件。如果源文件或任何被当做先决条件的头文件比对象文件更加新,或者对象文件不存在,则必须执行该处理。
处理其他规则是因为它们是默认目标的先决条件。如果默认目标不依赖某个规则,则该规则不会被处理,除非您告知make这样做(比如使用命令 make clean)。
重新编译对象文件之前,make会考虑更新其先决条件、源文件和头文件。如果.c和.h文件不是任何规则的目标,则make不会对这些文件执行任何操作。但是make会更新自动生成的C程序,比如Bison或Yacc根据规则生成的程序。
重新编译需要的任何目标文件后,make决定是否重新链接edit。如果文件edit不存在,或者目标文件比它新,则会重新链接edit。
因此,如果我们更改文件insert.c并运行make,make将编译该文件以更新insert.o,然后链接edit。如果我们更改文件command.h并运行make,make将重新编译目标文件kbd.o, command.o 和 files.o,然后链接文件edit。
1.4 变量使Makefile更简单
在示例中,我们必须为edit两次列出所有目标文件:
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。变量允许一次定义文本字符串,然后在多个位置进行替换。
标准做法是,每个makefile文件都有一个名为 objects, OBJECTS, objs, OBJS, obj, 或 OBJ 的变量,用于列出所有对象文件的名称。我们可以在makefile中定义一个如下的objects变量:
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
然后在每一个需要列出所有对象文件名称的地方,我们可以使用$(objects)进行代替。
将变量用于1.2节的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)
1.5 让make推断配方
不必详细说明编译单个C源文件的方法,因为make可以找出它们:它有一个隐式规则,使用“cc -c”命令从相应名称的“.c”文件更新“.o”文件。例如,它将使用配方’cc -c main.c -o main.o’将main.c编译为main.o。因此,我们可以从对象文件的规则中省略配方。
当以这种方式自动使用“.c”文件时,.c文件也会自动添加到先决条件列表中。因此,我们可以从先决条件中省略“.c”文件,前提是我们省略了配方。
下面是整个示例,包括上述两种更改以及1.4节所建议的变量对象:
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的方式。因为隐式规则非常方便,所以它们很重要。您将看到它们经常使用。
1.6 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 作为所有目标文件的先决条件;command.h 和 buffer.h 是为其列出的特定对象文件的先决条件。
这种做法的好坏取决于个人口味:它更紧凑,但是有些人不喜欢它,因为他们发现将每个目标的所有信息放在一个地方更加清楚。
1.7 清理目录的规则
Makefile通常会告诉您除编译程序外还要做些其他事情。例如,如何删除所有的对象文件和可执行文件,以便目录保持干净。
我们编写了一个make规则来清理示例:
clean:
rm edit $(objects)
在实践中,我们可能希望以某种更复杂的方式编写规则以处理意外情况。我们可以这样做:
.PHONY : clean
clean :
-rm edit $(objects)
这可以防止make被一个名为clean的实际文件误导,并使它在rm出错的情况下继续运行。
诸如此类的规则不应放在makefile的开头,因为我们不希望它默认运行!
由于clean不是edit的先决条件,因此如果我们不带任何参数的给出命令’make’,这条规则并不会执行。为了使规则运行,我们必须输入’make clean’。
参考文档
https://www.gnu.org/software/make/manual/html_node/Introduction.html#Introduction