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

1、make的工做方式

  1. 读入全部的 Makefile。shell

  2. 读入被 include 的其它 Makefile。bash

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

  4. 推导隐晦规则,并分析全部规则。ui

  5. 为全部的目标文件建立依赖关系链。spa

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

  7. 执行生成命令。code

    1-5为第一阶段;
    6-7为第二阶段;
    复制代码

第一阶段中,若是定义的变量被使用了,那么,make 会把其展开在使用的位置。但 make 并不会彻底立刻展开,make 使用的是拖延战术,若是变量出如今依赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。递归

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

2、伪目标

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

.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. 复制代码

3、多目标

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

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命令。

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

4、静态模式

语法:

<targets ...>: : <prereq-patterns ...>

....

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...>, )

名称:过滤函数——filter。

功能:以 模式过滤 字符串中的单词,保留符合模式 的单词。能够有多个模式。

返回:返回符合模式 的字串。

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文件保留其余文件过滤。

5、变量的使用

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

变量中的变量 普通定义

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展开后的最终值。)
  • ?= 是若是没有被赋值过就赋予等号后面的值
  • += 是添加等号后面的值
相关文章
相关标签/搜索