Makefile学习笔记_05_常用函数_shell_subst_patsubst_foreach_dir
目录
说明
自学B站某课程的笔记,欢迎小伙伴们共同讨论,若有理解不对的地方也欢迎指出。
https://www.bilibili.com/video/BV1EM41177s1/spm_id_from=333.999.0.0
开发环境:Visual Studio Code + gcc
函数调用语法
$(函数名, 函数参数)
圆括号
${函数名, 函数参数}
花括号
Note: 参数间以逗号 ,
分隔,函数名和参数之间以“空格”分隔
shell 函数
语法
$(shell <command> <arguments>)
调用 shell 命令 command,并返回 shell 命令 command 的执行结果。
示例
通过shell
指令找到src
文件夹下的所有.cpp
文件
cpp_srcs := $(shell find src -name "*.cpp")
debug:
@echo $(cpp_srcs)
.PHONY: debug
通过shell
指令获取计算机架构
HOST_ARCH := $(shell uname -m)
debug:
@echo $(HOST_ARCH)
.PHONY: debug
#我运行make debug输出了x86_64
subst 函数
字符串替换函数
语法
$(subst <a>,<b>,<text>)
把字符串text
中的a
替换成b
示例
# 找到src文件夹下的所有.cpp文件
cpp_srcs := $(shell find src -name "*.cpp")
# 把上一行找到的.cpp文件中的src/换成objs/
cpp_objs_1 := $(subst src/,objs/, $(cpp_srcs))
# 再把名字中的.cpp后缀换成.o后缀
cpp_objs_2 := $(subst .cpp,.o, $(cpp_objs_1))
debug:
@echo $(cpp_srcs)
@echo $(cpp_objs_1)
@echo $(cpp_objs_2)
.PHONY: debug
输出结果如下:
patsubst 函数
模式字符串替换函数。
语法
$(patsubst <pattern_1>,<pattern_2>,<text>)
在字符串text
中,按照模式1
找到相应文件,并替换成模式2
的形式。
通配符 %
表示任意长度的字串,函数返回被替换过后的字符串。
示例
在上面关于使用subst
函数的示例中,我们把src/
替换成objs/
,再把.cpp
替换成.o
一共分了2步完成,分别是以下2行代码。
# 把上一行找到的.cpp文件中的src/换成objs/
cpp_objs_1 := $(subst src/,objs/, $(cpp_srcs))
# 再把名字中的.cpp后缀换成.o后缀
cpp_objs_2 := $(subst .cpp,.o, $(cpp_objs_1))
若使用patsubst函数,则可以以模式的方式,将这2步合并为1步完成。
# 以src/%.cpp的模式进行文件寻找,以objs/%.o的模式进行字符替换
cpp_objs := $(patsubst src/%.cpp,objs/%.o,$(cpp_srcs))
完整代码如下:
# 找到src文件夹下的所有.cpp文件
cpp_srcs := $(shell find src -name "*.cpp")
# 以src/%.cpp的模式进行文件寻找,以objs/%.o的模式进行字符替换
cpp_objs := $(patsubst src/%.cpp,objs/%.o,$(cpp_srcs))
debug:
@echo $(cpp_srcs)
@echo $(cpp_objs)
.PHONY: debug
输出结果显示如下。
foreach 函数
循环函数。
语法
$(foreach <var>,<list>,<text>)
把字符串<list>中的元素逐一取出来,执行<text>包含的表达式,并把所有字符串所组成的整个字符串(以空格分隔)返回。
示例
我现在有3行字符串
include_paths := /aaa/bbb \
/aaa/ccc \
/aaa/ddd
我想在每行字符串的前面都加上一个-I
,操作代码如下:
include_paths := /aaa/bbb \
/aaa/ccc \
/aaa/ddd
include_paths :=$(foreach item,$(include_paths),-I$(item))
debug:
@echo $(include_paths)
.PHONY: debug
操作逻辑如图所示:
如果不用循环函数foreach
,还可以使用通配符达到同样的效果,代码如下:
include_paths := /aaa/bbb \
/aaa/ccc \
/aaa/ddd
I_flag := $(include_paths:%=-I%)
debug:
@echo $(I_flag)
.PHONY: debug
操作结果如下图所示。
dir函数
取目录函数。
语法
$(dir <names...>)
从文件名序列中取出目录部分并返回。目录部分是指最后一个反斜杠(“/”)之前
的部分。如果没有反斜杠,那么返回“./”。
示例
# 找到src文件夹下的所有.cpp文件
cpp_srcs := $(shell find src -name "*.cpp")
# 以src/%.cpp的模式进行文件寻找,以objs/%.o的模式进行字符替换
cpp_objs := $(patsubst src/%.cpp,objs/%.o,$(cpp_srcs))
objs/%.o : src/%.cpp
@mkdir -p $(dir $@)
@g++ -c $^ -o $@
compile : $(cpp_objs)
debug:
@echo $(cpp_srcs)
@echo $(cpp_objs)
.PHONY: debug compile
这里的代码与本文前面的部分差别不大,关键在下面代码:
objs/%.o : src/%.cpp #目标文件是objs下的所有.o文件,依赖项是src下的所有.cpp文件
@g++ -c $^ -o $@ #命令 编译依赖文件都编译成.o文件
但是如果这个objs
文件夹不存在,则会报错。在命令窗口使用make compile
代码,则会显示如下错误。
显示目录没有objs
这个文件夹。检查以下当前目录,嗯,确实没有。
因此,在执行这个命令前,我们要先创建一个objs
文件夹。
objs/%.o : src/%.cpp
@mkdir -p $(dir $@) #mkdir表示创建文件夹,加上-p则如果存在这个文件夹也不会报错
@g++ -c $^ -o $@ #$(dir $@)中的@指的是目标文件objs/%.o,dir函数取出目标文件的父目录。
在此基础上,我们再使用make compile
命令,则可以成功编译,结果如下。
可以看到,我们创建了objs
文件夹,并将src
中的main.cpp
文件编译成main.o
文件导出在objs
文件夹中。
备注
这个课程里对Makefile常用函数的讲解还包括以下3个函数:
notdir
、filter
、basename
但是讲课老师对于这几个函数的操作涉及到一些文件,我是远程在别人的服务器上跑的代码,没有这些文件,如果要写进博客,自己再去创建很多静态库、动态库,工作量有点大,这里就不进行讲解了,小伙伴们如果有兴趣,可以自己另外查找这3个函数的具体用法。