本节主要介绍makefile相关的基本概念。
Makefile基础
Makefile由若干条规则(Rule)构成,每一条规则指出一个目标文件(Target),若干依赖文件(prerequisites),以及生成目标文件的命令。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#makefile的核心逻辑是:默认把目标名当成“要生成的文件名”
#.PHONY: all clean 是告诉 make:all 和 clean 是“伪目标”,不是要生成的真实文件名。
#这样,无论是否存在名为 all 或 clean 的文件,make 都会执行对应的规则命令,而不会因为文件存在而跳过执行。这对于像 clean 这样的目标特别有用,因为它通常用于删除生成的文件,而不是生成一个新的文件。
all: clean x.txt cd
#makefile默认执行第一条规则
#这里clean相当于一个目标名,它没有依赖文件
#然而,在执行clean时,我们并没有创建一个名为clean的文件,所以,因为目标文件clean不存在,每次运行make clean,都会执行这个规则的命令。
clean:
rm -f x.txt m.txt
#目标文件:依赖文件1 依赖文件2
#tab开头的是命令,用来生成目标文件,这里使用cat命令将m.txt和c.txt的内容合并到x.txt中
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
#执行多条命令
cd:
pwd;\
cd ..;\
pwd;makefile默认执行第一条规则,上述代码中make == make all
makefile的核心逻辑是:默认把目标名当成“要生成的文件名”
所以当执行
make clean时,它会先看当前目录里有没有一个叫clean的文件:有
clean文件,且它看起来不需要更新(常见是clean没依赖)→make认为目标已完成,不跑命令。没有
clean文件 → 才会执行clean:下的rm -f ...。
加上
.PHONY: clean后,语义变成:clean不是文件目标,只是一个动作标签;- 无论目录里有没有同名文件
clean,都执行clean:下面命令。
- 无论目录里有没有同名文件
编译C程序
make最初就是为了编译C程序而设计的,为了免去重复创建编译.o文件的规则,make内置了隐式规则(Implicit Rule),即遇到一个xyz.o时,如果没有找到对应的规则,就自动应用一个隐式规则:1
2xyz.o: xyz.c
cc -c -o xyz.o xyz.c在
Makefile中,经常可以看到$@、$<这样的变量,这种变量称为自动变量(Automatic Variable),它们在一个规则中自动指向某个值。例如,$@表示目标文件,$^表示所有依赖文件,因此,我们可以这么写:1
2world.out: hello.o main.o
cc -o $@ $^C程序的基础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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64SRC_DIR = ./src
BUILD_DIR = ./build
TARGET = $(BUILD_DIR)/world.out
CC = cc
CFLAGS = -Wall #编译参数,-Wall 打开常见警告。
# ./src/*.c
SRCS = $(shell find $(SRC_DIR) -name '*.c')
# ./src/*.c => ./build/*.o
OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS))
# ./src/*.c => ./build/*.d
DEPS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.d,$(SRCS))
# 默认目标:
all: $(TARGET)
# build/xyz.d 的规则由 src/xyz.c 生成:
$(BUILD_DIR)/%.d: $(SRC_DIR)/%.c
@mkdir -p $(dir $@); \
rm -f $@; \
$(CC) -MM $< >$@.tmp; \
sed 's,\($*\)\.o[ :]*,$(BUILD_DIR)/\1.o $@ : ,g' < $@.tmp > $@; \
rm -f $@.tmp
# build/xyz.o 的规则由 src/xyz.c 生成:
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c -o $@ $<
# 链接:
$(TARGET): $(OBJS)
@echo "building $@..."
@mkdir -p $(dir $@)
$(CC) -o $(TARGET) $(OBJS)
# 清理 build 目录:
clean:
@echo "clean..."
rm -rf $(BUILD_DIR)
# 引入所有 .d 文件:
-include $(DEPS) #包含依赖文件;前缀 - 表示即使 .d 不存在也不报错
# #生成可执行文件world.out,依赖于hello.o和main.o
# world.out: hello.o main.o
# gcc -o world.out hello.o main.o
# #注释掉之后makefile会隐藏式生成默认规则,编译hello.c生成hello.o,编译main.c生成main.o
# #对于C程序来说,使用隐式规则有一个潜在问题,那就是无法跟踪.h文件的修改。
# #如果我们修改了hello.h的定义,由于隐式规则main.o: main.c并不会跟踪hello.h的修改
# #导致main.c不会被重新编译
# # #编译hello.c生成hello.o
# # hello.o: hello.c
# # gcc -c hello.c
# # #编译main.c生成main.o,依赖于hello.h
# # main.o: main.c hello.h
# # gcc -c main.c
# clean:
# rm -f *.o world.out