Makefile初识

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文件可以验证以上所说的变量赋值,看看打印出来的值是否符合预期?

image-20250927003324717

image-20250927003243342

可以看出确实是如此的,第二张图片执行结果是我将 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)
    

    image-20250927010309766

  • 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)
    

    image-20250927011253854

[!IMPORTANT]

至此!已经学会写一个Makefile文件了,接下来自己拿一个Makefile实例检验一下吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值