变量值的替换:使用指定字符(串)替换变量值中的后缀字符(串)。
语法格式:$(var:a=b)或${var:a=b}
替换表达式中不能有任何的空格.
make中支持使用${}对变量进行取值。
src :=a.cc b.cc c.cc
obj :=$(src:cc=o) //(a.o b.o c.o)
test :
@echo "obj =>$(obj)"
变量的模式替换:
使用%保留变量值中的指定字符,替换其他字符。
语法格式: $(var:a%b=x%y) 或 ${var:a%b=x%y}
替换表达式中不能有任何的空格。
make中支持使用${}对变量进行取值。
src :=a1b.c a2b.c a3b.c
obj :=$(src:a%b.c=x%y) //x1y x2y x3y
test:
@echo "obj=>$(obj)"
规则中的模式替换:
targets:target-pattern:prereq-pattern
command1
command2
...
意义:通过target-pattern从targets中匹配子目标;在通过prereq-pattern从子目标生成依赖;进而构成完整的规则。
规则中的模式替换示例:
OBJS :=func.o main.o
$(OBJS)(目标): %.o(模式取子串) : %.c //模式替换.o->.c
gcc -o $@ -c $^
=>
func.o: func.c
gcc -o $@ -c $^
main.o : main.c
gcc -o $@ -c $^
makefile:
src1 :=a.cc b.cc c.cc
obj1 :=$(src1:cc=0)
test1:
@echo "obj1=>$(obj1)"
src2:=a11b.c a22b.c a33b.c
obj2:=$(src2:a%b.c=x%y)
test2:
@echo "obj2=>$(obj2)"
3变量的嵌套应用:
一个变量名之中可以包含对其他变量的引用。
嵌套引用的本质是使用一个变量表示另外一个变量。
x :=y
y:=z
a:=$($(x)) ==> a:=$(y)==> a:=z //$(x) 取x的值
命令行变量:
运行make时,在命令行定义变量。
命令行变量默认覆盖makefile中定义的变量。
hm :=hello makefile
test :
@echo "hm=>$(hm)"
make hm=cmd ->hm=>cmd 临时改变变量的值
override关键字:
用于指示makefile中定义的变量不能被覆盖。变量的定义和赋值都需要使用override关键字。
override var :=test
test :
@echo "var =>$(var)"
make var=cmd -->hm=>test
define关键字:
用于在makefile中定义多行变量。
多行变量的定义从变量名开始到endef结束。
可使用override关键字防止变量被覆盖。
define定义的变量等价于使用=定义的变量。
makefile:
define foo
I'm fool!
endef
override define cmd
@echo "run cmd ls..." //多行变量的值是命令的话,要使用Tab键
@ls
endef
func.o main.o
小结:变量值的替换:$(var:a=b) 或 ${var: a=b}。
变量的模式替换:$(var:a%b=x%y) 或 ${ var:a%b=x%y }。
makefile支持将模式替换可以直接用于规则中。
makefile中的变量值能够嵌套使用。变量值当作变量名
命令行中定义的变量能够覆盖makefile中定义的变量。
override用于指示makefile中定义的变量不能被覆盖。
define用于在makefile中定义值为多行的变量。
7、变量的高级主题(下)
环境变量(全局变量):系统定义的
makefile中能够直接使用环境变量的值。
定义了同名变量,环境变量将被覆盖。
运行make时指定“-e”选项,优先使用环境变量。
为什么要在makefile中使用环境变量?
优势:环境变量可以在所有makefile中使用。
劣势:过多的依赖于环境变量会导致移植性降低。
变量在不同makefile之间的传递方式:
直接在外部定义环境变量进行传递。
使用export定义变量进行传递(定义临时环境变量)。
定义make命令行变量进行传递(推荐)。
export JAVA_HOME :=java home
#jiang xi tong zhong huan jing bian liang de zhi gai xie
export var :=D.t.software
new :=tdelphi
test:
@echo "JAVA_HOME=>$(JAVA_HOME)"
@echo "make anothwe file..."
@$(MAKE) -f makefile.2 #dang qian make jie shi qi wen jian ming
@$(MAKE) -f makefile.2 new:=$(new) #tong guo ming ling hang chuan di
makefile.2:
test:
@echo "JAVA_HOME=>$(JAVA_HOME)"@echo "var=>$(var)"
@echo "new=>$(new)"
目标变量(局部变量):
作用域只在指定目标及连带规则中。
target:name<assignment> value
target: override name<assignment> value
var :=D.o
test: var :=test-var
test:
@echo "test:"
@echo "var =>$(var)"
模式变量:
模式变量是目标变量的扩展。
作用域只在符合模式的目标及连带规则中。
pattern: name<assignment> value
pattern: override name<assignment> value
new :=Telphi
%e : override new:=test-new //%通配符,名为new的局部变量,作用域是所有以e结束的目标及连带规则
rule :
@echo "rule:"
@echo "new=>$(new)" //值为test-new
makefile:
var :=d.t.software
new :=Twelphi
test:var:=test-var
%e:override new :=test-new
test :another
@echo "test:"
@echo "var=>$(var)"
@echo "new=>$(new)"
another:
@echo "another:"
@echo "var=>$(var)"
@echo "new=>$(new)"
rule:
@echo "another:"
@echo "var=>$(var)"
@echo "new=>$(new)"
小结:makefile中的三种变量
全局变量:makefile外部定义的环境变量。
文件变量:makefile中定义的变量。
局部变量:指定目标的变量。
8、条件判断语句
makefile中支持条件判断语句:
可以根据条件的值来决定make的执行。
可以比较两个不同变量或者变量和常量值。
ifxxx(arg1,arg2)
# for ture
else
# for false
endif
注意事项:
条件判断语句只能用于控制make执行的语句;但是,不能控制规则中命令的执行过程。
条件判断语句的语法说明:
常用形式:
ifxxx(arg1,arg2) //(arg1,arg2)之前有空格,里边不能有空格
其他合法形式:
ifxxx "arg1" "arg2"
ifxxx 'arg1' 'arg2'
ifxxx "arg1" 'arg2'
ifxxx 'arg1' "arg2"
条件判断关键字:
ifeq 判断参数是否相等,相等为true,否则为false
ifneq 判断参数是否不相等,不相等为true,否则为false
ifdef 判断变量是否有值,有值为true,否则为false
ifndef 判断变量是否没有值,没有值为true,否则为false
.PHONY:test
var1 :=A
var2 :=$(var1)
var3 :=
test:
ifeq ($(var1),$(var2))
@echo "var1 == var2"
else
@echo "var1 != var2"
endif
ifneq ($(var2),)
@echo "var2 is not empty"
else
@echo "is empty"
endif
ifndef var3
@echo "var3 is empty"
else
@echo "var3 is not empty"
endif
一些工程经验:
条件判断语句之前可以有空格,但不能有Tab字符('\t')。
在条件语句中不要使用自动变量($@, $^, $<)。
一条完整的条件语句必须位于同一个makefile中。
条件判断类似C语言中的宏,预处理阶段有效(加载makefile时对条件判断语句进行处理),执行阶段无效。
make在加载makefile时:首先计算表达式的值(赋值方式不同,计算方式不同)。根据判断语句的表达式决定执行的内容。
.PHONY:test
var1 :=
var2 :=$(var1)
var3 =
var4 =$(var3) #du gui fu zhi jia zai qi wu fa que ding var4
var3=3
test:
ifdef var1
@echo "var1 is defined"
else
@echo "var1 is not defined"
endif
ifdef var2
@echo "var2 is defined"
else
@echo "var2 is not defined"
endif
ifdef var3
@echo "var3 is defined"
else
@echo "var3 is not defined"
endif
ifdef var4
@echo "var4 is defined"
else
@echo "var4 is not defined"
endif
小结:条件判断根据条件的值来决定make的执行。条件判断可以比较两个不同变量或者变量和常量值。
条件判断在预处理阶段有效,执行阶段无效。条件判断不能控制规则中命令的执行过程。
9、函数定义及调用
makefile中支持函数的概念:
make解释器提供了一系列的函数供makefile调用。
在makefile中支持自定义函数实现,并调用执行。
通过define关键字实现自定义函数。
自定义函数的语法:
函数定义:
define func1
@echo "my name is $(0)."
endef
define func2
@echo "my name is $(0)." //0号参数函数名自身
@echo "param=>$(1)" //1号参数调用时第一个实参的值
endef
函数调用:
test:
$(call func1)
$(call func2, D.T.Software)
深入理解自定义函数:
自定义函数是一个多行变量,无法直接调用。
自定义函数是一种过程调用,没有任何的返回值。
自定义函数用于定义命令集合,并应用于规则中。
.PHONY:test
define func1
@echo "my name is $(0)"
endef
define func2
@echo "my name is $(0)"
@echo "para 1=>$(1)"
@echo "para 2=>$(2)"
endef
var :=$(call func1)
#call jiang shi can ti huan dao duo hang min ling dui ying wei zhi
new :=$(func1)
test:
@echo "new=>$(new)" //@echo "my name is "
@echo "var=>$(var)" //@echo "my name is func1"
$(call func1) #=>@echo my name is func1
$(call func2, d.t.software,delphi)
make解释器中的预定义函数:
make的函数提供了处理文件名,变量和命令的函数。
可以在需要的地方调用函数来处理指定的参数。
函数在调用的地方被替换为处理的结果。
预定义函数的调用:
var:=$(func_name arg1,arg2,...) 返回值 函数名 函数实参
var :=$(abspath ./) //当前目录的绝对地址
test:
@echo "var =>$(var)"
问题?为什么自定义函数和预定义函数的调用形式完全不同?
本质剖析:
makefile中不支持真正意义上的自定义函数。
自定义函数的本质是多行变量。
预定义的call函数在调用时将参数传递给多行变量。
自定义函数是call函数的实参,并在call中被执行(参数替换,得到命令集合)。
.PHONY:test
define func1
@echo "my name is $(0)"
endef
#func2 := @echo "my name is $(0)"
define func2
@echo "my name is $(0)"
endef
var1 :=$(call func1)
var2 :=$(call func2)
var3 :=$(abspath ./)
var4 :=$(abspath test.cpp)
test:
@echo "var1=>$(var1)"
@echo "var2=>$(var2)"
@echo "var3=>$(var3)"
@echo "var4=>$(var4)"
真正意义的函数调用都是调用makefile中的预定义函数的调用,自定义函数的调用是不存在的,仅仅是模拟的行为。
小结:
make解释器提供了一系列的函数供makefile调用。
自定义函数是一个多行变量,无法直接调用。
自定义函数用于定义命令集合,并应用与规则中。
预定义的call函数在调用时将参数传递给多行变量。
自定义函数是call函数的实参,并在call中被执行。
10、变量与函数的综合示例
实战需求:
自动生成target文件夹存放可执行文件。
自动生成objs文件夹存放编译生成的目标文件(*.o)。
支持调试版本的编译选项。
考虑代码的扩展性。
工具原料:
$(wildcard_pattern):
获取当前工作目录中满足_pattern的文件或目录列表。
$(addprefix_prefix, _names):
给名字列表_names中的每一个名字增加前缀_prefix。
关键技巧:
1.自动获取当前目录下的源文件列表(函数调用)
SRCS :=$(wildcard *.c)
2.根据源文件列表生成目标文件列表(变量的值替换)
OBJS := $(SRCS: .c=.o)
3.对每一个目标文件列表加上路径前缀(函数调用)
OBJS:=$(addprefix path/, $(OBJS))
规则中的模式替换(目录结构)
工作目录中存在func.c和main.c =>
% .o : %.c //%.c用于模式匹配当前目录下的文件,% .o用于将% .c匹配成功的文件名进行模式替换,.c替换为 .o
gcc -o $@ -c $^ =>
func.o : func.c
gcc -o $@ -c $^
main.o : main.c
gcc -o $@ -c $^
编译规则的依赖:
all->target->dirs(创建文件夹) objs(编译目标文件) 生成可执行文件(链接)
makefile:
CC :=gcc
MKDIR :=mkdir
#chuang jian wen jian jia
RM :=rm -fr
DIR_OBJS := objs
DIR_TARGET :=target
DIRS :=$(DIR_OBJS) $(DIR_TARGET)
TARGET :=$(DIR_TARGET)/hello-makefile.out
SRCS :=$(wildcard *.c)
#main.c const.c func.c
OBJS :=$(SRCS:.c=.o)
#main.o const.o func.o
OBJS :=$(addprefix $(DIR_OBJS)/,$(OBJS))
#objs/main.o objs/const.o objs/func.o
.PHONY: rebuild clean all
$(TARGET):$(DIRS) $(OBJS)
$(CC) -o $@ $(OBJS) #lian jie
@echo "Target file=> $@"
$(DIRS):
$(MKDIR) $@ #chuang jian liang ge wenjianjia
$(DIR_OBJS)/%.o : %.c
ifeq ($(DEBUG),true)
$(CC) -o $@ -g -c $^ #dai tiao shi
else
$(CC) -o $@ -c $^
endif
rebuild: clean all
all: $(TARGET)
clean:
$(RM) $(DIRS)
查看反汇编:
objdump -S hello-makefile.out
#objdump命令反编译回去查看是否不带调试版本的可执行程序(汇编程序)
小结:目录可以成为目标的依赖,在规则中创建目录。
预定义函数是makefile实战时不可或缺的部分。
规则中的模式匹配可以直接针对目录中的文件。
可以使用命令行变量编译特殊的目标版本。