文章目录
- 一、介绍
- 二、工作原理
- 三、使用
- 1、make的使用
- 2、make的常用选项
- 3、Makefile文件
- 四、总结
一、介绍
make工具和 Makefile 文件是 Linux下非常重要的自动化构建工具, Makefile定义了项目的编译规则,指示make如何编译和链接源代码以生成可执行文件或其他输出。并且make 能够根据源文件的时间戳自动判断哪些文件需要重新编译,从而只编译那些需要更新的部分,极大地提高了开发效率。
二、工作原理
- make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
- 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“hello”这个文件, 并把这个文件作为最终的目标文件。
- 如果hello文件不存在,或是hello所依赖的后面的hello.o文件的文件修改时间要比hello这个文件新(可以用touch测试),那么,他就会执行后面所定义的命令来生成hello这个文件。
- 如果hello所依赖的hello.o文件不存在,那么make会在当前文件中找目标为hello.o文件的依赖性,如果找到则再根据那一个规则生成hello.o文件。(这有点像一个堆栈的过程)
- 当然,你的C文件和H文件是存在的啦,于是make会生成 hello.o 文件,然后再用 hello.o 文件声明 make的终极任务,也就是执行文件hello了。
- 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
- 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错, 而对于所定义的命令的错误,或是编译不成功,make根本不理。
- make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起, 我就不工作啦。
三、使用
1、make的使用
语法:
make
make会从上到下扫描Makefile文件,直到找到第一个目标名。
make name
name : 目标名
make会从上到下扫描Makefile文件,直到找到name。
2、make的常用选项
make -f filename:使用指定的Makefile文件执行构建。
make -C directory:在指定的目录中执行构建。
make -n:显示执行Makefile时将执行的命令,但不实际执行。
make -B:强制重新构建目标,即使目标已经是最新的。
make -j n:使用多个并行任务进行构建,其中n是并行任务的数量(这里的 n 是你希望并行执行的任务数量,通常设置为 CPU 核心数的 1.5 到 2 倍可以获得较好的效果,但这取决于你的具体系统和构建过程中的 I/O 密集度),可以显著提高构建速度。
对于:name -j n 选项
假如你的项目包含大量的源文件,需要编译成多个目标文件(.o 文件),并最终链接成可执行文件。不使用并行任务时(即只使用单个任务),make 会按顺序编译每个源文件,然后链接它们。这可能需要很长时间,尤其是当源文件数量很多时。然而,使用 make -j 8(假设你希望利用 CPU 和可能的 I/O 超线程,这里 8 是 CPU 核心数的两倍),make 会同时启动 8 个编译任务(或者尽可能多,直到达到 8 个),这样 CPU 就可以被更充分地利用,编译过程也会更快。
all: program
program: file1.o file2.o file3.o
gcc file1.o file2.o file3.o -o program
file1.o: file1.c
gcc -c file1.c -o file1.o
file2.o: file2.c
gcc -c file2.c -o file2.o
file3.o: file3.c
gcc -c file3.c -o file3.o
clean:
rm -f *.o program
此时make会尝试去同时编译三个.o文件。
3、Makefile文件
(1)基础元素
- 规则(Rules):
目标(Target):规则想要生成的文件名,比如可执行文件或对象文件。
依赖(Prerequisites):生成目标所依赖的文件列表。如果依赖的文件比目标文件更新,则规则中的命令会被执行。
命令(Commands):生成目标所需执行的命令,通常是编译器或链接器的调用。- 变量(Variables):用于存储文件名、编译器选项等,以便在多个规则中重用。
- 函数(Functions): 用于处理文件名、执行命令等,提供高级功能。
如: 假设VARIABLE = file.txt 函数$(VARIABLE)用途: 将VARIABLE替换成file.txt- 注释(Comments): 以#开头的行是注释,不会被make执行。
(2)伪目标
概念:
伪目标(也称为“伪目标”或“标签”)是指那些不代表实际文件名(如文件、目录等)的目标。伪目标主要用于执行某些命令或操作,而不是创建或更新文件名所指向的文件。由于伪目标不代表任何文件,因此每次执行make命令时,伪目标都会被视为已过期(即需要重新执行),除非使用了.PHONY声明来显式标记它们为伪目标。
定义伪目标:
伪目标在Makefile中就像普通目标一样被定义,但是通常不会对应到实际的文件。为了明确地将某个目标标记为伪目标,可以使用.PHONY特殊目标来声明它:
.PHONY: clean
clean:
rm -f *.o my_program
在这个例子中,clean是一个伪目标,它用于删除所有的.o文件和my_program可执行文件。通过.PHONY: clean声明,make知道clean是一个伪目标,因此每次执行make clean时,都会执行rm -f *.o my_program命令,而不会去检查是否存在一个名为clean的文件来决定是否需要执行该命令。
(3)利用上面的特性写一个Makefile文件
生成一个可执行程序
代码:
1 te = test.c
2 t = test
3
4 $(t) : $(te)
5 #test : test.c
6 #gcc -o test test.c
7 gcc -o $(t) $(te)
8
9 .PHONY: clean
10 clean:
11 #rm -rf test
12 rm -rf $(t)
解析:
(4)Makefile的一些高级特性
1、自动变量:
$@:表示规则中的目标文件名。
$<:表示规则中的第一个依赖文件名。
$^:表示规则中的所有依赖文件列表。
2、模式规则:
使用通配符定义规则,如%.o: %.c,表示所有.o文件都由对应的.c文件生成(通过对应的.c文件生成同名的.o文件)。
条件判断:
使用ifeq、ifneq、ifdef和ifndef等关键字进行条件编译。
(5)使用一些高级特性写一个Makefile文件
生成:一个可执行程序
1 target = test
2 depend = test.o
3
4 $(target) : $(depend)
5 gcc $^ -o $@
6
7 %.o : %.c
8 gcc -c $<
9
10 .PHONY: clean
11 clean:
12 rm -rf $(depend)
13
四、总结
上述讲的make/makefile只是一些基础使用,make/makefile还有许多操作与功能,想了解更多可以参考陈皓大佬的跟我一起写Makelile。