Makefile

Makefile 是一个用于自动化构建过程的文件,通常在 C/C++ 项目中使用,但也可以应用于其他编程语言。它定义了一系列规则来指定如何从源代码生成可执行文件或其他目标文件。

1 基本结构

一个简单的 Makefile 包含目标(target)、依赖项(prerequisites)和命令(commands)。其基本格式如下:

1
2
target: prerequisites
command

  • 目标:这是你希望构建的最终产物,比如一个可执行文件。
  • 依赖项:是生成目标所需要的文件或其它目标。
  • 命令:是一系列用来生成目标的 shell 命令。

1.1 基本示例1

1
funtion.c  hread.h  Makefile   tgdb.c  

假设我们有一个项目,包含三个源文件 funtion.c, hread.h, tgdb.c,以及一个 Makefile。
我们可以用以下 Makefile 来编译这个项目:

1
2
tgdb.exe: tgdb.c function.c
gcc -o tgdb.exe tgdb.c function.c

其等同于:gcc -o tgdb.exe tgdb.c function.c

1.2 基本示例2

利用makefile流程编译:
gcc的编译流程参见 C0.1gcc的编译过程
注意makefile的执行流程是从后向前执行的,故先写的目标文件再写依赖文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

tgdb.exe:tgdb.o funtion.o hread.h
gcc tgdb.o funtion.o hread.h -o tgdb.exe

tgdb.o:tgdb.s
gcc -c tgdb.s
tgdb.s:tgdb.i
gcc -S tgdb.i
tgdb.i:tgdb.c
gcc -E tgdb.c > tgdb.i

funtion.o:funtion.s
gcc -c funtion.s
funtion.s:funtion.i
gcc -S funtion.i
funtion.i:funtion.c
gcc -E funtion.c > funtion.i

1.3 基本示例3

文件代指,如$^表示所有的依赖文件,$@表示目标文件,$<表示第一个依赖文件。

1
2
3
4
5
tgdb.exe:tgdb.o funtion.o hread.h
gcc $^ -o $@;make clear

tgdb.o funtion.o:tgdb.c funtion.c
gcc -c $<

2 Makefile 编写指南

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 定义编译器
CC = gcc

# 定义编译选项
CFLAGS = -Wall -g

# 目标可执行文件
TARGET = myprogram

# 源文件
SRCS = main.c helper.c

# 对象文件
OBJS = $(SRCS:.c=.o)

# 默认目标
all: $(TARGET)

# 生成可执行文件
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^

# 为每个源文件生成对象文件
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@

# 清理生成的文件
clean:
rm -f $(OBJS) $(TARGET)

2.1 使用 Makefile

  1. 编写 Makefile:根据你的项目需求编写 Makefile。
  2. 保存 Makefile:将上述内容保存为名为 Makefile 的文件,没有扩展名。
  3. 运行 make:打开终端,切换到包含 Makefile 的目录,然后输入 make 并按回车键。这将根据 Makefile 中定义的规则自动编译你的程序。
  4. 清理:如果你想要删除所有生成的对象文件和可执行文件,可以运行 make clean

2.2 自动化特性

  • 依赖关系检查:Make 会检查目标文件和依赖文件的时间戳,只有当依赖文件比目标文件更新时才会重新编译。
  • 并行执行:可以通过 -j 参数指定同时执行的作业数,例如 make -j4 表示同时运行4个编译任务。

2.3 高级功能

  • 变量:可以在 Makefile 中定义变量,如上面例子中的 CC, CFLAGS 等。
  • 模式规则:如上面的 %.o: %.c 规则,可以匹配多个文件。
  • 条件语句:支持简单的条件逻辑,例如根据环境变量选择不同的编译选项。
  • 函数:提供一些内置函数来处理文件名、路径等。

2.4 伪文件

在执行make clean时,实际上make工具会把clean作为一个目标文件,并执行其命令。当clean文件存在时,makefile中的clean字段将不会执行。

1
2
clean:
rm -f *.o

为了避免这种情况,可以将clean文件名改为.PHONY,表示它是一个伪目标文件,不会被make工具自动生成。

1
2
3
4
.PHONY: clean

clean:
rm -f *.o

3 变量

3.1 自动变量

  • $@:代表目标文件名。
  • $^:代表所有的依赖文件。
  • $<:代表第一个依赖文件。
  • $?:代表比目标文件更新的所有依赖文件。

3.2 自定义变量

变量名只能包含字母、数字和下划线,且不能以数字开头。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CC = gcc
CFLAGS = -Wall -g

TARGET = myprogram

SRCS = main.c helper.c
OBJS = $(SRCS:.c=.o)

all: $(TARGET)

$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^

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

clean:
rm -f $(OBJS) $(TARGET)

3.2.1 变量的自动替换 $(.c=.o)

  • $(SRCS):代表所有源文件。
  • $(SRCS:.c=.o):代表所有源文件,将 .c 后缀替换为 .o
1
2
3
4
5
6
7
8
9
10
# $(wildcard *.c) 列出当前目录下的所有 .c 文件: hello.c main.c
# 用函数 patsubst 进行模式替换得到: hello.o main.o
OBJS = $(patsubst %.c,%.o,$(wildcard *.c))
TARGET = world.out

$(TARGET): $(OBJS)
cc -o $(TARGET) $(OBJS)

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

3.3 环境变量

可以在命令行中设置环境变量,也可以在 Makefile 中引用环境变量。

1
CC = $(shell which gcc)

4 使用模式规则

模式规则可以匹配多个文件,并为每个匹配的文件生成目标文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

souc = tgdb.c funtion.c
soucse = $(souc: .c=.o)


tgdb.exe:$(soucse)
gcc $^ -o $@

%.o :%.c #模式规则
gcc -c $<


clear:
rm -f *.i
rm -f *.s
rm -f *.o