SCENE / 001
返回札记NOTE / 学习笔记

Makefile入门:依赖关系、伪目标与增量构建

Makefile的依赖关系和依赖方法、伪目标,以及make靠什么判断要不要重新编译

#Linux#学习笔记#构建工具#编译
本文目录
  1. 手写gcc的麻烦
  2. 依赖关系与依赖方法
  3. make默认执行第一个目标
  4. 把编译过程拆开
  5. clean与伪目标
  6. make怎么判断要不要重新编译
  7. 变量与自动变量
  8. 来源
阅读记录0%
0%

这篇学Makefile

手写gcc的麻烦

单个C文件的编译很简单:

gcc hello.c -o hello

但手敲有个坑:-o后面跟的是输出文件,要是顺序写反了,gcc不会拦你,而是直接把源文件当成输出覆盖掉:

gcc -o hello.c hello

执行完,hello.c里的源码就没了

文件一多,编译顺序、链接参数、哪些文件要重编,靠脑子记迟早出错。Makefile就是把这些写进一个文件,以后只敲一个make

依赖关系与依赖方法

Makefile里的规则只有两类内容:依赖关系和依赖方法

mycode: mycode.c
	gcc -o mycode mycode.c

Makefile的第一行是依赖关系,第二行是依赖方法

第一行是依赖关系,表示mycode由mycode.c生成;第二行是依赖方法,也就是生成时执行的命令

命令那一行前面必须是Tab,写成空格会报missing separator,编辑器自动把Tab转空格的话要注意

之后在目录下执行make,它会自动找到Makefile或makefile并完成编译:

make
gcc -o mycode mycode.c

make默认执行第一个目标

make不带参数时,只执行文件里的第一个目标

所以顺序写错会出事:

.PHONY:clean
clean:
	rm -f mycode

mycode:mycode.c
	gcc -o mycode mycode.c

这时直接执行make,跑的是删除而不是编译:

make
rm -f mycode

clean写在前面时,直接运行make执行的是删除

这时要编译只能写明make mycode。一般把最终产物放最前面,clean这种放最后

把编译过程拆开

上面是一步完成编译。gcc从.c到可执行文件实际经过四步,Makefile可以把四步都写成规则:

hello: hello.o
	gcc hello.o -o hello

hello.o: hello.s
	gcc -c hello.s -o hello.o

hello.s: hello.i
	gcc -S hello.i -o hello.s

hello.i: hello.c
	gcc -E hello.c -o hello.i

gcc从hello.c到hello的四步

这样写好之后,make会从最终目标往回找,缺哪个中间文件就先生成哪个:hello依赖hello.o,hello.o依赖hello.s,一直找到hello.c,再按顺序执行每条命令

clean与伪目标

清理编译产物一般单独写一个目标:

.PHONY:clean
clean:
	rm -f mycode

clean没有依赖文件,规则里只有一行命令。.PHONY:clean把它声明为伪目标:伪目标不对应真实文件,声明之后,即使目录下正好有个叫clean的文件,make clean也会照常执行

.PHONY后面写的是目标名,不是依赖文件,很容易写混

普通目标则不同,依赖没变过时make会直接跳过:

make
make: 'mycode' is up to date.

make怎么判断要不要重新编译

依据是文件的修改时间。用stat能看到一个文件的三个时间:

stat mycode
字段 含义
Access 最近一次被读取的时间
Modify 最近一次内容被修改的时间
Change 最近一次元数据(权限、属主等)被修改的时间

make比较的是Modify:目标文件的Modify比依赖文件新,说明产物是最新的,不需要重建

make按修改时间决定编译还是跳过

为什么不用另外两个:Access现在默认按relatime策略更新,只有它比mtime或ctime旧、或者距上次更新超过24小时才会刷新(每读一次就写一次盘太费了),所以不准;Change记的是权限、属主这类元数据的变化,跟源码改没改不是一回事

想强制重新编译,可以手动刷新源文件的时间:

touch mycode.c
make

touch把文件时间改成现在,make一看源文件比目标新,就会重新编译

变量与自动变量

规则一多,编译器、选项、目标名到处都是重复的,可以用变量:

CC = gcc
CFLAGS = -Wall -O2
TARGET = mycode

$(TARGET): $(TARGET).c
	$(CC) $(CFLAGS) -o $@ $<

.PHONY: clean
clean:
	rm -f $(TARGET) *.o

make把变量展开后执行的命令

变量用$(名字)引用,要换编译器或者改选项,只改开头一处就行

$@、$<这类是自动变量,执行规则时由make自动填值:

写法 含义
$@ 当前规则的目标名
$< 第一个依赖文件
$^ 全部依赖文件

所以$(CC) $(CFLAGS) -o $@ $<展开就是gcc -Wall -O2 -o mycode mycode.c,换个目标名规则也不用改

来源

本文为个人学习笔记,根据lvy的《【Linux必备工具】自动化构建工具makefile的使用详解》提炼并重新组织,补充了变量与自动变量部分,保留原文链接供进一步阅读

READING / FOCUSOFF

留言

评论区暂未开放;可先通过 RSS 或「关于」页联系。

站内搜索

输入关键词开始搜索