Linux编程-Makefile

本文最后更新于:1 年前

伪目标

.PHONY : clean

PHONY 目标并非实际的文件名:只是在显式请求时执行命令的名字。有两种理由需要使用PHONY 目标:避免和同名文件冲突,改善性能。

1
2
3
4
5
.PHONY : clean
clean :
-rm edit $(objects)

#rm 命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。

默认语法

$@–目标文件,

$^–所有的依赖文件,

$<–第一个依赖文件。

:= 即时展开右边的变量,= 是在parse所有的makefile之后最后再展开变量值

?= 是如果没有被赋值过就赋予等号后面的值

+= 是添加等号后面的值,是否即时展开右边的变量,取决于变量是=复制还是:=赋值

静态模式

objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@

上面的例子中,指明了我们的目标从$objects中获取,“%.o”表明要所有以“.o”结尾的目标,也就是“foo.o bar.o”,也就是变量$object集合的模式,而依赖模式“%.c”则取模式“%.o”的“%”,也就是“foo bar”,并为其加下“.c”的后缀,于是,我们的依赖目标就是“foo.c bar.c”。而命令中的“$<”和“$@”则是自动化变量,“$<”表示所有的依赖目标集(也就是“foo.c bar.c”),“$@”表示目标集(也就是“foo.o bar.o”)。

于是,上面的规则展开后等价于下面的规则:

foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o

示例

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#################################################
####此部分为路径配置

#头文件路径
INC_DIRS := src/ \
src/CC \
src/CM \
src/common \
src/dqu_dfs_common \
src/DRV

#源文件相对路径,以MakeFile所在文件为根目录
SRC_DIRS := src/ \
src/CC \
src/CM \
src/common \
src/dqu_dfs_common \
src/DRV

#静态/动态库路径(CC工程没有自定义库)
#LIB_DIRS := dir1 \
# dir2 \
# dir3

#.o文件输出目录,以MakeFile所在文件为根目录
OUTPUT_DIR := build
#################################################

#设定生成的目标名称(可执行文件名称)
TARGET := app_bin

#编译选项配置
CFLAGS += -Wall -O -g
CLIBS += -lpthread -lrt -Lhome/zfr/cross_tool/aarch64-linux-gnu/bin

CC = arm-linux-gnueabihf-gcc
CXX = arm-linux-gnueabihf-g++
LD = $(CC)

#将INC_DIRS、SRC_DIRS、OUTPUT_DIR 转换为绝对路径
CUR_DIRS = $(shell pwd)
INC_DIRS := $(patsubst %,$(CUR_DIRS)/%,$(INC_DIRS))
SRC_DIRS := $(patsubst %,$(CUR_DIRS)/%,$(SRC_DIRS))
#LIB_DIRS := $(patsubst %,$(CUR_DIRS)/%,$(LIB_DIRS))
OUTPUT_DIR := $(patsubst %,$(CUR_DIRS)/%,$(OUTPUT_DIR))

#VPATH是Makefile的内置变量
#依赖项如果在根目录找不到的话就从VPATH目录中查找.
VPATH := $(SRC_DIRS)

#################################################

#CFLAGS用于C编译器的编译选项。
CFLAGS += $(patsubst %,-I%,$(INC_DIRS))
#LDFLAGS += $(patsubst %,-L%,$(LIB_DIRS))

#将SRC_DIRS内全部目录下的.c文件展开并返回到CFILES(包含绝对路径)
#SRC_DIRS在mk-rules.mk文件中由用户指定
CFILES := $(foreach dirs,$(SRC_DIRS),$(wildcard $(dirs)/*.c) )

#CFILES_NO_DIR包含了所有指定目录下的.c文件(去除掉了绝对路径)
CFILES_NO_DIR := $(notdir $(CFILES))

#COBJS包含了所有.c文件对应的.o文件
COBJS := $(patsubst %.c,%.o,$(CFILES_NO_DIR))
COBJS := $(patsubst %,$(OUTPUT_DIR)/%,$(COBJS))

#声明伪目标
.PHONY: all clean

#声明可忽略错误的目标。
#在命令行执行 make all ,如果没有这一行,当不存在*.o文件或者TARGET文件的时候
#会导致目标 clean 会出错导致make停止运行。
.IGNORE: clean

all: $(OUTPUT_DIR) $(TARGET)

#TARGET生成依赖于COBJS中的所有.o
$(TARGET) : $(COBJS)
$(CC) -o $@ $^ $(CLIBS)
# $(CC) -o $@ $^ $(CLIBS) $(LDFLAGS)

#COBJS内所有.o文件的生成依赖于对应的.c.
#以下是MakeFile的一种循环目标匹配规则:
#每次从COBJS中拿出一项进行 $(OUTPUT_DIR)/%.o:%.c 这样的字符串匹配。
#$(OUTPUT_DIR)/%.o为目标,%.c为依赖
$(COBJS) : $(OUTPUT_DIR)/%.o:%.c
$(CC) -o $@ -c $< $(CFLAGS)

$(OUTPUT_DIR):
mkdir -p $(OUTPUT_DIR)

#4 clean执行流程
clean:
rm $(OUTPUT_DIR)/*.o
rm $(TARGET)