1. 第一版Makefile
对于我们的示例代码,不通过Makefile编译其实也很简单:
gcc main.c fun1.c fun2.c -o app
我们知道,Makefile其实就是按规则一条条的执行。所以,我们完全可以把上面那条[命令]写成Makefile的一个规则。我们的目标是app,按此写法依赖是[main.c fun1.c fun2.c],则最终的Makefile如下:
app: main.c fun1.c fun2.c
gcc main.c fun1.c fun2.c -o app
但这个版本的Makefile有两个很重要的不足:
- 对于简单代码还好,而对于大型项目,具有成千上万代码来说,仅用一行规则是完全不够的,即使够的话也需要写很长的一条规则;
- 任何文件只要稍微做了修改就需要整个项目完整的重要编译。
基于此,我们在第一版的基础上[优化]出第二版。
2. 第二版Makefile
在第二版Makefile中,为了避免改动任何代码就需要重新编译整个项目的问题,我们将[主规则]的各个依赖替换成各自的中间文件,即main.c --> main.o,fun1.c --> fun1.o,fun2.c --> fun2.o,再对每个[中间文件]的生成各自写条规则比如对于main.o,规则为:
main.o: main.c
• gcc -c main.c -o main.o
这样做的好处是,当有一个文件发生改动时,只需重新[编译]此文件即可,而无需重新编译整个项目。完整Makefile如下:
app: main.o fun1.o fun2.o
gcc main.o fun1.o fun2.o -o app
main.o: main.c
gcc -c main.c -o main.o
fun1.o: fun1.c
gcc -c fun1.c -o fun1.o
fun2.o: fun2.c
gcc -c fun2.c -o fun2.o
第二版Makefile同样具有一些缺陷:
- 里面存在一些重复的内容,可以考虑用变量代替;
- 后面三条规则非常类似,可以考虑用一条[模式规则]代替。
基于此,我们在第二版的基础上优化出第三版。
3. 第三版Makefile
在第三版Makefile中,我们使用变量及模式规则使Makefile更加简洁。使用的三个变量如下:
obj = main.o fun1.o fun2.o
target = app
CC = gcc
使用的模式规则为:
%.o: %.c
• $(CC) -c $< -o $@
这条模式规则表示:所有的.o文件都由对应的.c文件生成。在规则里,我们又看到了两个[自动变量]: < 和 <和 <和@。其实自动变量有很多,常用的有三个:
$<:第一个依赖文件;
$@:目标;
$^:所有不重复的依赖文件,以空格分开
obj = main.o fun1.o fun2.o
target = app
CC = gcc
$(target): $(obj)
$(CC) $(obj) -o $(target)
%.o: %.c
$(CC) -c $< -o $@
第三版Makefile依然存在一些缺陷:
- obj对应的文件需要一个个输入,工作量大;
\2. 文件数目比较少时还好,文件数目一旦很多的话,obj将很长;
\3. 而且每增加/删除一个文件,都需要修改Makefile。
基于此,我们在第二版的基础上优化出第四版。
4. 第四版Makefile
在第四版Makefile中,我们隆重推出了两个函数:wildcard和patsubst。
wildcard:
扩展通配符,搜索指定文件。在此我们使用src = $(wildcard ./*.c),代表在当前目录下搜索所有的.c文件,并赋值给src。函数执行结束后,src的值为:main.c fun1.c fun2.c。
patsubst:
替换通配符,按指定规则做替换。在此我们使用
obj = $(patsubst %.c, %.o, $(src))
代表将src里的每个文件都由.c替换成.o。函数执行结束后,obj的值为main.o fun1.o fun2.o,其实跟第三版Makefile的obj值一模一样,只不过在这里它更智能一些,也更灵活。
除了使用patsubst函数外,我们也可以使用模式规则达到同样的效果,比如:
obj = $(src:%.c=%.o)
也是代表将src里的每个文件都由.c替换成.o。
几乎每个Makefile里都会有一个伪目标clean,这样我们通过执行make clean命令就是将中间文件如.o文件及目标文件全部删除,留下干净的空间。一般是如下写法:
.PHONY: clean
clean:
• rm -rf $(obj) $(target)
.PHONY代表声明clean是一个伪目标,这样每次执行make clean时,下面的规则都会被执行。
src = $(wildcard ./*.c)
obj = $(patsubst %.c, %.o, $(src))
#obj = $(src:%.c=%.o)
target = app
CC = gcc
$(target): $(obj)
$(CC) $(obj) -o $(target)
%.o: %.c
$(CC) -c $< -o $@
.PHONY: clean
clean:
rm -rf $(obj) $(target)
5. 总结
Makefile其实也并不难,但关键的是一定要自己动手写,这样才会更加加深理解,否则也容易造成眼高手低。如果实在不知道从何下手,可以尝试按上面的教程,一步步写下来,也只需要写四个版本而已,写完了相信就有了初步的理解。