一、为什么需要 Makefile在linux开发过程中当项目只有一两个.c文件时手动执行gcc命令并不麻烦gcc main.c hello.c-oapp但随着项目规模增长源文件越来越多手动编译变得难以维护特别是对于有复杂依赖关系的项目效率低每次改动一个文件就要重新输入冗长的编译命令容易出错漏编译某个文件、忘记清理中间产物浪费资源未修改的文件被重复编译Makefile的出现解决了这些问题——它定义了一套编译规则make命令自动按规则执行并智能地只重新编译被修改过的文件增量编译。二、快速上手第一个 Makefile2.1 项目结构假设有一个最简单的 C 项目project/ ├── code.c # 源文件---假设我们写了hello world └── Makefile # 编译规则文件2.2 编写 Makefilecode:code.c gcc code.c -o code这是最简单的Makefile格式之后在终端输入make即可编译文件2.3 使用make#终端会输出 gcc code.c -o code2.4 语法要点一条 Makefile 规则由两部分组成编译目标编译依赖文件所需要执行的命令必须以tab键开头比如上述的code就是编译的目标文件而code.c就是编译依赖所执行的命令就是gcc code.c -o code2.5 链式依赖我们以C代码的编译四个过程为例编译四个过程为预处理----.c文件变为.i编译-------.i文件变为.s汇编 ------.s文件变为.o文件链接-------.o文件变为可执行文件Makefile示例# 链接阶段.o 生成可执行文件依赖为main.o而main.o在下一行为也有依赖以此类推 main: main.o gcc main.o -o main # 汇编阶段.s 汇编成目标文件 .o main.o: main.s gcc -c main.s -o main.o # 编译阶段预处理文件.i 编译成汇编.s main.s: main.i gcc -S main.i -o main.s main.i: main.c gcc -E main.c -o main.i编译完成后即可生成main.i,main.s,main.o以及可执行文件main2.6 清理编译产物由于make会寻找Makefile的第一个目标第一个目标就是我们要编译的目标如果要增加一个清除所有编译产物的命令比如clear,clean,就需要生成一个伪目标当我们指定这个命令时就不会去找第一个要执行的目标而是我们指定的命令目标在Makefile文件后添加.PHONY:clean #表示声明一个伪目标clean clean: #目标clean rm -f code code.o code.s code.i在终端执行make clean即可删除编译产物三、标准化Makefile可以看出其实Makefile只是将一些要编译的命令放在一个文件当中当要执行的时候只是把命令缩短了。如何把一个Makefile能够更加拓展性的用在各个项目框架中呢变量使用变量来代替一些文件名即可做到拓展性BINcode #生成的可执行文件名 SRC$(wildcard *.c) #编译源文件表示该文件下所有.c文件 COMPILEgcc #你要使用的编译器 CFLAGS-c #编译选项,生成目标文件 #一般情况下会保留编译的目标二进制文件即.o文件 LFLAGS-o #链接形成可执行文件选项 OBJ$(SRC:.c.o) #表示该文件夹下所有.c的同名.o文件 $(BIN):$(OBJ) $(COMPILE) $^ $(LFLAGS) $ #^是将所有的OBJ都进行使用 echo 将$^链接为$ %.o:%.c $(COMPILE) $(CFLAGS) $ #是一一对应,也就是将对应的.c文件意义对应为.o文件 echo 将$编译为$ .PHONY:clean clean: $(RM) $(BIN) $(OBJ)上述代码中BIN,SRC等等是我们手动设置的变量需要我们自己定义而^、、等变量为自动变量有其独特的含义^表示在目标与依赖关系中冒号右边的所有文件:表示冒号右边的文件与冒号左边的文件一一对应的命令关系如果在所有命令之前表示关闭回显也就是执行了make之后不用显示具体执行了哪个命令,如果用作变量则表示冒号左边的所有文件^和的区别在于前者是指所有文件比如gcc code1.o code2.o code3.o -o code其中的code1~3.o就可以用^表示后者在gcc -c code1.c -o code1.o、gcc -c code2.c -o code2.o、gcc -c code3.c -o code3.o这一组命令中code1~3.c可以用来代替注意在使用变量时需要在变量前添加$符号标识这是一个变量我们还可以添加一些提示信息、比如将xxx.c编译为了xxx.o将xxx1.o,xxx2.o,xxx3.o链接为了xxx.exe如下BINcode.exe #编译执行的可执行文件 CCgcc #编译器 SRC$(wildcard *.c) #源文件 CFLAGS-c #编译选项 RMrm -f LFLAGS-o OBJ$(SRC:.c.o) $(BIN):$(OBJ) $(CC) $^ $(LFLAGS) $ echo 将$^链接为$ %.o:%.c $(CC) $(CFLAGS) $ echo 将$编译为$ .PHONY:clean clean: $(RM) $(BIN) $(OBJ)四、增量式编译这部分是关于Makefile的特点。make 只会重新编译比目标文件更新的依赖文件没改动的文件跳过不编译节省时间这就是增量编译。底层原理是每个文件都有修改时间戳make 执行时自动对比规则格式目标: 依赖如果依赖文件时间晚于目标文件 → 依赖被修改执行命令重新生成目标如果依赖比目标旧 → 文件无改动直接跳过这条规则不执行 gcc这一点其实在目前很多IDE上都有体现。