Makefile入门

作者: lesca 分类: Tutorials 发布时间: 2011-08-20 20:05

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中的第一个目标。在上面的例子中,buildclean之前,这要比默认删除目标文件来的合理得多。

阅读他人的Makefile

我希望本文已经足够让你维护自己的工作了。理解makefile的窍门在于理解你编译器的所有标识(flags)。makefile的神秘之处仅仅在于宏的使用,灵活使用宏可以使一长串编译命令看起来更容易理解。所以,你的编译器文档可以很大程度上帮到你。
另外,当你执行make的时候,它会为你展开所有的宏。这会在你理解一个非常复杂的命令的时候帮到你。

References

本文由 lesca 翻译自cprogramming.com 的文章Makefiles
您可以在CC许可证下自由转载

版权声明

本文出自 Lesca 技术宅,转载时请注明出处及相应链接。

本文永久链接: https://www.lesca.cn/archives/makefiles.html

如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!