欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

第六载:makefile变量的高级主题

程序员文章站 2022-05-13 15:09:03
...

    在makefile中,我们已经知道变量的基本赋值方法、使用等,但是在makefile中,变量还有一些高级的属性,掌握这些属性,可以使编写的makefile更加高效,现在就来了解下makefile中变量的高级属性

    一、变量值的替换

        - makefile支持使用指定字符(串)替换变量值中的后缀字符(串)
        - 语法格式为:$(var:a=b) 或者 ${var:a=b},即使用b替换变量var的后缀a,假如var为cba,那个替换后的结果cbb
          注意:替换表达式中不能有任何空格, make中也支持使用${}方式对变量进行取值

.PHONY : all

src := a.cpp b.cpp c.cpp
obj := $(src:.cpp=.o)

all:
    @echo "obj => $(obj)"

如上的makefile内容,执行后结果如下(使用.o替换src变量中的.cpp后缀)

/home/delphi>make all
obj => a.o b.o c.o
/home/delphi>

    二、变量的模式替换

        上边说的变量值的替换,仅仅是使用新的字符串替换变量的指定后缀字符串,而模式替换的替换范围稍微广一些,可以保留变量的指定字符,替换其他字符。
        - 使用%保留变量值中的指定字符,替换其他字符(%可以理解为make中的通配符)
        - 语法格式:$(var:a%b=x%y)或${var:a%b=x%y},即将var变量的前缀后缀a\b分别用x\y替换,中间部分不变。

.PHONY : all

src := hello.cpp hello.cpp hello.cpp
obj := $(src:h%.cpp=H%.o)

all:
    @echo "obj => $(obj)"

    上述的makefile,我们H、.o分别替换src变量的h前缀与.cpp后缀,运行结果如下

/home/delphi>make all
obj => Hello.o Hello.o Hello.o
/home/delphi>

   三、规则中的模式替换

    targets : target-pattern : prereq-pattern
              command1
              command2
              ......
     意义为:
      -通过target-patterntargets匹配子目标;再通过prereq-pattern子目标生成依赖;进而构成完整的规则。这个稍微不大好理解,下边用一个例子来说明第六载:makefile变量的高级主题

对于   $(obj) : %.o : %.c,会对变量obj进行逐个处理,首先拿%.o规则去匹配obj变量,得到main.o,main.o作为一个子目标,改行表达式就成为main.o : %.c,再使用%.c生成main.o的依赖,最后衍生成main.o : main.c这样的目标依赖关系,最终得到aaa@qq.com与$^;同理,遍历完obj中的main.o后,会接着遍历fun.o,逻辑与main.o一样,使用规则中的模式替换的方式可以避免对每个.c文件都写一条编译命令,下边来看一个makefile文件

.PHONY : clean rebuild all


target := hello.out
CC := gcc

obj : main.o func.o

#使用模式规则替换,避免每一条.c都要写一个编译命令
$(obj) : %o : %c
        $(CC) -o aaa@qq.com -c $^

$(target) : main.o func.o
        $(CC) -o aaa@qq.com $^


all : $(target)

rebuild : clean all

clean : 
        rm -f *.o $(target)

执行结果

/home/delphi/myshare/makefile>make all
gcc    -c -o main.o main.c
gcc    -c -o func.o func.c
gcc -o hello.out main.o func.o
/home/delphi/myshare/makefile>

从结果看出,规则中的模式替换,可以大大简化我们的工作量,尤其在项目中有成千上百个文件要编译时。

    四、变量值的嵌套引用

       - 一个变量名之中可以包含对其他变量的引用
       - 嵌套引用的本质是使用一个变量表示另一个变量

.PHONY : all

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

all:
    @echo "val => $(val)"

make all,这个嵌套引用比较简单

/home/delphi>make all
val => z
/home/delphi>

    五、命令行变量

        - 运行make时,在命令行定义变量
        - 命令行变量默认覆盖makefile中定义的变量
      
  - makefile中使用override关键字修饰变量时,命令行变量值不能覆盖makefile中定义的变量

.PHONY : all test

ifeq ($(DEBUG),true)
    result := this is debug
else
    result := this is release
endif

val_1 := this_is_val_1
override val_2 := this_is_val_2

all:
    @echo "result => $(result)"
    @echo "val_1 => $(val_1)"
    @echo "val_2 => $(val_2)"

执行命令,参数从命令行传入make all DEBUG:=true val_1:=cmd_this_is_val_1  val_2:=cmd_this_is_val_2

/home/delphi>make all DEBUG:=true val_1:=cmd_this_is_val_1  val_2:=cmd_this_is_val_2
result => this is debug
val_1 => cmd_this_is_val_1
val_2 => this_is_val_2
/home/delphi>

参数从命令行传入的好处是,我们可以直接在make参数中决定编译debug版本还是release版本

    六、环境变量(全局变量)

        - makefile中能够直接使用环境变量的值
        a、makefile中定义与环境变量同名的变量,环境变量会被覆盖
        b、运行make时指定"- e"选项,优先使用环境变量
        注意:所有makefile都可以直接使用环境变量,但是过多使用环境变量,会降低makefile的移植性

        - 变量在不同makefile之间的传递方式
        a、直接在外部定义环境变量进行传递(不推荐,可移植性差)
        b、使用export定义变量进行传递(定义临时环境变量)
        c、使用make 命令行变量进行传递(推荐)
        为了说明变量的跨文件传输,我们编写两个makefile测试:makefile与makefile1

#makefile 内容

.PHONY : all

QTDIR := MY_QTDIR    #QTDIR为环境变量,此处更改为MY_QTDIR,会覆盖原有环境变量,影响makefile1的QTDIR
val1 := this_is_val1
export val2 := this_is_val2    #定义临时环境变量,makefile1中可直接使用


all:
    @echo "QTDIR => $(QTDIR)"
    @echo "val1 => $(val1)"
    @echo "val2 => $(val2)"

    @echo "---------make test -f makefile1---------"
    @$(MAKE) test -f makefile1    #通过-f 指定make执行makefile1(下同)
    @echo "---------make test -f makefile1 end---------"

    @echo "---------make test -f makefile1 val1:=$(val1)---------"
    @$(MAKE) test -f makefile1 val1:=$(val1)    #通过命令行传递变量val1值
    @echo "---------make test -f makefile1 val1:=$(val1) end---------"
#makefile1 内容

.PHONY : test

test:
        @echo "QTDIR => $(QTDIR)"
        @echo "val1 => $(val1)"
        @echo "val2 => $(val2)"

 make all结果如下,可以看到,在makefile更改的QTDIR环境变量会覆盖原有的系统环境变量;export定义的临时环境变量val2在makefile1中也可直接访问;而普通变量在makefile中通过make参数传递到makefile1中,才能为makefile1所用

/home/delphi>make all
QTDIR => MY_QTDIR
val1 => this_is_val1
val2 => this_is_val2
---------make test -f makefile1---------
make[1]: 正在进入目录 `/home/delphi'
QTDIR => MY_QTDIR
val1 => 
val2 => this_is_val2
make[1]:正在离开目录 `/home/delphi'
---------make test -f makefile1 end---------
---------make test -f makefile1 val1:=this_is_val1---------
make[1]: 正在进入目录 `/home/delphi'
QTDIR => MY_QTDIR
val1 => this_is_val1
val2 => this_is_val2
make[1]:正在离开目录 `/home/delphi'
---------make test -f makefile1 val1:=this_is_val1 end---------
/home/delphi>

     七、目标变量及模式变量(二者均为局部变量,模式变量时目标变量的扩展)

        - 目标变量:作用域只在指定目标连带规则中
        - 模式变量:作用域只在符合模式的目标及连带规则中
       
说的直白点就类似于C语言中函数内部的局部变量,只不过这里的函数换成的目标
       
下边直接来看一个例子

.PHONY : all test another

val := this_is_val     #val作用域为整个makefile文件(同比C语言源文件中的全局变量)

test :  val := this_is_test_val   #val作用域为test目标或者连带规则中(同比C语言中函数中的局部变量)
%e : val := this_is_%e_val    #val作用域为以e结尾的目标或者连带规则中(同比C语言中函数中的局部变量)

all:
        @echo "all:"
        @echo "val => $(val)"

test : another
        @echo "test:"
        @echo "val => $(val)"

another :
        @echo "another:"
        @echo "val => $(val)"

rule :
        @echo "rule:"
        @echo "val => $(val)"
/home/delphi>
/home/delphi>make all test rule 
all:
val => this_is_val
another:
val => this_is_test_val
test:
val => this_is_test_val
rule:
val => this_is_%e_val
/home/delphi>

从运行结果可以看出,可以给变量限制不同的作用域。

以上7点便是变量的一些高级属性,需要在工作使用过程中慢慢体会。

以上内容参考《狄泰软件学院》操作系统篇之 - makefile专题

相关标签: make makefile