· Makefile中的变量有点类似C语言中的宏定义(#define),即用一个名称表示一串文本。
· 但与C语言的宏定义不同的是,Makefile的变量值是可以改变的,变量定义之后可以在目标、依赖、方法等Makefile文件中的任意地方进行引用。
· 变量可以用来表示:
① 文件名序列(如一大串的依赖)
② 编译选项
③ 需要运行的程序
④ 需要进行操作的路径
⑤ ......
· 一个简单的使用变量来表示文件名序列的例子
· 使用变量的时候先使用$符,然后加上大括号/小括号,括号内写上变量名即可。
· 如果想要在变量名中使用$这个字符,就要书写两个$:
可以看到,直接输出了变量a的内容,而a里面的内容没有被展开为变量来显示。
· 此处介绍另一种输出信息的方式:
${info <要输出的内容>}
· 例如:
可以发现输出了Hello World和${objs},这种输出方式会更适合在需要输出$符的情况下使用。
· GNU make使是分两个阶段来执行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的内容了。
· 第三种使用问号+等号进行赋值的方式称为条件赋值,即如果之前已定义则不再定义,如果之前未定义则定义,如:
上面的例子就会输出100,因为foo已经定义过为100了,不会被再次定义为200。
这个例子就会输出200,因为foo未被定义过。
· 使用加号+等号(+=)来在变量已有的基础上追加内容。例子:
· 使用感叹号+等号(!=)进行赋值,该变量会把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
· 使用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。