Makefile是什么?
gcc hello.c -o hello
gcc aa.c bb.c cc.c dd.c ...
make工具和Makefile
make和Makefile是什么关系?
make工具:找出修改过的文件,根据依赖关系,找出受影响的相关文件,最后按照规则单独编译这些文件。
Makefile文件:记录依赖关系和编译规则。
必须要学精Makefile吗?
怎么学习Makefile?
Makefile的本质:无论多么复杂的语法,都是为了更好地解决项目文件之间的依赖关系。
Makefile三要素
Makefile三要素是什么?
目标、依赖、命令
怎么描述三要素的关系?
目标:依赖的文件或者是其他目标 ( 记得加TAB字符)
<tab>命令1
<tab>命令2
<tab>...
实验演示
.PHONY:可以指定伪目标
gec@ubuntu:~/makefile/part_1$ sudo vi Makefile
gec@ubuntu:~/makefile/part_1$ sudo make
echo "targetb"
targetb
echo "targetc"
targetc
echo "targeta"
targeta
gec@ubuntu:~/makefile/part_1$ cat Makefile
#Makefile格式
#目标:依赖的文件或其它目标
#Tab 命令1
#Tab 命令2
#第一个目标,是最终目标及make的默认目标
#目标a,依赖于目标targetc和targetb
#目标要执行的shell命令 ls -lh,列出目录下的内容
targeta: targetb targetc
echo "targeta"
#目标b,无依赖
#目标要执行的shell命令,使用touch创建test.txt文件
targetb:
echo "targetb"
#目标c,无依赖
#目标要执行的shell命令,pwd显示当前路径
targetc:
echo "targetc"
#目标d,无依赖
#由于abc目标都不依赖于目标d,所以直接make时目标d不会被执行
#可以使用make targetd命令执行
# targetd:
# rm -f test.txt
gec@ubuntu:~/makefile/part_1$
上图中包含的原理说明如下:
make命令:
在终端上执行make命令时,make会在当前目录下搜索名为“Makefile”或“makefile”的文件,然后 根据该文件的规则解析执行。如果要指定其它文件作为输入规则,可以通过“-f”参数指定输 入文件,如“make -f 文件名”。
此处make命令读取我们的Makefile文件后,发现targeta是Makefile的第一个目标,它会被当成默认目标执行。
又由于targeta依赖于targetc和targetb目标,所以在执行targeta自身的命令之前,会先去完成targetc和targetb。
targetc的命令为pwd,显示了当前的路径。
targetb的命令为touch test.txt ,创建了test.txt文件。
最后执行targeta自身的命令ls -lh ,列出当前目录的内容,可看到多了一个test.txt文件。
.PHONY
是一个在 Makefile 中使用的特殊目标标签(伪目标),用于指示某个目标不对应实际的文件。
通常,Makefile 中的目标都对应着需要生成的文件,而这些目标称为“真实目标”。但有时候,我们需要定义一些在执行 make 命令时需要执行的操作,而这些操作不产生对应的文件。这时就可以使用 .PHONY
来定义这样的目标。
.PHONY
的作用是告诉 make 工具,无论是否存在与之同名的文件,它所标记的目标都应该被认定为需要执行的操作。这样,在执行该目标时,make 将会按照你在 Makefile 中定义的命令来执行相应的操作,而不会检查是否有与之同名的文件存在。
使用 .PHONY
的一个常见场景是在 Makefile 中定义一些常用的操作,比如 clean
来清理临时文件,或者 all
来构建所有的目标。
示例:
.PHONY: clean clean: rm -f *.o
在上述示例中,.PHONY: clean
表示 clean
目标是一个伪目标,不对应任何文件。当执行 make clean
命令时,将会执行 rm -f *.o
命令,删除所有 .o
结尾的临时文件。
总结来说,.PHONY
是用来定义伪目标的,它告诉 make 工具,该目标不对应任何文件,而是需要执行一些特定的操作
在Makefile的实际应用中,通常会把编译和最终的链接过程分开
也就是说,我们的hello_main目标文件本质上并不是依赖hello_main.c和hello_func.c文件,而是依 赖于hello_main.o和hello_func.o,把这两个文件链接起来就能得到我们最终想要的hello_main目 标文件。另外,由于make有一条默认规则,当找不到xxx. o文件时,会查找目录下的同名xxx.c文件进行编译。根据这样 的规则,我们可把Makefile改修改如下。
#Makefile格式
#目标文件:依赖的文件
#Tab 命令1
#Tab 命令2
hello_main: hello_main.o hello_func.o
gcc -o hello_main hello_main.o hello_func.o
#以下是make的默认规则,下面两行可以不写
#hello_main.o: hello_main.c
# gcc -c hello_main.c
#以下是make的默认规则,下面两行可以不写
#hello_func.o: hello_func.c
# gcc -c hello_func.c
以上代码的第5~6行把依赖文件由C文件改成了.o文件,gcc编译命令也做 了相应的修改。第8~13行分别是hello_main.o文件和hello_func.o文件的依赖和 编译命令,不过由于C编译成同名的.o文件是make的默认规则,所以这部分内容通常不会写上去。
Makefile的变量、模式匹配
变量
系统变量
自定义变量
=,延迟赋值
:=, 立即赋值
?=,空赋值(像const)
只有当这个变量为空时赋值才有效
+=,追加赋值
当我们用追加赋值给某个变量赋值时,不会覆盖该变量的值,而是在该变量原来的值后面加上新赋的值
自动化变量
$<:第一个依赖文件
$^:全部的依赖文件
$@:目标
优化
模式匹配
%:匹配任意多个非空字符
shell:*通配符
默认规则
.o文件默认使用当前目录下对应.c文件来进行编译
Makefile条件分支
条件分支
如果相等 ifeq (var1,var2) ... else ... endif
ifneq (var1,var2) ... else ... endif
Makefile的常用函数
Makefie官方手册:
GNU Make Manual - GNU Project - Free Software Foundation
patsubst:
文本匹配成新的模式
notdir:
- $(notdir <names...>)
-
名称:取文件函数——notdir。
-
功能:从文件名序列
<names>
中取出非目录部分。非目录部分是指最後一个反斜杠(/
)之后的部分。 -
返回:返回文件名序列
<names>
的非目录部分。 -
示例:
$(notdir src/foo.c hacks)
返回值是foo.c hacks
。
wildcard:
示例:
(wildcard *.c)
返回值为当前目录下所有.c 源文件列表。
foreach:
Makefile解决头文件依赖
1、写一个头文件,并把头文件添加到编译器的头文件路径中。
gcc -I +"头文件"
2、实时检查头文件的更新情况,一旦头文件发生变化,应该要重新编译所有相关文件。
gcc -MM
ARCH ?= x86
ifeq ($(ARCH),x86)
CC=gcc
else
CC=arm-linux-gnueabihf-gcc
endif
TARGET=mp3
#OBJS=main.o mp3.o
BUILD_DIR=build
SRC_DIR=module1 module2
INC_DIR=include
CFLAGS=$(patsubst %,-I%,$(INC_DIR))
INCLUDES=$(foreach dir,$(INC_DIR),$(wildcard $(dir)/*.h))
SOURCES= $(foreach dir,$(SRC_DIR),$(wildcard $(dir)/*.c))
OBJS=$(patsubst %.c,$(BUILD_DIR)/%.o,$(notdir $(SOURCES)))
VPATH=$(SRC_DIR)
$(BUILD_DIR)/$(TARGET):$(OBJS)
$(CC) $^ -o $@
#main.o:main.c
# $(CC) -c main.c -o main.o
#mp3.o:mp3.c
# $(CC) -c mp3.c -o mp3.o
$(BUILD_DIR)/%.o:%.c $(INCLUDES) | creat_build
$(CC) -c $< -o $@ $(CFLAGS)
.PHONY:clean creat_build
clean:
rm -r $(BUILD_DIR)
creat_build:
mkdir -p $(BUILD_DIR)