Makefile中的变量

Makefile   2023-08-15 15:46   70   0  

变量的定义与使用

· Makefile中的变量有点类似C语言中的宏定义(#define),即用一个名称表示一串文本

· 但与C语言的宏定义不同的是,Makefile的变量值是可以改变的,变量定义之后可以在目标、依赖、方法等Makefile文件中的任意地方进行引用。

· 变量可以用来表示:

① 文件名序列(如一大串的依赖)

② 编译选项

③ 需要运行的程序

④ 需要进行操作的路径

⑤ ......

· 一个简单的使用变量来表示文件名序列的例子

· 使用变量的时候先使用$符,然后加上大括号/小括号,括号内写上变量名即可。

· 如果想要在变量名中使用$这个字符,就要书写两个$

可以看到,直接输出了变量a的内容,而a里面的内容没有被展开为变量来显示。

· 此处介绍另一种输出信息的方式:

${info <要输出的内容>}

· 例如:

可以发现输出了Hello World和${objs},这种输出方式会更适合在需要输出$符的情况下使用。

Makefile的读取过程

· GNU make使是分两个阶段来执行Makefile的。

· 第一阶段,读取阶段,会执行:

① 读取Makefile文件的所有内容。

② 根据Makefile的内容在程序内建立变量。

③ 在程序内构建起显式规则、隐式规则。

④ 建立目标和依赖之间的依赖图。

· 第二阶段,目标更新阶段,用第一阶段构建起来的数据确定哪个目标需要更新,然后执行对应的更新方法。

· 变量和函数的展开如果发生在第一阶段,就称为立即展开,否则称为延迟展开

· 立即展开的变量和函数是在Makefile被读取的时候就展开;延迟展开的变量和函数将会在用到的时候才会展开。

Makefile中三种变量赋值方式的区别

· Makefile中有三种变量的赋值方式,例子如下:

var1 = hello
var2 := hello 或 var2 ::= hello
var3 ?= hello

· 第一种直接使用等号进行赋值的方式称为递归展开赋值,它是延迟展开的,具体来看一个例子。

all里面表示输出temp这个变量,它会被替换成var这个变量,此时,var这个变量上下两句赋值语句都已经被读入,所以下面一句会覆盖上面一句,因此会输出下面一句的结果。

foo = ${bar}
bar = ${ugh}
ugh = Huh?

all:
    echo ${foo}

这个例子里面输出的就是Huh?了,因为三个变量的赋值都是延迟展开的。

· 第二种使用冒号+等号进行赋值的方式称为简单赋值,它是立即展开的,把上面的例子修改一下:

此时就变成输出hello了,因为是立即展开的,也就是读到temp这一句赋值语句的时候,就已经把里面的内容替换为上面var的内容了。

· 第三种使用问号+等号进行赋值的方式称为条件赋值,即如果之前已定义则不再定义,如果之前未定义则定义,如:

spacer.gif

上面的例子就会输出100,因为foo已经定义过为100了,不会被再次定义为200。

这个例子就会输出200,因为foo未被定义过。

变量的追加

· 使用加号+等号(+=)来在变量已有的基础上追加内容。例子:

Shell命令赋值

· 使用感叹号+等号(!=)进行赋值,该变量会把Shell命令的执行结果进行输出。例子:

上述例子会执行ls命令并展示输出结果。

变量赋值练习

x = hello
y = world
a := $(x)$(y)

· a的值为:helloworld

x = y
y = z
a := $($(x))

· a的值为:z

x = y
y = z
z = u
a := $($($(x)))

· a的值为:u

x = $(y)
y = z
z = Hello
a := $($(x))

· a的值为:Hello

Makefile中多行变量的定义

· 使用define-endef块来进行变量的多行定义:

define objs
Hello
World
!
endef

· 多行变量定义一般用在定义多个Shell指令中,如:

· 当然,多行变量的赋值也是可以使用上面讲过的不同的赋值方式的。

取消变量定义

· 使用undefine即可取消某一个变量的定义,例子如下:

可以看到,输出了一个空串,即变量objs已被取消定义。

使用系统的环境变量

· 在方法中可以使用系统的环境变量,方法和使用普通变量一样,例子如下:

可以看到,输出了当前用户名root。

变量替换引用

· 使用替换引用可以把变量中的某些内容进行替换,语法如下

${var:a=b}

· 举个例子

可以看到,在输出中,所有的.o后缀都被替换为了.cpp后缀。

· 除了上面的写法,在新版的GNU Make中,也可以在被替换的内容前面加上百分号

${var:%a=%b}

· 配合变量的Shell命令赋值,可以更方便的书写总目标:

可以看到,files变量先使用ls获取了所有.cpp文件,然后objs变量把所有的文件的后缀变为了.o,最终输出的就是.o文件,有了这个,就不需要一个个自己写依赖了!

变量覆盖

· 在执行Make指令的过程中,可以指定某一个变量的值,如:

可以看到,在make指令后面跟上了MSG的变量的重新定义,输出内容也随之变化了。

· 如果不想某个变量在执行的过程中被覆盖,只需要在变量前加上修饰词override即可:

可以看到,即使加上了MSG的重新定义,输出的内容也没有变化。

绑定目标的变量

· Makefile中的变量一般都是全局变量,但也可以将某个变量指定在某个目标的范围内,这样这个变量就只能在这个目标对应的规则内使用,这类变量就被称为绑定目标的变量

· 语法如下

· 例子

可以看到,target_var被限定在first.c中使用,在second.cpp中无法访问到这个变量。

· 如果想要定义多条变量需要分行写:

first.c: target_var1 = hello
first.c: target_var2 = world

· 这种变量的定义方式也是可以使用模式匹配的,比如:

可以看到,target_var前面的目标使用了模式匹配。%.c的意思就是所有以.c结尾的目标都可以使用这个变量。

自动变量

· 自动变量就是Make内置的一些变量,可以直接使用。

$@:①本条规则的目标名;②如果目标是归档文件的成员,则为归档文件名;③在多目标的模式规则中, 为导致本条规则方法执行的那个目标名;
$<:本条规则的第一个依赖名称
$?:依赖中修改时间晚于目标文件修改时间的所有文件名,以空格隔开
$^:所有依赖文件名,文件名不会重复,不包含order-only依赖
$+:类似上一个, 表示所有依赖文件名,包括重复的文件名,不包含order-only依赖
$|:所有order-only依赖文件名
$*:(简单理解)目标文件名的主干部分(即不包括后缀名)
$%:如果目标不是归档文件,则为空;如果目标是归档文件成员,则为对应的成员文件名

· 下面例子展示了$@的使用

可以看到,输出了当前的目标名first.c。



博客评论
还没有人评论,赶紧抢个沙发~
发表评论
说明:请文明发言,共建和谐网络,您的个人信息不会被公开显示。