美文网首页
Makefile伪目标、多目标、静态模式与变量基础用法

Makefile伪目标、多目标、静态模式与变量基础用法

作者: chenwr2018 | 来源:发表于2019-03-16 17:15 被阅读0次

    一、make的工作方式

    1. 读入所有的 Makefile。

    2. 读入被 include 的其它 Makefile。

    3. 初始化文件中的变量。

    4. 推导隐晦规则,并分析所有规则。

    5. 为所有的目标文件创建依赖关系链。

    6. 根据依赖关系,决定哪些目标要重新生成。

    7. 执行生成命令。

      1-5为第一阶段;
      6-7为第二阶段;
      

    第一阶段中,如果定义的变量被使用了,那么,make 会把其展开在使用的位置。但 make 并不会完全马上展开,make 使用的是拖延战术,如果变量出现在依赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。

    makefile只有一个最终目标,第一条规则的目标为最终目标。

    二、伪目标

    伪目标 PHONY英文释义:假的,欺骗的。
    就是告诉make这个不是真正意义上的target,make不需要自动生成依赖关系和推导规则。

    .PHONY: clean
    clean:
        -rm run *.o
    

    关键字.PHONY告诉make,不管当前目录是否有“clean”这个文件,clean都是伪目标。
    如果不加.PHONY,恰好存在名为clean文件时,执行make clean就会出现。

    root@chenwr-pc:/home/workspace/my_workspace/study/makefile# make clean
    make: `clean' is up to date.
    

    三、多目标

    需要生成多个可执行文件的做法。
    使用伪目标的方法,给伪目标指定所依赖的文件。

    all: main hello test
    .PHONY: all
    main:
        gcc -o main main.c
    hello:
        gcc -o hello hello.c
    test:
        gcc -o test test.c
    .PHONY: clean
    clean:
        rm main hello test
    

    运行结果:

    root@chenwr-pc:/home/workspace/my_workspace/study/makefile# make all
    gcc -o main main.c
    gcc -o hello hello.c
    gcc -o test test.c
    

    生成3个可执行文件,只需要一个make all命令。

    同时也可以使用静态模式来解决多目标的问题。

    四、静态模式

    语法:
    <targets ...>: <target-pattern>: <prereq-patterns ...>
    <commands>
    ....
    targets 定义了一系列的目标文件,可以有通配符。是目标的一个集合。
    target-parrtern 是指明了 targets 的模式,也就是的目标集模式。
    prereq-parrterns 是目标的依赖模式,它对 target-parrtern 形成的模式再进行一次依赖目标的定义。

    专业术语看得也是懵懵的。

    “$<”和“@”则是自动化变量,“\<”为依赖目标集和“$@”则是目标集。

    target = main.o hello.o test.o
    all : $(target)
    $(target):%.o: %.c
        gcc -c $< -o $@ 
    

    运行结果:

    root@chenwr-pc:/home/workspace/my_workspace/study/makefile# make all
    gcc -c main.c -o main.o 
    gcc -c hello.c -o hello.o   
    gcc -c test.c -o test.o 
    

    解读下,all这个目标依赖于target这个变量。现在就是打算把依赖的文件修改了,单纯懒得重新写依赖关系。直接在原来依赖关系的基础上修改下。%.o表示当前目录下所有.o文件,类似shell中*.o表示所有文件一样。%.o: %.c无非表示所有.o文件文件名后缀修改为.c罢了。
    最终显示出来的结果,

    target = main.o hello.o test.o
    all : $(target)
    $(target) : main.c hello.c test.c
        gcc -c $< -o $@
    

    $< 从那些依赖文件中读取(main.c hello.c test.c这些文件),$@从target这个变量赋值的多目标中读取。

    再配合函数filter使用,就更加灵活了。filter英文释义为过滤器。
    filter过滤函数用法
    $(filter <pattern...>,<text>)
    名称:过滤函数——filter。
    功能:以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。可以有多个模式。
    返回:返回符合模式<pattern>的字串。

    target = main.o hello.o test.o other.a some.so
    all : 
        @echo $(filter %.o %.a, $(target))
    

    运行结果:

    root@chenwr-pc:/home/workspace/my_workspace/study/makefile# make all
    main.o hello.o test.o other.a
    

    filter函数将target变量中包含的所有.o .a文件保留其他文件过滤。

    五、变量的使用

    引用变量:(变量名) 使用\字符: $$

    变量中的变量
    普通定义

    VAR1 = $(VAR2)
    VAR2 = 666
    test:
        @echo $(VAR1)
    

    输出VAR1的值为666,但是这种方式也有缺点,在使用递归定义时出现循环引用。还有就是如果在变量中使用函数,那么,这种方式会让我们的 make 运行时非常慢,更糟糕的是,他会使用得两个 make 的函数“wildcard”和“shell”发生不可预知的错误。因为你不会知道这两个函数会被调用多少次。

    递归定义

    VAR1 = $(VAR2)
    VAR2 = $(VAR1)
    test:
        @echo $(VAR1)
    

    运行结果:

    root@chenwr-pc:/home/workspace/my_workspace/study/makefile# make test
    Makefile:1: *** Recursive variable `VAR1' references itself (eventually).  Stop.
    

    使用 :=操作符进行赋值,就不会报上述错误。

    VAR1 := $(VAR2)
    VAR2 := $(VAR1)
    test:
        @echo $(VAR1)
    

    “:=”操作符作用?与“=”有啥区别?

    使用“=”操作符

    VAR1 = 666
    VAR2 = $(VAR1) 233
    VAR1 = 555
    test:
        @echo $(VAR1)
        @echo $(VAR2)
    

    运行结果:

    root@chenwr-pc:/home/workspace/my_workspace/study/makefile# make test
    555
    555 233
    

    再次声明VAR1变量,原先的值会被覆盖。并且VAR2中引用VAR1的变量是makefile展开后的最终值。

    使用“:=”操作符

    VAR1 := 666
    VAR2 := $(VAR1) 233
    VAR1 := 555
    test:
        @echo $(VAR1)
        @echo $(VAR2)
    

    运行结果:

    root@chenwr-pc:/home/workspace/my_workspace/study/makefile# make test
    555
    666 233
    

    值得一提的是,这种方法,前面的变量不能使用后面的变量,只能使用前面已定义好了的变量。因此VAR2中的引用的VAR1只能使用之前定义赋值的值。

    如何定义一个值为空格的变量?

    null :=
    space := $(null) #  
    test:
        @echo hello$(space)world
    

    运行结果:

    hello world
    

    $(null) #注意中间有个空格

    “?=”操作符的作用
    变量之前定义后,则不执行这条语句,如果变量没有被定义,则进行赋值。

    VAR1 := 666
    VAR1 ?= 777
    test:
        @echo $(VAR1)
    

    输出:666

    VAR1 :=
    VAR1 ?= 777
    test:
        @echo $(VAR1)
    

    输出:空

    VAR1 ?= 777
    test:
        @echo $(VAR1)
    
    输出:777
    

    大致归纳一下

    • = 是最基本的赋值
    • := 是覆盖之前的值 (“:=”表示变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值。)
    • ?= 是如果没有被赋值过就赋予等号后面的值
    • += 是添加等号后面的值

    相关文章

      网友评论

          本文标题:Makefile伪目标、多目标、静态模式与变量基础用法

          本文链接:https://www.haomeiwen.com/subject/slvsmqtx.html