网站搜索

使用 GNU Make 进行开源软件开发中的“Makefile”简介


GNU Make 是一种开发实用程序,它确定要重新编译的特定代码库的部分,并可以发出命令以在代码库上执行这些操作。这个特定的 make 实用程序可以与任何编程语言一起使用,前提是可以通过发出命令从 shell 完成编译。

为了使用GNU Make,我们需要一些规则来定义程序中不同文件之间的关系以及更新每个文件的命令。它们被写入一个名为“makefile”的特殊文件中。 “make”命令使用“makefile”数据库和文件的最后修改时间来决定所有文件都要重新编译。

Makefile 的内容

一般来说'makefiles'包含5种东西,即:隐式规则显式规则变量定义指令注释

  1. 显式规则指定如何创建/重新创建一个或多个文件(称为目标,稍后将解释)以及何时执行相同操作。
  2. 隐式规则指定如何根据文件名创建/重制一个或多个文件。它描述了目标文件名如何与名称与目标相似的文件相关联。
  3. 变量定义是为稍后要替换的变量指定字符串值的行。
  4. 指令是 make 在读取 makefile 时执行一些特殊操作的指令。
  5. #”符号用于表示 makefile注释的开始。以“#”开头的行将被忽略。

Makefile 的结构

告诉make如何重新编译系统的信息来自于读取名为makefile的数据库。一个简单的makefile将由以下语法规则组成:

target ... : prerequisites ... 
	recipe 
... 
...

目标被定义为程序生成的输出文件。它也可以是虚假目标,这将在下面解释。目标文件的示例包括可执行文件目标文件虚假目标,例如干净安装卸载等。

先决条件是用作创建目标文件的输入的文件。

配方make根据先决条件创建目标文件所执行的操作。有必要在 makefiles 中的每个配方之前放置制表符,除非我们指定 '.RECIPEPREFIX' 变量来定义其他字符作为前缀到食谱。

示例 Makefile

final: main.o end.o inter.o start.o
	gcc -o final main.o end.o inter.o start.o
main.o: main.c global.h
	gcc -c main.c
end.o: end.c local.h global.h
	gcc -c end.c
inter.o: inter.c global.h
	gcc -c inter.c
start.o: start.c global.h
	gcc -c start.c
clean:
	rm -f main.o end.o inter.o start.o

在上面的示例中,我们使用4 C源文件和两个头文件来创建可执行文件final。这里每个“.o”文件既是 makefile 内的目标,也是先决条件。现在看一下最后一个目标名称clean。它只是一个动作而不是目标文件。

由于我们通常在编译过程中不需要它,因此它不会作为任何其他规则的先决条件编写。不引用文件而只是操作的目标称为虚假目标。它们不会像其他目标文件那样有任何先决条件。

GNU Make 如何处理 Makefile

默认情况下,make 从“makefile”中的第一个目标开始,并称为“默认目标'。考虑到我们的示例,我们将 final 作为我们的第一个目标。由于其先决条件包括其他目标文件,因此在创建最终之前要更新这些文件。这些先决条件中的每一个都根据其自己的规则进行处理。

如果对源文件头文件进行了修改,或者目标文件根本不存在,则会发生重新编译。重新编译必要的目标文件后,make决定是否重新链接final。如果文件 final 不存在,或者任何目标文件比它新,则必须执行此操作。

因此,如果我们更改文件 inter.c,那么在运行 make 时,它将重新编译源文件以更新目标文件inter.o,然后链接final

在 Makefile 中使用变量

在我们的示例中,我们必须在 final 规则中列出所有对象文件两次,如下所示。

final: main.o end.o inter.o start.o
	gcc -o final main.o end.o inter.o start.o

为了避免这种重复,我们可以引入变量来存储 makefile 中正在使用的目标文件列表。通过使用变量OBJ,我们可以将示例makefile重写为如下所示的类似文件。

OBJ = main.o end.o inter.o start.o
final: $(OBJ)
	gcc -o final $(OBJ)
main.o: main.c global.h
	gcc -c main.c
end.o: end.c local.h global.h
	gcc -c end.c
inter.o: inter.c global.h
	gcc -c inter.c
start.o: start.c global.h
	gcc -c start.c
clean:
	rm -f $(OBJ)

清理源目录的规则

正如我们在示例 makefile 中看到的,我们可以定义规则来清理源目录,方法是在编译后删除不需要的目标文件。假设我们碰巧有一个名为clean的目标文件。 make如何区分以上两种情况?这里出现了虚假目标的概念。

虚假目标并不是真正的文件名,而只是每当从makefile<发出显式请求时执行的配方的名称。。使用虚假目标的一个主要原因是避免与同名文件发生冲突。另一个原因是为了提高性能。

为了解释这件事,我将揭示一个意想不到的转折。默认情况下,运行 make 时不会执行 clean 的配方。相反,有必要通过发出命令 make clean 来调用相同的命令。

.PHONY: clean
clean:
	rm -f $(OBJ)

现在尝试为您自己的代码库创建makefile。欢迎在此发表您的疑问。