type
date
status
slug
summary
tags
category
password
icon
GNU make: nice 自动化工具。
Makefile文件的组成部分
- 显式规则。显式规则说明了如何生成一个或多个目标文件。该部分由makefile文件书写者明确指出,包括要生成的文件,文件的依赖文件,生成的命令等。
- 隐晦规则。利用make工具的自动推导功能,简化makefile文件的书写。
- 变量的定义。变量类似于C语言中的宏,一般为字符串。
- 文件指示。包括三部分:首先,在一个makefile中引用另一个makefile,类似include;其次,根据情况制定makefile中的有效部分,类似C语言中的选择编译宏;还有就是定义一个多行的命令。
- 注释。 #。
Makefile文件名
一般实用“Makefile”作为文件名。
也可以通过在make上添加”-f”或”- -file”后跟具体文件名来指定makefile文件。
使用include引用其它makefile文件
make的include文件寻找路径:
- 执行make时利用“-I”“- -include-dir”参数指定搜索目录;
- 如果目录/include存在(/usr/local/bin或/usr/include),make也会在此搜索。
make的工作方式
- 读入所有的makefile文件;
- 读入被include的其它makefile;
- 初始化文件中的变量;
- 推导隐晦规则,并分析所有规则;
- 为所有的目标文件创建依赖关系链;
- 根据依赖关系,决定哪些目标需要重新生成;
- 执行生成命令。
makefile的书写规则
规则包含两个部分:依赖关系+生成目标方法
在makefile中,规则的顺序是很重要的,在makefile中,第一条规则中的目标被确立为最终的目标。
无条件执行命令(-)
在命令前添加“-”,例如:
无轮后方文件是否出现问题,都执行该命令。
Makefile文件搜寻
在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录当中。
Makefile文件中的特殊变量“VPATH”就是代表makefile文件自动搜索的目录的,如果没有指明整个变量,make只会在当前的工作目录当中寻找依赖文件和目标文件。
For example:
上述变量添加了两个目录分别是src和../headers, makefile会按顺序搜索,但是会优先搜索当前目录。
另一个方法是实用vpath关键词:
伪目标
关于clean等伪目标,为了防止其与一些目标文件重名,可以利用.PHONY关键词,来显式地告诉makefile文件,不管是否有这个文件,这个就是个伪目标。
For example:
伪目标是总是被执行的,并不会考虑目标文件和依赖文件的新旧问题,所以始终认为目标文件比依赖文件新。
可以利用伪目标文件的这个机制实现一次性创建多个目标文件的目的:
By the way, 目标文件可以成为依赖,同样,伪目标文件也可以称为依赖。
For example:
多目标
Makefile文件的规则中的目标可以有多个,可能出现多个目标同时依赖于一个文件,并且生成的命令大体相似,可以实现合并。并使用自动化变量“$@”来表示目标的集合,“$@”依次取出目标,并执行命令。
For example:
这里的“>”有“为”的意思,即为哪一个目标文件执行次操作。
静态模式
静态模式可以更加容易地定义多目标的规则,可以让我们的规则变得更加地有弹性和灵活性:
targets定义了一系列的目标文件,可以有通配符,是目标的一个集合;
target-parrtern是指明了targets的模式,也就是目标集模式;
prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义。
例如,如果我们<target-parrtern>定义为“%.o”,意思是我们的集合中都是以“.o”结尾的,而如果我们的<prereq-parrterns>定义成“%.c”,意思是对<target-parrtern>所形成的的目标集进行二次定义,其计算方法是,取<target-parrtern>模式中的“%”(也就是去掉.o这个结尾),并为其加上.c结尾,形成新的集合。
所以,我们的“目标模式”或是“依赖模式”中都应该有“%”这个字符。
其中,“$<”变量表示所有的依赖目标集,即“foo.c bar.c”,“$@”表示目标集,即“foo.o bar.o”.
自动生成依赖性
可以利用gcc/g++工具的-MM选项输出源文件所依赖的头文件。
不过,如何将次功能与Makefile的编写结合起来呢。GNU组织建议把编译器为每一个源文件自动生成的依赖关系放到一个文件中,为每一个“name.c”文件都生成一个“name.d”的makefile文件,[.d]文件中就存放着[.c]文件的依赖关系。
一旦我们完成这个工作,接下来,我们就要把这些自动生成的规则放进我们的主Makefile中。我们可以使用Makefile的“include”命令,来引入别的Makefile文件:
上述语句的意思是根据“.c=.d”做一个替换,把变量$(sources)所有.c的字符串都替换成.d。
显示命令
通常,make会把其要执行的命令行在执行前输出到屏幕上。当我们用“@”字符在命令行前,name,这个命令将不会被make显示出来,最具代表性的例子是:
当make执行该命令时,会输出<message>,而不会显示命令。
如果执行make时,加入参数
-n
或者--just-print
,那么只显示命令,但不会执行命令,这一功能便于调试makefile。make的
-s
或--slient
参数则是全面禁止命令的显示。命令执行
当依赖目标新于目标时,make会一条一条地执行后面的命令。这很简单,不过需要注意的是,如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令。
命令出错
为防止命令出错而导致当前规则甚至是所有规则停止,我们需要在命令的前面添加“-”来强制命令执行成功。
嵌套执行make
在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile,这有利于让我们的Makefile变得更加地简洁,而不至于把所有的东西全部写在一个Makefile中,这样会很难维护我们的Makefile,这个技术对于我们模块编译和分段编译有着非常大的好处。
例如,在当前目录下有一个子目录名为subdir,该目录下也有一个makefile文件,那当前目录下的makefile文件可以:
我们将这个makefile文件叫做“总控makefile”,总控makefile的变量可以传递到下级的makefile中(显式声明),但是不会覆盖下层的makefile中的定义,除非制定了“-e”参数。
如果需要传递变量到下级,可以实用export关键词
需要注意的是,有两个变量,SHELL和MAKEFLAGS,不管是否实用export,其总是要传递到下层makefile中,尤其是MAKEFLAGs变量,其中包含了make的参数信息。
定义命令包
如果makefile文件中出现一些相同命令序列,name我们可以为这些相同的命令序列定义一个变量。定义这种命令序列的语法以“define”开头,以“endef”结束:
其中run-yacc是该命令包的名字,要避免其与makefile中的变量重名;处于define和endef两行之间的便是命令序列:
在makefile中使用变量
援引一下原博客:
在 Makefile中的定义的变量,就像是C/C++语言中的宏一样,他代表了一个文本字串,在Makefile中执行的时候其会自动原模原样地展开在所使用的地方。其与C/C++所不同的是,你可以在Makefile中改变其值。在Makefile中,变量可以使用在“目标”,“依赖目标”,“命令”或是 Makefile的其它部分中。变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有“:”、“#”、“=”或是空字符(空格、回车等)。变量是大小写敏感的,“foo”、“Foo”和“FOO”是三个不同的变量名。传统的Makefile的变量名是全大写的命名方式,但我推荐使用大小写搭配的变量名,如:MakeFlags。这样可以避免和系统的变量冲突,而发生意外的事情。有一些变量是很奇怪字串,如“$<”、“$@”等,这些是自动化变量,我会在后面介绍。
变量的使用
变量在声明时需要给予初值,而在使用时,需要给在变量名前加上“$”符号,但最好用小括号“()”或是大括号“{}”把变量给包括起来。如果你要使用真实的“$”字符,那么你需要用“$$”来表示。变量可以使用在许多地方,如规则中的“目标”、“依赖”、“命令”以及新的变量中。
变量中的变量
我们可以使用两种方式来利用变量定义变量。
第一种方式是简单地使用“=”,而且右侧变量的值可以定义在文件的任何一处,例如:
不过要使用这种方法也会产生诸多问题。
第二种方式,使用“:=”来定义变量,例如:
这一方法中,前面的变量不能使用后面的变量,只能使用前面定义好了的变量。
For example:
MAKELEVEL: 如果make有一个嵌套执行的动作,那么这个变量会记录我们当前makefile的调用层数。
操作符“?=”:如果左侧未被定义,则定义;否则什么也不做。
变量的高级用法
变量值的替换
格式:“$(var:a=b)”或”${var:a=b}”,意思是把变量“var”中所有以a字符串结尾的地方替换为以b结尾,这里的结尾标识符可以是空格或是结束符。
For example:
当然也可以使用静态模式进行定义:
这依赖于被替换字符串中有相同的模式,模式必须包含一个“%”字符。
自动化变量的使用
- $@: 所有目标文件;
- $<: 所有依赖文件;
makefile文件中函数的使用
word
words
wildcard
在Makefile规则中,通配符会被自动展开。但在变量的定义和函数引用时,通配符将失效。这种情况下如果需要通配符有效,就需要使用函数“wildcard”,它的用法是:
在Makefile中,它被展开为已经存在的、使用空格分开的、匹配此模式的所有文件列表。如果不存在任何符合此模式的文件,函数会忽略模式字符并返回空。
patsubst
模式字符串替换函数:
功能:查找text中的单词(单词以“空格”“Tab”或“回车”“换行”分隔)是否符合pattern,如果匹配,则以replacement替换。
这里,pattern可以包括通配符“%”,表示任意长度的字串。如果replacement中也包含“%”,那么,replacement中的这个“%”将是pattern中的那个“%”所代表的字串。(可以用“\”来转义,以“%”来表示真实含义的“%”字符)
abspath
用法:
$(abspath _names)
该函数主要用于将_names中的各路径转换成绝对路径,并将转换后的结果返回。
addprefix
功能:把<prefix>加到name序列中的每一个元素前面。
addsuffix 用法与addprefix相同,只是一个是前缀,一个是后缀。
if
功能:<condition>参数是 if的表达式,如果其返回的为非空字符串,那么这个表达式就相当于返回真,于是,<then-part>会被计算,返回计算结果字符串;否则<else- part>会被计算,返回计算结果字符串。<else-part>可以省略。
error
报告致命错误:TEXT,并退出make执行。
call
call函数是唯一一个可以用来创建新的参数化的函数的函数。你可以定义一个非常复杂的表达式,然后你可以用call函数来向这个表达式传递参数。
findstring
在IN字符串中查找FIND字符串,找到则返回FIND子串,否则返回空;
basename
取出前缀。
subst
word
从text字符串中取出第n个单词,n从1开始;
flavor
这一函数的输入为某一变量名,注意此时的变量名不需要括号与$;这一函数的输出根据输入变量种类的不同分为以下三种情况:
1.当输入变量在makefile及其include中都没有时,函数输出为’undefined’字符串
2.当输入变量在makefile及其include中有,且是用于了循环的变量时,输出’recursive’
3.当输入变量在makefile及其include中有且不是循环变量时,函数输出为’simple’
shell
两种用法
- 获取指令的执行结果保存到变量中;
- 只执行shell命令,进行相应操作;
sort
对LIST中的单词升序排序,并去掉重复单词;
join
将LIST1和LIST2字符串中对应单词相连接。
Make 中的基本变量
MAKECMDGOALS
make 在执行时会设置一个特殊变量 -- "MAKECMDGOALS" ,该变量记录了命令行参数指定的终极目标列表,没有通过参数指定终极目标时此变量为空。该变量仅限于用在特殊场合(比如判断),在 Makefile 中最好不要对它进行重新定义。
- 作者:Tdotd
- 链接:https://www.tdotd.top//article/make-learn
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。