Makefile初识
什么是Makefile
当我们用KEIL、IAR、Vscode常常只需要傻瓜式的点击编译就能给我们生成一个可执行文件,点击清除就能保证我们的工程文件的干净整洁,那么他们背后是怎么实现的呢?在Linux开发中往往脱离这样的IDE工具,依赖于一个规则文件Makefile,按照一定规则依赖编译可执行文件,实际上这些编译器干的事儿也是Makefile干的事儿,接下来让我们来认识、了解、使用一下Makefile。
Makefile的核心
[目标] : 依赖1 依赖2 依赖3 ……
[TAB] 命令
当"目标"文件不存在或者某个依赖比目标文件新
则执行相对应的"命令"
# example:
# 文件夹下有funcA.c、funcB.c、funcC.c
func : funcA.o funcB.o funcC.o
gcc -o func funcA.o funcB.o funcC.o
funcA.o : funcA.c
gcc -c -o funcA.o funcA.c
funcB.o : funcB.c
gcc -c -o funcB.o funcB.c
funcC.O : funcC.c
gcc -c -o funcC.o funcC.c
clean:
rm -f *.o func
如上所示是一个简单的Makefile文件,我的可执行文件func依赖于三个.o文件,这三个.o文件又分别依赖于所对应的.c文件,此时在目录下执行make就能生成可执行文件
/* makefile的使用
make [目标名]
若make后面无目标名则默认是第一个目标
*/
make
//如果没有安装相应的环境需要安装一下
apt-get install gcc g++ make
//清理工程目录
make clean
Makefile基本语法
通过以上的学习,已经大致清楚了Makefile的实现原理,能够在一些小的工程项目上使用Makefile了;为什么是小的工程项目,相信不难发现,上面的Makefile文件为每一个.c文件都写了一个规则,在一些比较小的项目上还可以,如果面对那种大型项目往往成千上百个文件,这会让我们的Makefile文件变得很臃肿,接下来我们来学习义一些Makefile的基本语法来优化这个问题。
通配符
-
%.o %.c
表示所有的.o、.c文件;
-
$@
表示相应的目标
-
$<
表示第一个依赖文件
- $^
表示所以的依赖文件
# example:
# 文件夹下有funcA.c、funcB.c、funcC.c
func : funcA.o funcB.o funcC.o
gcc -o $@ $^
%.o : %.c
gcc -c -o $@ $<
clean:
rm -f *.o func
[!IMPORTANT]
这样是不是更加简洁以及明了了呢!!
假想目标
我们上面讲过makefile的使用是make [目标名],此时就会存在一个问题,比如上面写的Makefile文件有个规则目标为clean,我可以执行 make clean 来删除清空我们的工程,那如果我们工程目录下存在一个名字为clean的文件呢,此时再执行 make clean 就不会起作用了,他会首先判断名为clean的文件,此时我们的假想目标—— PHONY就起作用了。
# example:
# 文件夹下有funcA.c、funcB.c、funcC.c
func : funcA.o funcB.o funcC.o
gcc -o $@ $^
%.o : %.c
gcc -c -o $@ $<
.PHONY: clean
clean:
rm -f *.o func
如上图所示,这样操作之后不会判断clean的文件,可以正常执行
make clean
变量(即时变量和延时变量)
- 即时变量
:=
即时变量在定义的时候就给变量赋值,例:TARGET := func,定义的时候TARGET就赋值为func。
-
延时变量
=?==延时赋值,当调用到变量的时候才给他赋值;?=同理延时变量,但是这个只有第一次赋值的时候才会生效。+=可能是延时变量也可能是即时变量,却决于之前的定义
# example:
TARGET := func
CC = $(TEMP_CC)
SRC := /src
SRC ?= /lib/src
TEMP_CC = gcc
all:
@echo TARGET = $(TARGET)
@echo CC = $(CC)
@echo SRC = $(SRC)
执行以上Makefile文件可以验证以上所说的变量赋值,看看打印出来的值是否符合预期?


可以看出确实是如此的,第二张图片执行结果是我将 SRC := /src 注释掉之后的执行结果
学习了以上变量之后是不是可以对我们原先的Makefile文件做一些修改,让他更加的通用易读呢?尝试一下吧。
TARGET := func
#
SRCS := funcA.c funcB.c funcC.c
OBJS := $(SRCS:.c=.o)
# 编译器与参数
CC := gcc
# 可以加一些编译选项
CFLAGS := -Wall -Wextra -O2 -std=c99
LDFLAGS :=
# 默认目标
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^
# 通用模式规则
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
# 清理
clean:
$(RM) $(OBJS) $(TARGET)
函数
-
foreach函数
$(foreach <val>, <list>, <text>) #把列表list中的单词逐个取出放到参数val并执行text #example fun_name := funa funb func fund files := $(foreach val, $(fun_name), $(val).o) all: @echo files = $(files) #执行make 将输出funa.o funb.o func.o fund.o -
filter和filter-out函数
$(filter <patten……>, <text>) #在text中取出符合patten格式的值 $(filter-out <patten……>, <text>) #在text中取出不符合patten格式的值 #example fun_name := funa.o funb.c func.d fund.o fune.c fun1 = $(filter %.o, $(fun_name)) fun2 = $(filter-out %.d, $(fun_name)) all: @echo fun1 = $(fun1) @echo fun2 = $(fun2)
-
wildcard和patsubst函数
$(wildcard <patten……>) #patten定义了文件名的格式,取出其中存在的文件 $(patsubst <patten>, <replacement>, <var>) #从列表中取出每个值,如果符合patten则替换成replacement #example 假设 fun_name := funa.o funb.c func.d fund.o fune.c fun1 = $(wildcard src/*.c) fun2 = $(patsubst %.o, %.c, $(fun_name)) all: @echo fun1 = $(fun1) @echo fun2 = $(fun2)
[!IMPORTANT]
至此!已经学会写一个Makefile文件了,接下来自己拿一个Makefile实例检验一下吧

1万+

被折叠的 条评论
为什么被折叠?



