当前位置: 首页 > backend >正文

makefile总结

Makefile

学习视频:1、野火的基础入门篇-第32讲 Makefile三要素_哔哩哔哩_bilibili

​ 2、b站视频04 一个稍复杂的Makefile_哔哩哔哩_bilibili

学习资料:第2个视频对应的Make/make.md · 无限十三年/CPP - 码云 - 开源中国

ch0_Makefile简介

Makefile是什么?

通过make工具和Makefile去管理需要编译的文件

make和Makefile是什么关系?

  • make工具:找出修改过的文化,根据依赖关系,找出受影响的相关文件,最后按照规则单独编译这些文件。
  • Makeflie文件:记录依赖关系和编译规则

makefile总览

在这里插入图片描述

ch1_Makefile三要素

Makefile三要素是什么?

目标、依赖、命令

怎么描述三要素?

目标:依赖的文件或者是其他目标

命令1

命令2

命令可以多行,但是前面必须加tap字符

实验演示

targeta:targetb targetcecho "targeta"
targetb:echo "targetb"
targetc:echo "tarfetc"mzj@ubuntu:~/Makefile$ make
echo "targetb"
targetb
echo "targetc"
targetc
echo "targeta"
targeta
mzj@ubuntu:~/Makefile$ make targetb
echo "targetb"
targetb
mzj@ubuntu:~/Makefile$ make targetc
echo "targetc"
targetc

.PHONY:目标

需要注意当文件路径下有跟目标相同的文件名时,想要make不被影响,应该在Makefile文件的开头加上.PHONY:目标名

原因:

  • .PHONY后面跟的目标都被称为伪目标
  • 也就是说我们 make 命令后面跟的参数如果出现在.PHONY 定义的伪目标中,那就直接在Makefile中就执行伪目标的依赖和命令。不管Makefile同级目录下是否有该伪目标同名的文件,即使有也不会产生冲突。
  • 另一个就是提高执行makefile时的效率。
.PHONY:targetb
targeta:targetb targetcecho "targeta"
targetb:echo "targetb"
targetc:echo "targetc"mzj@ubuntu:~/Makefile$ ls
Makefile  targetb  targetc
mzj@ubuntu:~/Makefile$ make targetc
make: “targetc”已是最新。
mzj@ubuntu:~/Makefile$ make targetb
echo "targetb"
targetb

ch2_Makefile变量

系统变量

Makefile_test文件
.PHONY:allall:echo "$(CC)"echo "$(AS)"echo "$(MAKE)"echo "$(MAKE)"mzj@ubuntu:~/Makefile$ make -f Makefile_test 
echo "cc"
cc
echo "as"
as
echo "make"
make
echo "make"
make

这里的make -f 指定Makefile文件名字,针对这个这个文件,可能有很多Makefile_test

自定义变量

  • = :延迟赋值,执行到这个变量时才赋值

    Makefile_test1文件
    A = 123
    B = $(A)
    A = 456.PHONY:all
    all:echo "$(B)"mzj@ubuntu:~/Makefile$ make -f Makefile_test1 
    Makefile_test1:7: *** 遗漏分隔符 (null)。 停止。
    !!!!!!!这里是tap建被vscode换成4个空格了,需要设置!!!!!!!
    #这里是执行到$(B)时,B才执行B = $(A) 456
    mzj@ubuntu:~/Makefile$ make -f Makefile_test1 
    echo "456"
    456
    
  • :=:为立即赋值,与常规思路一样

    A = 123
    B := $(A)
    A = 456.PHONY:all
    all:echo "$(B)"mzj@ubuntu:~/Makefile$ make -f Makefile_test1 
    echo "123"
    123
    
  • ?=:空赋值,只有变量为空时,赋值才有效,后面再赋值没有用,像是一次初始化

    A ?= 123
    A ?= 456.PHONY:all
    all:echo "$(A)"mzj@ubuntu:~/Makefile$ make -f Makefile_test1 
    echo "123"
    123
    
  • +=:追加赋值,不覆盖前面的,在前面的值基础上在尾部继续追加一些值

    A ?= 123
    A += 456.PHONY:all
    all:echo "$(A)"mzj@ubuntu:~/Makefile$ make -f Makefile_test1 
    echo "123 456"
    123 456
    

自动化变量

  • $<:第一个依赖文件first_tar
  • $^:全部的依赖文件first_tar second_tar
  • $@:目标all
all:first_tar second_tarecho "$<"echo "$^"echo "$@"
first_tar:
second_tar:mzj@ubuntu:~/Makefile$ make -f Makefile_test1 
echo "first_tar"
first_tar
echo "first_tar second_tar"
first_tar second_tar
echo "all"
all

Makefile中还有其它自动化变量,此处仅列出方便以后使用到的时候进行查阅,见下表。

符号意义
$@匹配目标文件
$% @ 类似,但 @类似,但 @类似,但%仅匹配“库”类型的目标文件
$<依赖中的第一个目标文件
$^所有的依赖目标,如果依赖中有重复的,只保留一份
$+所有的依赖目标,即使依赖中有重复的也原样保留
$?所有比目标要新的依赖目标

使用变量编译C文件

CC = gcc
TARGET=mp3
OBJS=main.o mp3.o$(TARGET):$(OBJS)$(CC) $^ -o $@main.o:$(CC) -c main.c -o main.o 
mp3.o:$(CC) -c mp3.c -o mp3.o .PHONY:cleanclean:rm $(TARGET)

ch3_Makefile模式匹配和默认规则

  • %:匹配任意多个非空字符

    .PHONY:all%:@echo "$@"@echo "$^"@echo "$<"
    # @ 符号的作用是抑制命令本身的输出,只显示命令执行的结果。mzj@ubuntu:~/Makefile$ make
    make: *** 无目标。 停止。
    mzj@ubuntu:~/Makefile$ make 123
    123

    @符号的作用是抑制命令本身的输出,只显示命令执行的结果。

    命令行输入给了%这个位置

    再看这个模板:这里的.c文件到.o文件的形式基本上一样的,就可以使用模式匹配%去代替

    CC = gcc
    TARGET=mp3
    OBJS=main.o mp3.o$(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 |||
    %.o:%.c$(CC) -c $< -o $@.PHONY:cleanclean:rm $(TARGET)
    

    这里的%.o:包含了所有的.o文件

  • 默认规则:.o文件默认使用.c文件进行编译,所以可以直接不管.o文件,下面即可

    CC = gcc
    TARGET=mp3
    OBJS=main.o mp3.o$(TARGET):$(OBJS)$(CC) $^ -o $@.PHONY:cleanclean:rm $(TARGET) *.o
    
  • 默认规则还有很多:后续补充

ch4_Makefile条件分支

ifeq(var1,var2)...
else...
endififneq(var1,var2)...
else...
endif

ifeq:两个条件相等

ifneq:两个条件不相等

里面需要加上

搭配?=进行举例

ARCH ?= x86
ifeq($(ARCH),x86)CC = gcc
elseCC = arm-linux-gnueabihf-gcc
endif
TARGET=mp3
OBJS=main.o mp3.o$(TARGET):$(OBJS)$(CC) $^ -o $@.PHONY:cleanclean:rm $(TARGET) *.omzj@ubuntu:~/Makefile$ make 
那么,CC = gcc
mzj@ubuntu:~/Makefile$ make ARCH=arm
由于空赋值,命令行中就给ARCH赋值了,CC = arm-linux-gnueabihf-gcc

ch5_Makefile常用函数

很多看官方手册

这里挑四个常用的

  • patsubst

patsubst

作用,例如把一堆.c文件名转成.o文件名

有两种方法:objects = foo.o bar.o baz.o

  • $(objects:.c=.o)
  • $(patsubst %.o,%.c,$(objects))
  • 第一种看起来,更直观,更简洁
$(patsubst %.c,%.o,x.c.c bar.c)

把字串“x.c.c bar.c”中以.c 结尾的单词替换成以.o 结尾的字符。函数的返回结果是“x.c.o bar.o”

$(patsubst %.c, build_dir/%.o, hello_main.c )
#函数的输出为:
build_dir/hello_main.o
#执行如下函数
$(patsubst %.c, build_dir/%.o, hello_main.xxx )
#由于hello_main.xxx不符合匹配规则"%.c",所以函数没有输出

例如:

我们存在一个代表所有.o 文件的变量。定义为

objects = foo.o bar.o baz.o

为了得到这些.o 文件所对应的.c 源文件。我们可以使用以下两种方式的任意一个:

$(objects:.o=.c)$(patsubst %.o,%.c,$(objects))

notdir

作用,把路径去了,只保留文件名

示例:

$(notdir src/foo.c hacks)
foo.c hacks

返回值为:这里的src/被去掉了,只留下foo.c hacks

wildcard

作用:列出当前目录下所有符合模式“PATTERN”格式的文件名。

“PATTERN”使用shell可识别的通配符,包括“?”(单字符)、“*”(多字符)等。

示例:

$(wildcard *.c)#在sources目录下有hello_func.c、hello_main.c、test.c文件
#执行如下函数
$(wildcard sources/*.c)
#函数的输出为:
sources/hello_func.c sources/hello_main.c sources/test.c

返回值为当前目录下所有.c 源文件列表

foreach

我们来看一个例子,实现了将变量“files”赋值为目录“dirs”下所有文件列表:

dirs := a b c d
files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))

例子中,表达式第一次执行时将展开为

$(wildcard a/*);第二次执行时将展开为$(wildcard b/*);第三次展开为$(wildcard c/*);….;以此类推。所以此函数所实现的功能就和一下语句等价:

files := $(wildcard a/* b/* c/* d/*)

ch6_多级结构工程的Makefile

接下来我们使用上面三个函数修改我们的Makefile,以适应包含多级目录的工程,修改后的内容如下所示。

在这里插入图片描述

#定义变量
#ARCH默认为x86,使用gcc编译器,
#否则使用arm编译器
ARCH ?= x86
TARGET = hello_main#存放中间文件的路径
BUILD_DIR = build_$(ARCH)
#存放源文件的文件夹
SRC_DIR = sources
#存放头文件的文件夹
INC_DIR = includes .#源文件
SRCS = $(wildcard $(SRC_DIR)/*.c)
#目标文件(*.o)
OBJS = $(patsubst %.c, $(BUILD_DIR)/%.o, $(notdir $(SRCS)))
#头文件
DEPS = $(wildcard $(INC_DIR)/*.h)#指定头文件的路径
CFLAGS = $(patsubst %, -I%, $(INC_DIR))#根据输入的ARCH变量来选择编译器
#ARCH=x86,使用gcc
#ARCH=arm,使用arm-gcc
ifeq ($(ARCH),x86)
CC = gcc
else
CC = arm-linux-gnueabihf-gcc
endif#目标文件
$(BUILD_DIR)/$(TARGET): $(OBJS)$(CC) -o $@ $^ $(CFLAGS)#*.o文件的生成规则
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c $(DEPS)
#创建一个编译目录,用于存放过程文件
#命令前带“@”,表示不在终端上输出
@mkdir -p $(BUILD_DIR)
$(CC) -c -o $@ $< $(CFLAGS)#伪目标
.PHONY: clean cleanall
#按架构删除
clean:rm -rf $(BUILD_DIR)#全部删除
cleanall:rm -rf build_x86 build_arm

ch7_同一项目中有多个Makefile文件

包含其他makefile文件

使用include指令可以读入其他makefile文件的内容,效果就如同在include的位置用对应的文件内容替换一样。

include mkf1 mkf2 # 可以引入多个文件,用空格隔开
include *.mk    # 可以用通配符,表示引入所有以.mk结尾的文件

如果找不到对应文件,则会报错,如果要忽略错误,可以在include前加-

-include mkf1 mkf2

应用实例:自动生成依赖

objs = block.o command.o input.o main.o scene.o test.osudoku: $(objs)g++ $(objs) -o sudokuinclude $(objs:%.o=%.d)%.d: %.cpp@-rm $@$(CXX) -MM  $< > $@.temp@sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.temp > $@@-rm $@.temp%.o : %.cppg++ -c $<@echo $^

嵌套make

如果将一个大项目分为许多小项目,则可以使用嵌套(递归)使用make。具体做法为,写一个总的Makefile,然后在每个子项目中都写一个Makefile,在总Makefile中进行调用。

例如,可以把sudoku项目中除main.cpp,test.cpp外的其他cpp存为一个子项目,编译为一个库文件,main.cpp test.cpp为另一个子项目,编译为.o然后链接库文件成可执行文件:

库文件Makefile

vpath %.h ../includeCXXFLAGS += -I../include -fexec-charset=GBK -finput-charset=UTF-8cpps := $(wildcard *.cpp)
objs := $(cpps:%.cpp=%.o)libsudoku.a: $(objs)ar rcs $@ $^$(objs): %.o : %.cpp %.h

main.cpp test.cpp的Makefile

CXXFLAGS += -I../include -fexec-charset=GBK -finput-charset=UTF-8
vpath %.h ../include
vpath %.a ../lib../sudoku: main.o test.o -lsudoku$(CXX) -o $@ $^

总的Makefile

.PHONY: all cleanall: subsrcsubsrc: sublib$(MAKE) -C srcsublib:$(MAKE) -C libclean:-rm *.exe src/*.o lib/*.o lib/*.a 

其中

$(MAKE) -C subdir

这一指令会自动进入subdir文件夹然后执行make。

可以通过export指令向子项目的make传递变量。

export var  # 传递var
export         # 传递所有变量
unexport    # 取消传递
http://www.xdnf.cn/news/2514.html

相关文章:

  • 关于Spark知识点与代码测试的学习总结
  • 单片机 + 图像处理芯片 + TFT彩屏 复选框控件
  • 30-算法打卡-字符串-重复的子字符串-leetcode(459)-第三十天
  • 使用 Cherry Studio 调用高德 MCP 服务
  • NFS从零部署
  • 华为 CCE 查看节点剩余可调度cpu核数
  • 从零实现分布式WebSocket组件:设计模式深度实践指南
  • 路由协议基础
  • babel和loader的关系
  • 微深节能 平板小车运动监测与控制系统 格雷母线
  • LeetCode题解1297. 子串的最大出现次数
  • 低压电工常见知识点
  • 麒麟系统通过 Service 启动 JAR 包的完整指南
  • 【KWDB 创作者计划】_KWDB引领数据库技术革新的璀璨之星
  • 业务校验工具包-validate-utils介绍
  • spring-rabbit的CachingConnectionFactory默认参数导致消费者Channel数量暴增问题解决
  • go语言八股文(三)
  • Deep Dark Sea 局域網文件共享即時匿名聊天去數據庫部署
  • Ldap高效数据同步- MirrorMode双主复制模式配置详解(下)
  • spring项目rabbitmq es项目启动命令
  • 【Pandas】pandas DataFrame rfloordiv
  • 查回来的数据除了 id,其他字段都是 null
  • 【音视频】SDL事件
  • AI 发展历史与关键里程碑_附AI 模型清单及典型应用场景以及物流自动化适合的模型选择
  • 从零开始开发一个简易的五子棋游戏:使用 HTML、CSS 和 JavaScript 实现双人对战
  • windows安装docker,发现没有hyper
  • JVM常见的垃圾回收器
  • RT Thread Studio创建USB虚拟串口工程
  • 生物医学AI的特种算力需求:冷冻电镜数据处理中的GPU加速方案
  • 互斥量函数组