前言
一、Makefile的结构
Makefile 通常由一系列规则组成,每条规则定义了如何从源文件生成目标文件。每个规则又由目标、依赖和命令三部分组成。
下面是 Makefile 规则的基本结构:
target: dependencies command1 command2 ...
其中,target 是要生成的目标文件名,dependencies 是生成目标文件所依赖的文件或目录,而 command1、command2 等则是生成目标文件所需执行的命令。
例如,以下的规则:
hello: hello.c gcc -o hello hello.c
指定了一个规则,用于将 hello.c 编译成可执行文件 hello。其中,hello 是目标文件名,hello.c 是依赖的源文件名,而 gcc 则是生成目标文件所需要的命令。注意,在命令行中,我们需要使用 tab 而非空格来缩进命令行。
首先我们先编写一个hello.c:
#include <stdio.h> int main(void) { printf("Hello World\n"); return 0; }
执行make命令:
ls查看生成的文件:
这里可以看到生成了一个hello可执行文件。
执行hello可执行文件:
二、深入案例
这个 Makefile 有一个 all 目标,依赖于 test1 目标。all 目标包含一行命令 echo hello world,用于输出字符串 “hello world”。test1 目标没有依赖关系,包含一行命令 echo test1,用于输出字符串 “test1”。
当我们执行 make 命令时,make 会首先查找 Makefile 文件,并读取 all 目标。因为 all 目标依赖于 test1 目标,因此 make 会接着查找 test1 目标,并执行其命令。在 test1 的命令执行完成后,make 会回到 all 目标,并执行其命令,即输出字符串 “hello world”。
all : test1 echo hello world test1 : echo test1
因此,make 命令的输出结果如下:
三、Makefile中的一些技巧
在命令前面加上@可以不显示出执行的命令:
all : test1 @echo hello world test1 : @echo test1
执行结果;
这里我们可以看到执行make后没有显示出执行的命令。
将all和最终可执行文件名放在makefile的第一个目标里面:
hello all : hello.o func.o gcc -o hello hello.o func.o hello.o : hello.c gcc -c -o hello.o hello.c func.o : func.c gcc -c -o func.o func.c
对应文件代码:
hello.c:
extern void func(void); int main(void) { func(); return 0; }
func.c:
#include <stdio.h> void func(void) { printf("Hello World\n"); }
执行make命令:
再次执行make命令:
我们发现这里执行第二次make命令的时候会告诉我们hello已经是最新的了,那么就不会再次进行编译执行了。
在执行Make命令时,Make会根据规则的依赖关系判断哪些规则需要重新执行以及哪些规则可以跳过。
如果Make发现生成目标的依赖文件没有更新,那么就没有必要重新生成目标文件。因此,Make会跳过这条规则,并且不执行规则中的命令。只有当目标依赖关系中的某个文件发生了变化,才会导致相关规则和命令的重新执行。
这种依赖关系可以有效地提高Make的构建效率,避免不必要的重复构建。当你修改了源代码文件时,Make会自动检测出需要重新构建的目标,并生成最新的可执行文件或静态库。
那么加上all又可以起到什么作用呢:
使用make all命令:
当执行make all时make程序会自动寻找到makefile中的all目标进行执行。