makefile学习总结

makefile学习总结

之前在跑板子时,自己了解过一些makefile,也尝试自己编写过,今天有时间,就学了学实验楼教程的makefile相关内容,故做了如下笔记。

makefile简介

makefile存在主要解决两个问题:

1)makefile文件用于记录整个项目工程的所有需要编译的文件列表,这样在编译时,仅需要输入简单的make命令便能完成项目的编译;
2)makefile文件反映整个项目中各模块的依赖关系,这样在改变了某些源文件后,仅需简单的make命令,make工具便会依据makefile文件里描述的依赖关系分析哪些模块需要重新编译,并执行相关操作。

makefile文件用于描述一个特定编译系统所需要的策略;make工具则通过makefile文件并执行相应的命令来帮助构建其编译系统。

makefile其实是一个简单的文本文件,它本质是由一条条的规则构成。makefile中最基本的语法单元就是规则。

makefile的规则构成如下:

target:prerequisites
command1
command2
……
commandN
其中,target为规则的目标;prerequisite是规则的依赖列表;command为规则的命令。注意:每行命令必须以tab键开始。

下面是一个最简单的makefile文件:只有一条规则,规则的目标是all,没有任何依赖(规则不可以没有目标,但可以没有依赖),有一条命令(规则也可以没有命令)。

# 最简单的makefile文件
all:
    echo “hello world"     #注意:每行命令都必须以tab键开始!

makefile的执行,使用make命令即可。

make命令的工作机理

make命令的基本使用范式如下:

make [-f makefile ] [ options ] … [ targets ] …

make命令最简单的执行方式如下:

1)简单粗暴,不带任何参数,直接执行make:make
2)执行makefile文件:make -f
3)执行makefile目标:make
4)到指定目录下执行make:make -C

执行make时,可以使用-f <文件名> 参数,来指定make命令从哪里读取makefile文件,如果不显示指定,则make会在当前目录下一次查找名为GNUmakefile,makefile和Makefile的文件作为其makefile的文件。

读取完makefile内容后,make工具并不是逐条执行makefile里的规则,而是以某条规则为突破口,然后执行makefile里的规则。而作为突破口的规则目标称为“终极目标”。在执行make时,如果已参数的形式指定终极目标,便以此执行为突破口,如果不显示指定终极目标,则make一般情况下使用makefile中的第一条规则的目标为终极目标。

一般makefile的内容如下:

终极目标:依赖A 依赖B 依赖C
终极目标命令
依赖A:子依赖A1 子依赖A2
依赖A命令
依赖B:子依赖B1 子依赖B2
依赖B命令
依赖C:子依赖C1 子依赖C2
依赖C命令
……

在编写makefile时,一定要充分分析清楚整个工程各模块的依赖关系,及所对应的生成命令。

makefile语法

makefile的语法包括:注释、指示符、内嵌函数、规则、变量定义。

注释

注释主要是用于提升makefile的可维护性。代码的可维护性强的一个重要标志是恰到好处的注释。makefile中“#”字符后的内容作为注释内容处理。如果注释行结尾存在反斜线(),则下一行也为注释行。如果makefile中需要使用#,可以利用转移字符\来实现,具体使用(#)即可。建议:**在写makefile时将注释作为一个独立的行,而不要和makefile的有效行放在一行中书写。makefile在被make时,会自动对以#开始到行尾的内容直接忽略不做任何处理。

变量

变量能够使makefile更灵活。makefile中的变量与C语言中的宏类似,它是一个文本字符串(变量的值,其类型只能是字符串类型)提供一个名字(变量名)。变量的定义如下:

变量名 赋值符 变量值

变量名即该变量的名字,不包括“:”、“#”、“=”、前置空白和尾空白的任何字符串。注意:尽管GNU make对变量的命名没有其他限制,但定义一个包含除字母、数字和下划线外的变量的做法也是不可取的,因为除字母、数字和下划线外的其他字符可能会在以后make版本中被赋予特殊函数,并且这样命令的变量对于一些shell来说不能作为环境变量使用。变量名对大小写敏感。makefile的传统做法是变量名全采用大写的方式。推荐对于内部定义的一般变量(如目标文件列表object)使用小写方式,而对一些参数列表使用大写方式。

变量值指变量所代表的内容,可是一个文件列表、编译选项列表、程序运行的选项参数列表、搜索源文件的目录列表、编译输出的目录列表等。变量值本质就是一个字符串。

赋值符:有基本定义类型(=和:=)和扩展定义类型(?=和+=)。

变量主要被目标、依赖、命令等引用。变量引用就是引用变量名的地方,用变量所代表的内容,执行一个严格的文本替换过程(展开过程),替换掉变量的名字。变量引用的几种方式:

1) 2 (变量名)
3) @、$^等。

makefile中变量引用的例子如下:
#变量定义
objects = program.o foo.o utils.o

program:$(objects)              #在依赖中引用变量
    gcc -o program ${objects}   #在命令中引用变量

$(objects):defs.h               #在目标中引用变量

根据变量定义时所使用的赋值操作不同,可将变量分成两种:递归展开式变量和直接展开式变量。使用=、+=和?=定义的变量都是递归展示式变量,而使用赋值操作符:=定义的变量为直接展开式变量。两种变量类型的根本区别是:变量值的求值时机,递归式变量的求值时机在于变量被引用时,直接展开式的求值时机在于变量被定义时。注意:使用递归展开式的变量定义,可能会由于出现变量的递归定义而导致make陷入到无限的变量展开过程中,最终使make执行失败。+=为追加赋值操作符,?=为条件赋值操作符。
在makefile中,除了用户定义的变量外,还有一些make工具提供的特殊变量。具体如如下的自动化变量:

@ <:表示规则的第一个依赖的文件名
$^:表示规则中所有依赖文件的列表,文件名用空格分割

注意: makefile使 @在bash中也有特殊用途,如果要显示,则需加上\字符。

变量的替换,对应格式为:(VAR:A=B){VAR:A=B},意思是将变量VAR所表示的值中所有字符串A结尾的字符替换为B。结尾的含义是在空格之前(变量的多个字以空格分开),而对变量其它部分的A字符不进行替换。

makefile复杂规则

多目标规则

多目标规则:即将多条具有相同依赖和相同生成命令的规则合并成一条规则。基本格式如下:

target …:prerequisite…
commands

多规则目标

多规则目标:即一个目标可以同时出现在多条规则中。这种情况下,此目标文件的所有依赖文件将会被合并成目标一个依赖文件列表,其中任何一个依赖文件比目标更新时,make将会执行特定的命令来重建这个目标。对于一个多规则的目标,重建此目标的命令只能出现在一个规则中(可以是多条命令)。如果多个规则同时重建此目标命令,make将使用最后一个规则的命令,同时提示错误信息。

当多条规则的命令一样,且它们目标依赖关系有点相似(目标都是以.o结尾的文件,依赖都是以.c结尾的文件),对于这种长得很像的规则,makefile提供了静态模式规则帮助简化规则的编写。静态模式规则,可以理解为一种特殊的多目标规则,它仅要求多条规则具有相同的命令,而依赖可以不完全一样。静态模式规则的基本语法如下:

TARGETS …: TRAGET-PATTERN: PREREQ-PATTERNS …
COMMANDS

静态模式规则基本语法的意思为:用TARGET-PATTERN: PREREQ-PATTERNS …描述的模式,从TARGETS …取值来形成一条条规则,所有规则的命令都是COMMANDS。TARGETS …代表具有相同模式的规则的目标列表。TARGET-PATTERN: PREREQ-PATTERNS …部分定义了如何为目标列表生成目标,生成依赖,其中TARGET-PATTERN为目标模式,PREREQ-PATTERNS为依赖模式。目标模式和依赖模式中,一般偶读包含模式字符%。目标模式的作用就是从目标列表中的目标匹配过滤出需要的值,目标模式中的字符%表示在匹配过滤的过程中不做过滤的部分,目标模式中的其他字符表示要与目标列表中的目标精确匹配。依赖模式的作用就是表示要如何生成依赖文件。具体生成过程就是使用目标模式过滤出来的值,替换依赖模式字符%所代表的位置。

为目标

伪目标,即不是整整的文件名,在执行make时可以指定这个目标来执行其所在的规则定义的命令。其基本语法如下:

.PHONY: <伪目标>

命令回显

make在执行命令前,要显示某句话,可使用echo命令。而关闭回显的方式如下:

1)在每个需要关闭回显的命令行前加上@字符
2)执行make时带上参数-s和–slient禁止所有执行命令的回显
3)在makefile中使用没有依赖的特殊目标.SILENT来禁止所有命令的回显

命令执行

makefile中在同一行的多个命令属于一个完整的shell命令行,而独立一行一条命令的命令是独立的shell命令行。注意:**在一个规则的命令中,命令行cd改变目录不会对其后的命令的执行产生影响。即其后的命令执行的工作目录不会是之前使用cd进入的那个目录。要实现这个目的,不能把cd和其后的命令放在两行书写,而应该把这两条命令写在一行上,用分号隔开,这样它们才是一个完整的shell命令行。

命令执行的错误处理

通常,规则中的每一条命令在运行结束后,make都会将检测命令执行的返回条件,如果返回成功,就执行下一条命令;如果命令出错(返回非0),make就会放弃对当前规则的执行,或者终止对当前makefile的解析执行。一些情况下,规则中的一个命令的执行失败并不代表规则执行的错误。为了忽略一些无关紧要的命令执行失败的情况,可在命令之前加一个减号-,来告诉make忽略此命令执行失败检查。

内嵌函数

make的内嵌函数为我们提供了处理文件名、变量、文本和命令的方法。它可使makefile更加灵活健壮。我们可以在需要的地方调用函数来处理指定的文本(参数)。函数调用(引用)的展开和变量引用的展示方式类似,对应方式如下:

1) (FUNCTIONARGUMENTS2 {FUNCTION ARGUMENTS}

内嵌函数分类:

文本处理函数:subst、patsubst、strip、findstring、filter、filter-out、sort、word、wordlist、firstword
文件名处理函数:dir、notdir、suffix、basename、addsuffix、addpreffix、join、wildcard
控制函数:error、warning
其他函数:if、foreach、call、value、eval、origin、shell

wildcard函数

wildcard函数,使用范式:

$(wildcard PATTERN)

函数名:wildcard
函数功能:列出当前目录下所有符合模式“PATTERN”格式的文件名
返回值:空格分割的、存在当前目录下的所有符合模式“PATTERN”的文件名
函数说明:“PATTERN”使用shell可识别的通配符,包括?(单字符)、*(多字符)等。

例如:$(wildcard *.c):返回值为当前目录下所有.c源文件列表。

makefile依赖关系

makefile支持使用sinclude关键字将指定文件导入到当前的makefile中,它的作用域C语言的#include预处理的命令是一样的,使用方式sinclude


参考文献https://www.shiyanlou.com/courses/713/labs/2374/document

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值