风之栖息地

Makefile学习笔记

字数统计: 1k阅读时长: 4 min
2023/06/17 Share

前言

这篇学习笔记是根据陈浩写的《跟我一起写Makefile》系列文章总结出的内容,这个系列文章对Makefile的讲解深入浅出,对于新人十分友好。推荐大家在有时间的情况下,通读全文。

在写文章到一半的时候,惊闻陈浩老师离世,中文互联网永远失去了一位充满理想的技术人,写这篇文章也是感谢陈浩老师为我们贡献了如此之多的好博文,您永远都是我的榜样。希望中国的程序员不要那么的苦逼,不是每天996,而是真正的在探索技术,利用技术来提升自己的生产力。

这是第一次感受到程序员身体健康的重要性,也希望广大程序员朋友积极锻炼身体,身体才是革命的本钱。

Makefile思维导图

一些有用的例子

将所有.c文件编译为目标foo

1
2
3
objects := $(patsubst %.c,%.o,$(wildcard *.c))
foo : $(objects)
cc -o foo $(objects)

其中$(wildcard *.c)找到所有的.c文件,通过$(patsubst %.c,%.o,$(wildcard *.c))将所有.c文件替换为.o文件当做object变量的值。最后的依赖规则中成为foo目标的依赖项,其中的.o会被自动推导为对应.c文件的编译生成内容。

生成多个目标文件

1
2
3
4
5
6
7
8
9
all : prog1 prog2 prog3
.PHONY : all

prog1 : prog1.o utils.o
cc -o prog1 prog1.o utils.o
prog2 : prog2.o
cc -o prog2 prog2.o
prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o

all依赖3个prog,这样就能全部执行对应的生成命令,最后得到3个目标文件。

清理不同类型文件

1
2
3
4
5
6
7
8
.PHONY : cleanall cleanobj cleandiff

cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff

定义了3个伪目标,这样能通过make cleanall清除所有目标,make cleanobj清除.o文件,make cleandiff清除.diff文件。

将多个存在生成规律的目标整合成一条规则

1
2
3
4
5
6
7
bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) > $@
# 上述规则等价于
bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput

$@表示目标的集合,会依次从中取出目标用于执行命令,之后又使用$(subst output,,$@)将output替换成空。

利用静态模式规则实现批量依赖规则

1
2
3
4
5
6
files = foo.elc bar.o lose.o

$(filter %.o,$(files)): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc,$(files)): %.elc: %.el
emacs -f batch-byte-compile $<

$(filter %.o,$(files))从files变量中筛选出.o文件,这些.o文件全部匹配上.c文件。对应的执行规则$<在模式匹配中会表示一组依赖文件集,和$@组合在一起刚好是对所有符合的.c文件生成对应.o文件。

自动更新Makefile中的依赖关系

1
2
3
4
5
6
7
%.d: %.c
@set -e; rm -f $@; \
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$

include $(sources:.c=.d)

将所有的.d文件依赖于.c文件,当我们的c文件更新时会自动更新.d依赖文件。首先会删除原有的依赖文件,之后则是生成新的依赖文件。$$$$ 意为一个随机编号,生成的文件名会拼接上这个随机符号,这里的名称可能是name.d.12345。第三行的sed是将其中的依赖关系加入xxx.d文件,第四行则是删除临时文件。如果我们要使用这个依赖关系,可以直接用include批量引入。

嵌套执行Makefile

1
2
3
4
5
6
7
8
subsystem:
cd subdir && $(MAKE)

subsystem:
cd subdir; $(MAKE)

subsystem:
$(MAKE) -C subdir

由一个主Makefile开始,去执行子目录中的Makefile,这样的结构能有效减少Makefile的复杂度。

使用MAKELEVEL对不同的调用层作区分

1
2
3
4
5
6
ifeq (0,${MAKELEVEL})
cur-dir := $(shell pwd)
whoami := $(shell whoami)
host-type := $(shell arch)
MAKE := ${MAKE} host-type=${host-type} whoami=${whoami}
endif

系统变量MAKELEVEL记录了当前的Makefile的调用层数,嵌套调用的时候这个变量数值会增加。

利用VPATH搜索路径指定依赖文件的搜索路径

1
override CFLAGS += $(patsubst %,-I%,$(subst :, ,$(VPATH)))

如果$(VPATH)的值为src:../headers,那么上面的语句将会返回-Isrc -I../headers

CATALOG
  1. 1. 前言
  2. 2. Makefile思维导图
  3. 3. 一些有用的例子
    1. 3.1. 将所有.c文件编译为目标foo
    2. 3.2. 生成多个目标文件
    3. 3.3. 清理不同类型文件
    4. 3.4. 将多个存在生成规律的目标整合成一条规则
    5. 3.5. 利用静态模式规则实现批量依赖规则
    6. 3.6. 自动更新Makefile中的依赖关系
    7. 3.7. 嵌套执行Makefile
    8. 3.8. 使用MAKELEVEL对不同的调用层作区分
    9. 3.9. 利用VPATH搜索路径指定依赖文件的搜索路径