What & How & Why

MakeFile

Notes & usage


Installation

  • 只能在 linux 环境下运行
  • Windows 可以使用 msYS2

# ubuntu
sudo apt install build-essential
# check current version
make -v

Basics

Makefile 由若干规则(Rule)组成。每条规则包含:

  • 目标文件(Target
  • 依赖文件(Prerequisites
  • 生成目标文件的命令

格式为:

Target File: Prerequisites1, Prerequisites2....
    # 生成命令以 Tab 开头
    complie commands...
比如我们要基于 a.txtb.txt 生成文件 m.txt
m.txt: a.txt, b.txt
    cat a.txt, b.txt > m.txt

多层级 makefile

假设 m.txt 是中间件,用于上一级的 x.txt 生成,那么 makefile 可以写成:

# makefile 会默认执行第一条规则
# 查找依赖的时候会递归向下找寻对应规则
x.txt: m.txt c.txt
	cat m.txt c.txt > x.txt
m.txt: a.txt b.txt
	cat a.txt b.txt > m.txt
可以看到此时 m.txt 并未生成。这种情况下,make 会向下查找是否有可以支持 m.txt 生成的规则。

增量编译

make 生成的文件会基于依赖文件的最后修改时间来决定是否重新生成。如果当前生成文件使用的是最新的依赖文件,那么 make 会自动跳过生成,并提供对应输入:

# 增量编译
$ make
make: 'x.txt' is up to date.

伪目标

从上面的例子中可以看到,make 只有在两种情况下才有新文件的生成动作:

  • 目标文件不存在
  • 目标文件过期

假设我们希望的不是生成文件,而是批量执行一系列命令,那么上述要求就会导致问题。比如我们想批量删除之前例子中的生成文件。一般思路是写一个文件 clean,并把命令都写进去:

clean:
    rm-f m.txt
    rm-f x.txt
当我们运行的时候,由于 clean 作为目标,本身没有任何依赖文件,因此 makefile 会认为该目标文件是 up to date 的状态;所以不会有任何命令的执行。

这种情况的解决方式是,不要以任何形式的文件作为命令的载体,而是以 .PHONY 关键词开头,将 clean 对应的命令全写到 makefile 里面去。.PHONY 关键字会让 makefile 将目标 clean 视为伪目标PHONY Target)。这种情况下的 clean 会被视作一个动作;不管有没有这个名字的文件,都会执行这个动作
x.txt: m.txt c.txt
	cat m.txt c.txt >x.txt
m.txt: a.txt b.txt
	cat a.txt b.txt >m.txt

# define clean "action"
.PHONY: clean
clean:
	rm -f m.txt
	rm -f x.txt
运行时,需要单独的使用该目标:
make clean
这样的伪目标还有很多,比如:
make build   # 编译程序
make run     # 运行程序
make clean   # 删除中间产物
make         # 默认执行 all → build