风之栖息地

Makefile学习笔记

字数统计: 1k阅读时长: 4 min
2023/06/17 277 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. 一些有用的例子