纸上得来终觉浅,绝知此事要躬行。
- 陆游 《冬夜读书示子聿》

Makefile编写指南

2025-08-30 23:16 · 9 minutes read

1. Makefile 基础概念

  1. Makefile 用于自动化编译、构建、测试、打包等任务。

  2. make 工具会根据 Makefile 定义的规则执行任务。

  3. 常见格式:

    1target: prerequisites
    2    command
    
    • target:目标,例如生成的文件或任务名。
    • prerequisites:依赖文件或其他目标。
    • command:执行的命令,必须以 Tab 键 开头。

2. 简单示例

假设有以下源文件:main.cfunctions.c

示例 Makefile

 1# 变量定义
 2CC = gcc
 3CFLAGS = -Wall -g
 4TARGET = program
 5OBJECTS = main.o functions.o
 6
 7# 默认目标
 8all: $(TARGET)
 9
10# 编译目标文件
11$(TARGET): $(OBJECTS)
12	$(CC) $(CFLAGS) -o $(TARGET) $(OBJECTS)
13
14# 编译源文件为中间目标 (object 文件)
15main.o: main.c
16	$(CC) $(CFLAGS) -c main.c
17
18functions.o: functions.c
19	$(CC) $(CFLAGS) -c functions.c
20
21# 清理目标
22clean:
23	rm -f $(TARGET) $(OBJECTS)
24
25# 伪目标:不生成文件
26.PHONY: all clean

执行流程

  1. 执行 make 命令
    • make 会查找默认目标 all 并执行相关依赖规则。
  2. 编译生成目标文件
    • main.cfunctions.c 会被编译为 main.ofunctions.o
  3. 链接生成最终可执行文件
    • 链接中间文件,生成 program
  4. 执行 make clean
    • 清理中间文件和目标文件。

3. Makefile 语法详解

3.1 变量

1CC = gcc
2CFLAGS = -Wall -g

3.2 自动变量

1$(TARGET): $(OBJECTS)
2	$(CC) $(CFLAGS) -o $@ $^

3.3 伪目标 (.PHONY)

1.PHONY: clean
2clean:
3	rm -f $(TARGET) $(OBJECTS)

3.4 模式规则

1%.o: %.c
2	$(CC) $(CFLAGS) -c $< -o $@

表示将所有 .c 文件编译成对应的 .o 文件。


4. Makefile 进阶技巧

4.1 自动搜索依赖

使用 -MMD 选项自动生成依赖文件。

1CFLAGS = -Wall -g -MMD
2-include $(OBJECTS:.o=.d)

4.2 多目标编译

1all: program1 program2
2
3program1: program1.c
4	$(CC) -o program1 program1.c
5
6program2: program2.c
7	$(CC) -o program2 program2.c

4.3 定义函数

使用 define 定义复杂操作。

1define compile
2	$(CC) $(CFLAGS) -c $1.c -o $1.o
3endef
4
5main.o:
6	$(call compile, main)

5. 常用命令总结

命令 说明
make 执行默认目标(第一个规则)
make target 执行指定目标
make clean 执行clean规则
make -jN 并行执行任务,N 表示线程数
make -n 只打印命令,不执行

6. 示例执行过程

1# 编译项目
2make
3
4# 执行清理
5make clean
6
7# 使用多线程编译
8make -j4

7. 基本示例

7.1 编译Makefile文件同级目录下的 .c 文件

 1# 变量定义
 2CC = gcc
 3CFLAGS = -Wall -g
 4TARGET = program
 5
 6# 自动查找当前目录下的所有 .c 文件
 7SRCS := $(wildcard *.c)
 8OBJS := $(SRCS:.c=.o)
 9
10# 默认目标
11all: $(TARGET)
12
13# 链接目标文件
14$(TARGET): $(OBJS)
15	$(CC) $(CFLAGS) -o $@ $(OBJS)
16
17# 编译 .c 文件为 .o 文件(模式规则)
18%.o: %.c
19	$(CC) $(CFLAGS) -c $< -o $@
20
21# 清理目标
22clean:
23	rm -f $(TARGET) $(OBJS)
24
25.PHONY: all clean

7.2 编译Makefile文件所在目录及子目录的c文件

 1# 变量定义
 2CC = gcc
 3CFLAGS = -Wall -g
 4TARGET = program
 5
 6# 查找当前目录及所有子目录中的 .c 文件
 7SRCS := $(shell find . -name "*.c")
 8OBJS := $(SRCS:.c=.o)
 9
10# 默认目标
11all: $(TARGET)
12
13# 链接目标文件
14$(TARGET): $(OBJS)
15	$(CC) $(CFLAGS) -o $@ $(OBJS)
16
17# 编译 .c 文件为 .o 文件(模式规则)
18%.o: %.c
19	$(CC) $(CFLAGS) -c $< -o $@
20
21# 清理目标
22clean:
23	rm -f $(TARGET) $(OBJS)
24
25.PHONY: all clean

7.3 多个Makefile编译

  1. 假设项目目录结构如下:
 1project/
 2├── Makefile           # 顶层 Makefile
 3├── main.c
 4├── src/
 5   ├── Makefile       # src 目录下的 Makefile
 6   ├── func1.c
 7   └── func2.c
 8└── lib/
 9    ├── Makefile       # lib 目录下的 Makefile
10    ├── utils.c
11    └── helper.c
  1. 顶层 Makefile

顶层 Makefile 负责调用子目录中的 Makefile 并编译整个项目。

 1# 变量定义
 2SUBDIRS = src lib
 3CC = gcc
 4CFLAGS = -Wall -g
 5TARGET = program
 6
 7# 默认目标
 8all: subdirs $(TARGET)
 9
10# 编译主程序
11main.o: main.c
12	$(CC) $(CFLAGS) -c main.c -o main.o
13
14# 链接所有目标文件
15$(TARGET): main.o
16	@echo "Linking target: $(TARGET)"
17	$(CC) $(CFLAGS) -o $(TARGET) main.o src/*.o lib/*.o
18
19# 递归编译子目录
20subdirs:
21	@for dir in $(SUBDIRS); do \
22		echo "Building in $$dir..."; \
23		$(MAKE) -C $$dir; \
24	done
25
26# 清理目标
27clean:
28	@for dir in $(SUBDIRS); do \
29		echo "Cleaning in $$dir..."; \
30		$(MAKE) -C $$dir clean; \
31	done
32	rm -f $(TARGET) main.o
33
34.PHONY: all clean subdirs
  1. 子目录的 Makefile

s**rc/Makefile**

 1CC = gcc
 2CFLAGS = -Wall -g
 3OBJS = func1.o func2.o
 4
 5all: $(OBJS)%.o: %.c
 6	$(CC) $(CFLAGS) -c $< -o $@
 7
 8clean:
 9	rm -f $(OBJS)
10
11.PHONY: all clean

lib/Makefile

 1CC = gcc
 2CFLAGS = -Wall -g
 3OBJS = utils.o helper.o
 4
 5all: $(OBJS)%.o: %.c
 6	$(CC) $(CFLAGS) -c $< -o $@
 7
 8clean:
 9	rm -f $(OBJS)
10
11.PHONY: all clean
  1. 工作原理
  2. 顶层 Makefile
    • 定义了子目录 SUBDIRS = src lib
    • 使用 $(MAKE) -C 命令进入子目录并执行子目录的 Makefile
  3. 递归编译
    • $(MAKE) -C dir 会在 dir 目录中执行 make
    • 子目录的 Makefile 负责编译自己的 .c 文件并生成 .o 文件。
  4. 主程序链接
    • 在顶层 Makefile 中,链接主程序的 main.o 和子目录生成的所有 .o 文件,生成最终的可执行文件。
  5. 清理目标
    • make clean 会递归调用子目录的 Makefile 中的 clean 目标,删除所有中间文件。

end.


🌙