1. 什么是Makefile
Makefile 是一种用于自动化构建和管理项目的工具,特别是在软件开发中非常常见。
它包含了一系列规则(rules)和指令,描述了如何编译和链接源代码文件,以及生成最终的可执行文件或库文件。
简单来说,在系统中存在一个叫做make的命令,该命令被使用之后,会在当前目录下寻找一个叫做makefile/Makefile的文件,依据其内定义的规则进行项目的自动化构建。
当我们的项目由多个文件构成时,Makefile能够极大地简化我们构建程序的过程。
Makefile 的优点
自动化构建:通过 Makefile 可以自动化执行编译、链接等构建任务,减少手动操作。
增量构建:Makefile 可以根据文件的时间戳来实现增量构建,避免不必要的重复构建过程。
可移植性:Makefile 是跨平台的构建工具,可以在不同的操作系统上使用
2. Makefile的基本知识和基本用法
2.1 目标,依赖关系和命令
Makefile 的基本结构包括目标、依赖关系和命令。
目标是指要构建的文件或动作,依赖关系描述了生成目标所需的前置条件,而命令则是实际执行的操作,通常是编译源文件、链接目标文件或执行其他操作。
例如,我们需要生成一个test.exe(在Linux下可不带后缀)的可执行程序,而编译出test.exe依赖于其源文件test1.c,test2.c,test3.c...等,则可用下面的语句表述:
目标:目标依赖的文件
命令
...
注意:命令前有且仅能有 "一个TAB" 键,不可以手动敲4个空格。
保存退出,输入make命令:
可以看到,我们再输入make命令并回车之后,自动执行了我们预设的命令,并编译生成了可执行程序test。
如果不希望被自动执行的命令回显到屏幕上,我们可以在命令前加上 "@" :
2.2 伪目标
在一个Makefile文件中可以有多个目标,默认会执行第一个目标,也可在make后紧跟要执行的目标,以指定执行某个目标:
上面提到过,目标也可以是动作,这时常常不生成文件,我们叫这种目标为伪目标。
例如,我们有一个目标(clean)的作用是清除生成的可执行程序以重新生成:
2.3 .PHONY
相信看完上面一小节之后,大家会都很好奇.PHONY是什么。其实,被.PHONY修饰的目标就是始终会执行的目标。
这么说来,test和shishen不是始终会被执行的目标吗?我们尝试多次执行make,会发现:
第一次执行可以成功,然而之后再去执行却发现无法成功,而显示test已是最新。
这是因为test所依赖的文件没有发生更新,所以再次进行make没有任何意义,只会浪费系统资源。
所以,make仅仅提示test已是最新,而不去执行默认目标。
Makefile 通过检查目标文件及其依赖关系的时间戳,确定哪些文件需要重新构建。如果目标文件需要重新构建,Makefile 将按照规则中定义的命令来执行,以生成最终的目标文件。这种方式允许进行增量构建,即只重新构建发生变化的文件,从而提高构建效率。
2.4 自动构建工程的工作原理
Makefile自动构建工程的工作原理:如果某个目标的依赖文件不存在当前目录下,make就会尝试查找与依赖文件同名的目标并优先执行。
如果没有同名目标则会报错,即使在某个目标中存在生成所需文件的指令。
更准确地说,依赖关系列表中的元素默认为文件,当文件不存在时,认为其是同名目标,即使该目标不会生成所需文件,该目标也会被执行。
在下面这个例子中,我们让.c文件一步一步地生成可执行程序,而不是直接生成:
可以看到,即使我们没有指定,其余的三个目标也自动执行了,而且顺序是按照依赖关系的先后进行的。
可以猜想,make程序的内部应该会维护一个栈,用于存储需要执行,但依赖关系不满足的目标。
3. 简化Makefile的技巧语法
3.1 宏定义
当然,不是C语言的那个宏定义,但是二者十分相似,例如:
此时,"SRC" 就代表了所需源文件的集合,定义的符号需要放到 "$()" 中才能使用。
在指令部分,也可用**"@" 代表目标,"^" 代表所依赖的文件**:
3.2 通配符
在Makefile中,通配符为 "%" ,假如要将所有.c文件先编译为对应的.o文件,再进行链接(也是最常用的方式):
3.3 相同后缀的集合
在源文件增多时,上面的Makefile需要在宏定义处增加对应的文件。
这未免太麻烦了,我们所使用的通配符也不能最大限度地发挥其作用。
而下面的写法可以解决这个问题:
4. 大多数情况下通用的Makefile
BIN=code
CC=gcc
SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)
LFLAGS=-o
FLAGS=-c
RM=rm -f
$(BIN):$(OBJ)
@echo "linking $^ to $@..."
@$(CC) $(LFLAGS) $@ $^
%.o:%.c
@echo "compling $< to $@..."
@$(CC) $(FLAGS) $<
.PHONY:clean
clean:
@echo "removing $(BIN)..."
@$(RM) $(BIN) $(OBJ)
@echo "Finish"