Makefile

总览

定义:Makefile是一个自动化编译工具,用于定义文件依赖关系与构建规则

Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释

显示规则

显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出:要生成的目标,目标的依赖文件,生成的命令。其格式如下:

1
2
目标: 依赖文件
命令 # 必须用Tab缩进
  • make 会检查目标文件的时间戳,若依赖文件比目标新,则重新执行命令
  • 若目标文件已存在且依赖无变化,跳过构建(增量编译)

1.伪目标:用于执行非文件生成的操作(如清理、安装等),需通过 .PHONY 显式声明

1
2
3
4
5
.PHONY: clean install
clean:
rm -f *.o app
install:
cp app /usr/local/bin/

2.默认目标:一个Makefile中可以定义多个目标,当我们使用make命令但不指定具体的目标时,默认生成第一个目标

3.中间文件目标:临时生成的文件(如 .o 文件),可通过 .INTERMEDIATE 声明,make会在构建完成后自动删除中间文件

1
2
3
.INTERMEDIATE: temp.o
app: temp.o
gcc temp.o -o app

4.模式目标:使用通配符(%)定义通用规则,简化重复操作

1
2
%.o: %.c        # 所有 .o 文件依赖同名 .c 文件
gcc -c $< -o $@

隐晦规则

隐晦规则是 Make 内置的一些通用构建规则,用于自动推导如何从一种类型的文件生成另一种类型的文件(例如从 .c 生成 .o)。这些规则无需显式写出,Make 会根据文件扩展名自动匹配并应用它们。隐晦规则简化了 Makefile 的编写,尤其在处理常见编译任务时

常见隐晦规则(makefile已经内置,不需要再手动写出来了):

1
2
3
4
5
6
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
%: %.o
$(CC) $(LDFLAGS) $^ -o $@
  • 其他隐晦规则可用make -p打印

变量

变量的定义:

1
VAR_NAME = value

变量的使用:

1
$(VAR_NAME)  # 或 ${VAR_NAME}

常见用途:

  • 存储编译器名称(CC = gcc
  • 存储编译选项(CFLAGS = -Wall -O2
  • 存储文件列表(SRC = main.c utils.c

预定义变量

Makfile中有许多预定义变量,主要用于隐晦规则

变量 用途 默认值
CC C 编译器 cc
CFLAGS C 编译选项
CXX C++ 编译器 g++
CXXFLAGS C++ 编译选项
LDFLAGS 链接选项

自动化变量

用于在规则中动态引用目标或依赖文件

变量 含义 示例(规则中的用法)
$@ 当前目标文件名 gcc -c $< -o $@
$< 第一个依赖文件名 gcc -c $< -o $@
$^ 所有依赖文件(去重) gcc $^ -o $@
$? 比目标新的依赖文件 用于增量编译测试

赋值

1.延迟赋值(=)变量值在使用时才展开(递归展开)

1
2
3
4
5
6
A = 1
B = $(A) # B 的值会随 A 的变化而改变
A = 2

all:
@echo $(B) # 输出 2(而非 1)

2.立即赋值(:=)变量值在定义时立即展开(静态展开)

1
2
3
4
5
6
A := 1
B := $(A) # B 的值固定为 A 的当前值(1)
A := 2

all:
@echo $(B) # 输出 1

3.条件赋值(?=)仅当变量未定义时才赋值

1
CFLAGS ?= -Wall  # 如果 CFLAGS 未定义,则赋值为 -Wall

4.追加赋值(+=)向变量追加内容(自动添加空格分隔)

1
2
CFLAGS := -Wall
CFLAGS += -O2 # CFLAGS 变为 "-Wall -O2"

5.命令行赋值:在使用make命令行工具的时候,可以进行赋值,此时的赋值为覆盖赋值,优先级高于在makefile里面的赋值

6.环境变量:在Makefile里面,可以使用系统的环境变量

文件指示

一个Makfile中可以通过如下方式引用其他的Makefile

1
include a.mk b.make

make的工作方式

make 是 linux 下的一个程序软件,Makefile 相当于针对 make 程序的配置文件,当我们执行 make 命令时,make 将会在当前目录寻找 Makefile 文件,然后根据 Makefile 的配置对源文件进行编译。它的工作流程如下:

1、读入当前目录的顶层Makefile

2、读入被include的其它Makefile

3、初始化文件中的变量

4、推导隐晦规则,并分析所有规则

5、为所有的目标文件创建依赖关系链

6、根据依赖关系,决定哪些目标要重新生成

7、执行生成命令