Makefile入门
Makefile
Makefile是一个有点晦涩的话题。有人认为,世界上只有一个makefile,而其他所有makefile版本都是它的扩展。然而,这不是真的。在这篇文章中,我会向大家介绍自己编写makefile的方法。
背景知识:理解 make 命令
如果你以前使用过make
,那你大可以略过本节。
makefile是一种将目标与和这个目标相关的一系列命令关联起来的一个文件,这些命令会在这个目标动作被请求的时候执行。例如:clean是一个常用的makefile目标,它能够在编译结后,对目标文件和可执行文件等进行清理。
当你从命令行启动make
命令,它会读取makefile作为它的配置信息。如果用户没有指定,那么它会默认地在当前目录中读取名叫Makefile的文件(注意大小写——译者注)。一般而言,make
要么单独执行,要么伴有指定的目标。(在下文中,%将用作命令提示符)
执行默认的目标:
% make
执行一个特定的目标,例如clean:
% make clean
此外,make
还能检查文件的时间戳并且判断哪些文件需要被重新编译。我们会在“目标 Targets”章节中进行详述。你现在只需要知道,make可以减少一些文件的编译次数。
“目标”与“目标文件”
目标:makefile中的Target,对应于一系列的目标动作和与它相关的依赖。
目标文件:通常由gcc等编译器生成,扩展名以.o结尾。
————译者注
Makefile的元素
大多数makefile具有两个基本的组成部分:宏与目标的定义。宏是非常有用的常量:它允许你方便地对你的程序的主要部分进行修改,他们也许会出现在很多地方。例如,你创建一个替换编译器名字的宏,于是某一天你突然不想再用gcc了,你只需要修改一行代码就能达到目的。
注释
使用前导的#符号,可以使整行成为注释。
宏 Macros
宏可以简单地写成x=y的形式。例如,将C编译器设置为gcc,你应该这样写:
CC=gcc
为了让宏变得有意义,你只需要把宏名称包含在$()中。例如,要将CC转为编译器名字:
$(CC) a_source_file.c
这条语句会展开成:
gcc a_source_file.c
事实上,也可以将宏定义为另一个宏的值。例如,你定义了一个编译参数的宏OPT,当然还有编译器CC,你希望将他们结合为一个宏COMP:
COMP = $(CC) $(OPT)
很多宏名称都有默认的值,你可以通过该命令查看:
% make -p
例如,CC默认是cc
编译器。另外还要注意,任何环境变量都会作为宏导入到你的makefile中,并且会覆盖对应宏的默认值。
目标 Targets
目标是makefile行为的核心:它将一行命令输入转化为一系列动作。例如,make clean
命令告诉make去执行那些在clean后面的的代码。目标(Target)由三个组成部分:目标名称、目标的依赖(dependency),以及和这个目标相关的一系列动作(action):
target: [dependencies] <command> <command 2> ...
注意
每一行必须以一个制表符TAB开始,不可以以4个或者8个空格代替。所以,请确保你的文本编辑器不会自动将制表符展开。
与依赖相关是目标或者文件本身。如果目标依赖于文件,并且从上次该目标被执行开始,这些文件之一被修改,那么目标动作才会再次为它们执行。如果目标依赖于其他目标,那么这些目标的代码同样会被执行。
一个简单的命令可以没有依赖,这样它总能在你需要的时候执行。例如,clean目标可以是这样的:
clean: rm -f *.o core
但另一方面,当你编译程序的时候,很有可能要依赖于其他文件才能编译。这让makefile文件看上去是这样的:
CC = gcc FILES = in_one.c in_two.c OUT_EXE = out_executable build: $(FILES) $(CC) -o $(OUT_EXE) $(FILES)
现在,如果文件in_one.c和in_two.c的修改时间比目标文件的创建时间要老,那么当你执行make build
的时候,make会告诉你“没神马可以做的。。(nothing to be done.)” 请注意:如果你漏掉一些依赖文件,那么这可能会导致一些问题。这时,可以把上面的文件像这样修改:
CC = gcc FILES = in_one.c in_two.c OUT_EXE = out_executable build: $(FILES) $(CC) -o $(OUT_EXE) $(FILES) clean: rm -f *.o core rebuild: clean build
现在,当你执行rebuild目标,make就会先删除相关的目标文件,然后再重新建立目标文件。
当目标执行失败时 When Targets Fail
目标执行后,无论它是否执行成功,都会返回一个状态。如果目标执行失败,那make就不会执行任何依赖于它的目标了。例如,在上面的例子中,如果clean失败了,那么rebuild也就不会执行build目标了。这只可能在没有内核文件(core file)的时候才会发生,但是只需要在命令前面加上一个减号–就能忽略命令返回的状态了。
clean: -rm -f *.o core
默认目标 The Default Target
如果仅仅输入make
,那么通常会引发一些合理的行为。在没有指定目标的时候,make只会执行makefile中的第一个目标。在上面的例子中,build在clean之前,这要比默认删除目标文件来的合理得多。
阅读他人的Makefile
我希望本文已经足够让你维护自己的工作了。理解makefile的窍门在于理解你编译器的所有标识(flags)。makefile的神秘之处仅仅在于宏的使用,灵活使用宏可以使一长串编译命令看起来更容易理解。所以,你的编译器文档可以很大程度上帮到你。
另外,当你执行make的时候,它会为你展开所有的宏。这会在你理解一个非常复杂的命令的时候帮到你。