Makefile(2)------Makefile的基本语法

2024-01-09 10:24:02

Makefile是一种用于自动化编译程序的工具,它包含一系列规则来指示系统如何构建应用程序。在Makefile中,每个规则由一个目标文件、一个或多个依赖文件以及一组命令行指令组成。make 程序根据 Makefile 中的规则描述执行相关的命令来完成指定的任务。

在一个完整的 Makefile 中,包含了 5 个东西:显式规则、隐含规则、变量定义、 指示符和注释。

1.Makefile规则

? ? ? 熟悉规则对于书写Makefile至关重要,我们书写的Makefile的第一个规则应该就是重建整个程序或者多个程序的依赖关系和执行命令的描述。

1.1规则语法

一个简单的Makefile描述规则组成:

TARGET: PREREQUISITES

COMMAND

TARGET:规则的目标。通常是最后需要生成的文件名或者为了实现这个目的而必需 的中间过程文件名。可以是.o文件、也可以是最后的可执行程序的文件名等。另外,目 标也可以是一个make执行的动作的名称,如目标“clean”,我们称这样的目标是“伪 目标”。

PREREQUISITES:规则的依赖。生成规则目标所需要的文件名列表。通常一个目标依 赖于一个或者多个文件。

COMMAND:规则的命令行。是规则所要执行的动作(任意的 shell 命令或者是可在 shell 下执行的程序)。它限定了 make 执行这条规则时所需要的动作。一个规则可以有多个命令行,每一条命令占一行。

注意:每一个命令行必须以[Tab] 字符开始,[Tab]字符告诉 make 此行是一个命令行。make 按照命令完成相应的动作。

规则的中心思想是:目标文件的内容是由依赖文件文件决定,依赖文件的任何一处改动,将导致目前已经存在的目标文件的内容过期。规则的命令为重建目标提供了方法。 这些命令运行在系统 shell 之上。

举例说明:

现有一个加法运算的计算器add.c

#include<stdio.h>

void add_init()
{
????????printf("add operation??init.\n");
}

add.h

#ifndef _ADD_H
#define _ADD_H

void add_init();

#endif

operation.c

#include<stdio.h>
#include "add.h"

int main()
{
????????printf("operation beginning\n");
????????add_init();
????????return 0;
}

则其Makefile如何编写呢?

.PHONY:clean
all:test

test:add.o? operation.o
gcc -o test? add.o operation.o

operation.o :operation.c
gcc -o operation.o -c operation.c

add.o : add.c
gcc -o add.o -c add.c

clean:
rm? -rf? add.o test? operation.o

此代码依赖关系解析

1.2 Makefile特殊目标

在Makefile中,有一些名字,当它们作为规则的目标时,具有特殊含义。它们时一些特殊的目标,GUN make所支持的特殊的目标有很多,常用的特殊目标有:

.PHONY:

目标“.PHONY”的所有的依赖被作为伪目标。伪目标是这样一个目标:当使用 make命令行指定此目标时,这个目标所在规则定义的命令、无论目标文件是否存在都会被无条件执行。

1.3 Makefile伪目标

Makefile中一个重要的特殊目标:伪目标,它不代表一个真正的文件名,在执行 make 时可以指定这个目标来执行其所在规则定义的命令,有时也可以将一个伪目标称为标签。以下就对其简单使用进行分析解说:

? ? ?? 如果我们需要书写一个规则,规则所定义的命令不是去创建目标文件,而是通过make命令行明确指定它来执行一些特定的命令,比如常见的clean目标。

clean:
rm? *.o

此命令就是删除当前目录下的所有.o文件,当工作目录下不存在“clean”这个文件时,我们输入“make clean”,就会执行删除命令,实现了这条规则的设想结果。但是如果在当前工作目录下存在文件“clean”,同样我们输入 “make clean”,由于这个规则没有任何依赖文件,所以目标被认为是最新的而不去执行规则所定义的命令,因此命令“rm”将不会被执行。无法达到设想的结果。所以我们需要将目标“clean”声明为伪目标。将一个目标声明为伪目标的方法是将它作为特殊目标.PHONY”的依赖。如:

.PHONY : clean

这样目标“clean"就被声明为一个伪目标,无论在当前目录下是否存在“clean”这个 文件。我们输入“make clean”后。“rm”命令都会被执行。而且,当一个目标被声 明为伪目标后,make 在执行此规则时不会去试图去查找隐含规则来创建它。这样也提高了make 的执行效率,同时也不用担心由于目标和文件名重名。因此目标“clean”的完整书写格式应该如下:

.PHONY : clean
clean
rm? *.o

1.4 多目标

一个规则中可以有多个目标,规则所定义的命令对所有的目标有效。一个具有多目标的规则相当于多个规则。多目标规则意味着所有的目标具有相同的依赖文件。举例说明

可在上述Makefile中增加如下,以下为一个多目标

all:test2 test3
test2 test3:
@echo "hello world"

相当于

all test2 test3

test2:
@echo "hello world"

test3:
@echo "hello world"

发现make后打印了两次hello world。

1.5 多规则目标

? ?? Makefile 中,一个文件可以作为多个规则的目标(多个规则中只能有一个规则定义 命令)。这种情况时,以这个文件为目标的规则的所有依赖文件将会被合并成此目标一个依赖文件列表,当其中任何一个依赖文件比目标更新(比较目标文件和依赖文件的时 间戳)时,make 将会执行特定的命令来重建这个目标。

? ?? 如果多个规则同时给出重建此目标的命令,make将使用最后一个规则中所定义的命令,同时提示错误信息。

举例说明:

可在上述Makefile中增加如下,以下为一个多规则目标

all :test4

test4:
@echo "hello world"

test4:
@echo "welcome to China"

可见出现了警告,并且只显示了最后一条规则定义的命令。可用双冒号规则解决。

1.6 文件时间戳

make第一次编译某个项目时,会依次编译所有的源文件。但是当我们修改程序后,再次使用make编译,make并不会重新编译整个源文件,而是只编译你新添加或修改了的源文件,那make是如何做到的呢?

很简单,make是根据时间戳来判断一个规则中的目标依赖文件是否有更新。make在编译程序时,会依次检查依赖关系树中的所有源文件的时间戳,如果发现某个文件的时间戳有更新,会认为这个文件有改动过,会重新编译这个源文件。如果发现文件的时间戳没有更新,就不会再重新编译一次。

1.7 自动产生依赖

Makefile 中,有时需要书写一些规则来描述一个.o 文件和头文件的依赖关系。例如, 如果在 main.c 中使用“#include add.h”,那么我们可能就需要一个像下边那样的规则来描述头文件“add.h”被修改以后再次执行 make,目标“main.o”应该被重建。

operation.o :operation.c? add.h

这样,对于一个大型工程。就需要在 Makefile 中书写很多条类似于这样的规则。并且, 当在源文件中加入或删除头文件后,也需要小心地去修改 Makefile。现在的 c 编译器提供 了通过查找源文件中的“#include”来自动产生这种依赖关系的功能。Gcc 通过“-M” 选项来实现此功能,使用“-M”选项 gcc 将自动找寻源文件中包含的头文件,并生成 文件的依赖关系。如果在“main.c”中包含了标准库的头文件, 使用 gcc 的“-M”选项时,其输出结果中也包含对标准库的头文件的依赖关系描述。 当不需要在依赖关系中考虑标准库头文件时,对于 gcc 需要使用“-MM”参数。

gcc -MM operation.c

其输出是:

operation.o :operation.c add.h

1.8 隐式规则

隐式规则是指 Makefile 中不需要明确指定的规则,而是根据文件名的后缀自动生成的规则。由Make自动推导出来的规则。隐式规则包括了Make的基本命令集,可以用于自动化编译和链接。

比如说:将.c文件编译成.o文件

add.o:add.c
gcc -c add.c -o add.o

Make可以自动推导出这个隐式规则,通过使用隐式规则,可以简化Makefile的编写,特别是对于比较大的项目,隐式规则能够自动处理许多常见的任务,减少了Makefile的复杂度和工作量。

1.9模式匹配

? ?? 通常,模式规则中目标模式由前缀、后缀、模式字符“%”组成,这三个部分允许 两个同时为空。实际文件名应该是以模式指定的前缀开始、后缀结束的任何文件名。文 件名中除前缀和后缀以外的所有部分称之为“茎”(模式字符“%”可以代表若干字符。 因此:模式“%.o”所匹配的文件“test.c”中“test”就是“茎”)。模式规则中依赖文 件名的确定过程是:首先根据规则定义的目标模式匹配实际的目标文件,确定“茎”, 之后使用 “茎”替代规则依赖文件名中的模式字符“%”,生成依赖文件名。这样就产 生了一个明确指定了目标和依赖文件的规则。例如模式规则:“%.o : %.c”,当“test.o” 需要重建时将形成规则“test.o : test.c”。例如上述Makefile中:

operation.o :operation.c
gcc -o operation.o -c operation.c

add.o : add.c
gcc -o add.o -c add.c

当有大量的C文件生成O文件,分别写非常麻烦,于是可以改为:

%.o:%.c???????
gcc -o $@ -c $^

执行make test 命令,发现仍跟上述Makefile命令一致。

1.10规则的命令

规则的命令由一些 shell 命令行组成,它们被一条一条的执行。规则中除了第一条 紧跟在依赖列表之后使用分号隔开的命令以外,其它的每一行命令行必须以[Tab]字符 开始。多个命令行之间可以有空行和注释行。

? ?? 例如打印命令@echo "hello world",make都会开一个进程,命令执行完后,make都会检测到命令的返回码0,然后make会继续执行下一个命令,若命令执行出错,make会终止执行当前规则,退出编译。

1.11命令的执行

? ?? 在 Makefile 中书写在同一行中的多个命令属于一个完整的 shell 命令行,书写在独立行的一条命令是一个独立的 shell 命令行。因此:在一个规则的命令中,命令行“cd” 改变目录不会对其后的命令的执行产生影响。就是说其后的命令执行的工作目录不会是 之前使用“cd”进入的那个目录。如果要实现这个目的,就不能把“cd”和其后的命 令放在两行来书写。而应该把这两条命令写在一行上,用分号分隔。这样它们才是一个完整的 shell 命令行。

举例如下:Makefile

all :
cd? /;pwd

如果希望把一个完整的 shell 命令行书写在多行上,需要使用反斜杠(\)来对处于 多行的命令进行连接,表示他们是一个完整的 shell 命令行。

举例如下:Makefile

all :? ?
cd /;? \
pwd

1.12并发执行命令

GNU make 支持同时执行多条命令。通常情况下,同一时刻只有一个命令在执行, 下一个命令只有在当前命令执行完成之后才能够开始执行。不过可以通过 make 的命令 行选项“-j”或者“--job”来告诉 make 在同一时刻可以允许多条命令同时被执行。如果选项“-j”之后存在一个整数,其含义是告诉 make 在同一时刻可允许同时执 行命令的数目。例如:

make?? -j4

说明同一时刻可以允许同时执行四条命令。

文章来源:https://blog.csdn.net/IVY_WANG_QAQ/article/details/135457513
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。