🌈个人主页:嵌入点滴
🔥近期文章: Linux 权限管理(简单易懂)https://blog.youkuaiyun.com/qq_66197674/article/details/139269090
文章目录
前言
💬今日更新了 Makefile 的相关内容
Makefile 语法格式 | 变量 | 函数的使用
🎉欢迎大家关注🔍点赞👍收藏⭐️留言📝
一. Makefile 语法格式
Makefile 是一个文本文件,用于指定一个文件项目如何构建。以下是 Makefile 的基本语法格式:
1. 目标(target): 依赖(dependencies)
命令(command)
目标是 Makefile 中的执行对象,依赖是目标所依赖的文件或目标,命令是执行生成目标所需要的操作。2. 目标: 依赖1 依赖2 ...
命令1
命令2
...目标可以有多个依赖和多个命令。命令前必须使用 Tab 键进行缩进。
3. 变量的定义
VARIABLE = value可以使用变量来定义文件名、编译器、编译选项等。在 Makefile 中,变量名通常使用大写字母。
4. 命令前的特殊符号
@ 表示不输出当前命令的执行信息
- 表示忽略执行过程中的错误5. 伪目标
.PHONY: clean
clean:
rm -rf *.o.PHONY 指示 make 工具在执行 clean 目标时不会检查是否存在与 clean 同名的文件。常用于声明一些不生成文件的目标。
可以根据项目需求来编写具体的 Makefile 文件。
设置 vim 首行缩进
打开 vim 编辑器的配置文件: (以 rc 结尾的配置文件)
vi /etc/vim/vimrc
在最后一行输入 set tabstop=4,保存后退出即可,然后 vim 打开后的缩进就是4个空格了。
二. 应用案例
目标:all
依赖:空
命令:gcc hello.c -o hello
也可以这样写
目标:all 和 hello.o
依赖:hello.o 和 hello.c
命令:gcc hello.c -o hello 和 gcc -c hello.c
因为 all 依赖 hello.o 文件,所以要先执行 gcc -c hello.o 文件,然后才可以执行
gcc hello.c -o
故输入 make 命令后,执行顺序如下:
在编译的时候,我们可以使用 make 目标来编译,如果我们不指定目标的话,默认执行的第一个目标所对应的规则,也就是说 make 和 make all 是一样的,如上面所示。
接下来,我们使用 make 目标的方法来编译。修改 makefile 代码如下:
然后我们输入命令 make clear 就可以直接执行 rm -rf *.o hello 命令。如下图所示。
但是,我们在当前目录下不能有和 makefile 目标名一样的文件。比如在当前目录下创建一个名为 clean 的文件,然后在执行 make clean 命令就会报错。如下图所示。
为了解决这个问题,makefile 引入了一个新的概念——伪目标
我们使用为目标来声明 clean 就可以避免与当前目录下的同名文件发生冲突。
伪目标格式:
.PHONY:目标
所以,我们可以把上面的代码修改成如下图所示:
然后在执行 make clean 命令,尽管当前目录下有 clear 同名文件,make clean 命令也是可执行成功的。如下:
三. Makefile 变量和变量赋值
- 变量可以在很多地方使用,比如目标,依赖,命令等。
- 变量的赋值可以是用:“ =,?=,:=,+= ”。
- 变量的引用:通过 $() 来完成变量的引用。
示例1:
使用 " := ” 来赋值
在 Makefile 里声明一个变量 var1 用 变量var2 来引用 var1, 然后打印输出:
使用 “ := ” 给变量赋值,是立刻赋值,在执行 var1:=eee 的同时变量值已经被确定了,所以最后打印是 eeeppp ,而不是 tttppp
示例2:
使用 " = ” 来赋值
使用 “ = ” 给变量赋值,是延迟赋值,就是 Makefile 里面最后被指定的值。因为最后给变量 var1 赋值为 ttt ,所以最后打印为 tttppp ,而不是 eeeppp ,如下:
示例3:
使用 " ?= ” 来赋值
使用 “ ?= ” 给变量赋值,意思是如果 var1 变量在前面没有被赋值,那么就给它赋值 kkk ,如果前面已经赋值了,就使用前面的值,所以,打印为 tttppp ,而不是 kkkppp,如下图:
如果我们注释掉前面 var1 赋值操作,那么 var1 变量在前面没有被赋值,那么就给它赋值 kkk
打印输出为kkkppp
示例4:
使用 " += ” 来赋值
使用 “ += ” 来赋值是追加赋值,是在我们前面定义好的字符串里面追加进去新的字符串,所以最后会打印 eee ttt fffppp,中间用空格隔开,如下图所示。
使用 “ += ” 也类似这样的赋值,请看此图!
如果赋值很长,我们还可以使用换行符 “ \ ” ,效果是一样的。
四. Makefile 自动化变量
定义:
在Makefile中,自动化变量是一组特殊的变量,它们在执行规则时会自动被设置为相应的值。这些自动化变量可以帮助我们在规则中引用与当前规则相关的文件名、目录名等信息。
以下是常用的自动化变量:
- $@:代表当前规则的目标文件名。
- $<:代表当前规则的第一个依赖文件名。
- $^:代表当前规则的所有依赖文件的列表(去重)。
- $+:代表当前规则的所有依赖文件的列表(保留重复文件)。
- $*:代表当前规则的目标文件名去除后缀。通过使用这些自动化变量,我们可以更加方便地引用当前规则相关的文件名,并使用它们来执行相应的操作。例如,在编译C程序时,可以使用$@来表示目标文件,$<来表示第一个依赖文件,从而方便地编译出对应的目标文件。
应用场景
先写一个程序如下:
main.c
hello.c
hello.h
makefile
运行结果
使用这个 makefile 虽然也可以成功编译运行,但是,一旦编译的文件多了,用这样的方式来编写,makefile 就会变得非常的复杂。故,自动化变量就可以用在这里啦!
接下来我们一步一步的来简化这个 makefile 。
简化一:用变量表示依赖文件
后面如果我们在增加依赖文件的话,直接在变量 var 后面增加就可以了。
简化二:
使用通配符 “ % ” ,和自动化变量 “ $@ ”, " $< " 替代依赖和目标,简化完后如下:
使用 “ %.o:%.c ” 就会自动帮我们匹配 hello.o ,main.o 和 hello.c ,main.c
" $< " 代表一系列的依赖文件名 hello.c ,main.c
“ $@ ” 表示目标文件名 hello.o ,main.o
简化三:
使用自动化变量 “ $^ ” 代表所有依赖( hello.o ,main.o ),简化完后如下:
运行输出结果如下
所以 “ $^ ” 表示所有依赖的列表是没问题的。
五. Makefile 函数
Makefile 函数是 Makefile 文件中的一种特殊语法,用于在构建过程中执行一些命令,并根据命令执行结果返回相应的值。Makefile 函数可以用来定义变量、执行命令和进行条件判断等操作。
Makefile 函数有两种形式:内置函数和自定义函数。
内置函数:是由 Make 工具提供的一些预定义函数,如shell函数、subst函数、patsubst函数等。
自定义函数:则是用户根据需要自行定义的函数,可以根据具体情况编写不同的函数来实现自己的需求。
使用函数的语法为:
$(FUNCTION_NAME arguments)
其中 FUNCTION_NAME 为函数名,arguments 为函数的参数。
Makefile 函数可以用于各种场景,如定义变量时使用函数给变量赋值、在命令中使用函数执行一些操作、进行条件判断时使用函数等。通过灵活地使用 Makefile 函数,可以更加灵活和智能地管理项目的构建过程。
1. wildcard 函数
格式:$(wildcard PATTENR)
功能:展开指定的目录
举例:
在 /home/pfb/桌面 目录下有一个 a.c 的 c 文件和一个 test 的文件夹,在 /home/pfb/桌面/test
文件夹下有一个 b.c 的文件。
我们在当前目录下创建 makefile 在里面写如下代码,echo 前面加 “ @ ” 符号,echo 这个命令就不会在打印中显示出来:
执行结果:
得到了 ./a.c 和 ./test/b.c,所以 wildcard函数把我们指定的 ./ 和 ./test/ 目录下的 c文件展开来。
2. notdir 函数
格式:$(notdir $ (var))
功能:去掉路径
举例:
在上面的 makefile 中添加以下代码,因为上面的例子我们得到的结果是了 ./a.c 和 ./test/b.c,是有路径的,可以直接使用这个变量。
执行结果:
因为 notdir 函数可以去掉路径,所以 ./a.c 和 ./test/b.c 去掉路径就可以得到 a.c 和 b.c
3. dir 函数
格式:$(dir<names...>)
给你:取出目录,这里的目录指的是最后一个反斜杠 “ / ” 之前的部分,如果没有反斜杠 “ / ” 就返回当前。
举例:
我们在上面的例子中加入以下代码,如图所示:
因为 var2 的值为 ./a.c 和 ./test/b.c,所以取出目录就是 ./ 和 ./test,如图所示:
4. patsubst 函数
格式:$(patsubst 原文件,目标文件,文件列表)
功能:替换文件后缀
举例:
我们在上面的例子中加入以下代码,如图所示:
使用这个 patsubst 函数仅仅只是替换文件后缀名,并不会改变当前目录下的后缀名
5. foreach 函数
格式:$(foreach <var>,<list>,<text>)
功能:把参数 <list> 中的字符逐一取出放到参数 <var> 所指定的变量中,然后在执行 <text> 所包含的表达式,每一次 <text> 会返回一个字符串。
举例:
我们在上面的例子中加入以下代码,如图所示:
执行结果如下:
结语
虽然学习和编写Makefile可能需要一些时间和精力,但一旦掌握了基本的概念和语法,它将成为开发过程中的重要帮手。无论是个人项目还是团队项目,Makefile都是一个值得掌握的工具。