代码改变世界

Linux Makefile

2022-04-05 15:20  jym蒟蒻  阅读(138)  评论(0编辑  收藏  举报

Makefile 格式

一个例子如下。Makefile由一系列规则组成。

在这里插入图片描述

规则格式如下:命令列表中的命令,以TAB键开始。

目标…: 依赖文件集合……
	命令 1
	命令 2
	……

下面规则,目标是main,依赖文件是main.o input.o calcu.o,命令是gcc -o main main.o input.o calcu.o。

依赖文件其中任何一个更新了,目标也得更新。

更新,执行一遍规则中的命令列表。

由于第一次编译,main不存在,所以执行第一条规则。

由于第一条规则依赖于main.o input.o calcu.o,但是目前没有这三个.o文件,所以make会去找以这三个.o文件为目标的规则,然后执行。

main : main.o input.o calcu.o
     gcc -o main main.o input.o calcu.o

最后一个规则,目标是clean,但是没有依赖文件,所以其对应命令不执行。

如果想要执行clean,使用命令make clean。

make执行过程:

1.当前目录下,找以Makefile命名的文件。

2.找到后,按Makefile中的规则,编译生成最终的目标文件。

3.目标文件不存在,或目标的依赖文件比目标新,执行后面的命令更新目标。

Makefile 变量

Makefile中的变量都是字符串。

定义一个变量objects,使用=赋值。

引用变量使用$(变量名)。

objects = main.o input.o calcu.o
main: $(objects)
	gcc -o main $(objects)

赋值符

=赋值符

变量的真实值,是它所引用的变量的最后一次有效值。

name = 111
curname = $(name)
name = 222

print:
	@echo curname: $(curname)

运行结果如下。

在这里插入图片描述

:=赋值符

不使用后定义的变量,只使用前面已经定义好的。

name = 111
curname := $(name)
name = 222

print:
	@echo curname: $(curname)

运行结果如下。

在这里插入图片描述

?=赋值符

如果变量在前面没有被赋值,那么就赋为新值;如果前面赋过值,那么就使用前面赋的值。

name = 111
curname = $(name)
curname ?= 222

print:
	@echo curname: $(curname)

结果:

在这里插入图片描述

+=赋值符

给前面已定义好的变量添加一些字符串。

objects = main.o inpiut.o
objects += calcu.o

Makefile 模式规则

通过模式规则,可以用一条规则,将所有.c 文件编译为对应的.o文件。

%表示长度任意的非空字符串。

如%.c,是所有以.c 结尾的文件。

a.%.c,表示以a.开头,以.c结束的所有文件。

下面这个代码加上命令,可以代替原来Makefile的3-8行代码。

命令还没写,需要借助自动化变量。

%.o : %.c
	#命令

Makefile 自动化变量

每一次对模式规则进行解析,都是不同的目标和依赖文件。

自动化变量,可以把符合模式的文件自动依次取出。

自动化变量,只出现在规则的命令中。

自动化变量描述
$@ 规则中的目标集合,在模式规则中,如果有多个目标,$@表示匹配模式中定义的目标集合。
$< 依赖文件集合中的第一个文件,如果依赖文件是以模式(%)定义的,$<是符合模式的一系列的文件集合。
$^ 所有依赖文件的集合,空格分开,如果依赖文件中有多个重复的文件,$^会去除重复的依赖文件,只保留一份。
$% 当目标是函数库时,表示规则中的目标成员名,如果目标不是函数库文件,那么其值为空。
$? 所有比目标新的依赖目标集合,以空格分开。
$+ 类似$^,但是,当依赖文件存在重复时,不会去除重复的依赖文件。
$* 表示目标模式中%及其之前的部分,如果目标是test/a.test.c,目标模式为a.%.c,$*就是test/a.test。

下面这个代码,可以代替原来Makefile的3-8行代码。

%.o : %.c
	gcc -c $<

Makefile 伪目标

下面这个代码,用来清理工程。

但是,规则中,没有创建文件clean的命令。

如果规则目录下创建一个名为clean的文件,执行make clean时,由于没有依赖文件,目标被认为是最新的。后面的rm命令不执行。

clean:
	rm *.o
	rm main

为解决这个问题,可以将clean声明成伪目标。

声明clean为伪目标后,无论当前目录下是否存在名为clean的文件,输入make clean,会执行rm命令。

.PHONY:clean

Makefile 条件判断

<条件关键字>
	<条件为真时执行的语句>
endif
    
<条件关键字>
	<条件为真时执行的语句>
else
	<条件为假时执行的语句>
endif

条件关键字:ifeq判断是否相等、ifneq判断是否不相等

ifeq (<参数 1>, <参数 2>)
ifeq ‘<参数 1 >’,‘ <参数 2>’
ifeq “<参数 1>”, “<参数 2>”
ifeq “<参数 1>”, ‘<参数 2>’
ifeq ‘<参数 1>’, “<参数 2>”

ifdef变量名非空,表达式为真、ifndef。

ifdef <变量名>

Makefile 函数

不支持自定义函数,函数是已经定义好的。

参数之间以逗号,隔开,函数名和参数之间用空格分开,调用函数以$开头。

$(函数名 参数集合)
${函数名 参数集合}

subst

字符串替换。

$(subst <from>,<to>,<text>) 将字符串<text>中的<from>替换为<to>,返回替换以后的字符
串
$(subst jym,JYM,Dog is JYM)

patsubst

模式字符串替换。

$(patsubst <pattern>,<replacement>,<text>)
    找字符串<text>中,符合<pattern>的字符串,用<replacement>替换掉。
    <pattern>可以使用通配符%表示任意长度的字符串。
$(patsubst %.c,%.o,a.c b.c c.c)
将字符串a.c b.c c.c中,所有符合%.c的字符串替换为%.o
    替换后,字符串是a.o b.o c.o

dir

获取目录。

$(dir <names…>)
从文件名序列<names>中提取出目录部分。
$(dir </src/a.c>)
提取文件/src/a.c的目录部分,/src。

notdir

去除文件中的目录部分,提取文件名。

$(notdir <names…>)
$(notdir </src/a.c>)
    提取出a.c

foreach

完成循环。

$(foreach <var>, <list>,<text>)
把参数<list>中的单词逐一取出,放到参数<var>中。
执行<text>包含的表达式。
每次<text>都返回一个字符串。
循环的过程中,<text>中所包含的每个字符串以空格隔开。
最后整个循环结束时,<text>返回的每个字符串所组成的整个字符串是foreach函数的返回值。
names := a b c d
files := $(foreach n,$(names),$(n).o)
$(name)中的单词被挨个取出,存到变量n中。
    $(n).o每次根据$(n)计算出一个值,这些值以空格分隔,最后作为foreach函数的返回
    $(files)的值是“a.o b.o c.o d.o”。

wildcar

%只有在规则中才会展开,变量定义和函数使用时,通配符不会自动展开,此时用函数wildcar。

$(wildcard PATTERN…)
$(wildcard *.c)
获取当前目录下,所有.c文件。