makefile 学习笔记(二)

x = variable1
variable2 := Hello
y = ( s u b s t 1 , 2 , (subst 1,2, (subst1,2,(x))
z = y
a := ( ( (($(z)))

最终, ( a ) 的 值 就 是 (a)的值就是 (a)(variable2)的值—— “Hello”。

a := ( ( (($(z)))
:= ( ( ((subst 1,2,$(x)))
:= ( ( ((subst 1,2,variable1))
subst函数把“variable1”中的所有“1”字串替换成“2”字串
:= ( ( ((subst 1,2,variable1))
:= $(variable2)
:= Hello

2021年9月15日

追加变量值

我们可以使用“+=”操作符给变量追加值,如:

objects = main.o foo.o bar.o utils.o
objects += another.o

使用“+=”操作符,可以模拟为下面的这种例子:
objects = main.o foo.o bar.o utils.o
objects := $(objects) another.o

如果变量之前没有定义过,那么,“+=”会自动变成“=”,如果前面有变量定义,那么“+=”会继承于前次操作的赋值符。
如果前一次的是“:=”,那么“+=”会以“:=”作为其赋值符,如:

variable := value
variable += more

等价于:

variable := value
variable := $(variable) more

但如果是这种情况:

variable = value
variable += more

由于前次的赋值符是“=”,所以“+=”也会以“=”来做为赋值,那么岂不会发生变量的递补归定义,
这是很不好的,所以make会自动为我们解决这个问题,我们不必担心这个问题。

五、override 指示符

如果有变量是通常make的命令行参数设置的,那么Makefile中对这个变量的赋值会被忽略。
如果你想在Makefile中设置这类参数的值,那么,你可以使用“override”指示符。其语法是:

override =
override :=

当然,你还可以追加:

override +=

六、多行变量
还有一种设置变量值的方法是使用define关键字。使用define关键字设置变量的值可以有换行,
这有利于定义一系列的命令(前面我们讲过“命令包”的技术就是利用这个关键字)。

define two-lines
echo foo
echo $(bar)
endef

七、环境变量

make 运行时的系统环境变量可以在make开始运行时被载入到Makefile文件中,但是如果Makefile中已定义了这个变量,
或是这个变量由make命令行带入,那么系统的环境变量的值将被覆盖。

当make嵌套调用时(参见前面的“嵌套调用”章节),上层Makefile中定义的变量会以系统环境变量的方式传递到下层的Makefile中。

而定义在文件中的变量,如果要向下层 Makefile传递,则需要使用exprot关键字来声明。

八、目标变量

前面我们所讲的在Makefile中定义的变量都是“全局变量”,在整个文件,
我们都可以访问这些变量。当然,“自动化变量”除外,如“$<”等这种类量的自动化变量就属于“规则型变量”,
这种变量的值依赖于规则的目标和依赖目标的定义。

当然,我样同样可以为某个目标设置局部变量,这种变量被称为“Target-specific Variable”,
它可以和“全局变量”同名,因为它的作用范围只在这条规则以及连带规则中,所以其值也只在作用范围内有效。
而不会影响规则链以外的全局变量的值。

其语法是:
<target …> :
<target …> : overide

可以是前面讲过的各种赋值表达式,如“=”、“:=”、“+=”或是“?=”。
第二个语法是针对于make命令行带入的变量,或是系统环境变量。

这个特性非常的有用,当我们设置了这样一个变量,这个变量会作用到由这个目标所引发的所有的规则中去。如:

prog : CFLAGS = -g
prog : prog.o foo.o bar.o
$(CC) $(CFLAGS) prog.o foo.o bar.o

prog.o : prog.c
$(CC) $(CFLAGS) prog.c

foo.o : foo.c
$(CC) $(CFLAGS) foo.c

bar.o : bar.c
$(CC) $(CFLAGS) bar.c

在这个示例中,不管全局的 ( C F L A G S ) 的 值 是 什 么 , 在 p r o g 目 标 , 以 及 其 所 引 发 的 所 有 规 则 中 ( p r o g . o f o o . o b a r . o 的 规 则 ) , (CFLAGS)的值是什么,在prog目标, 以及其所引发的所有规则中(prog.o foo.o bar.o的规则), (CFLAGS)progprog.ofoo.obar.o(CFLAGS)的值都是“-g”

九、模式变量

在GNU的make中,还支持模式变量(Pattern-specific Variable),通过上面的目标变量中,
我们知道,变量可以定义在某个目标上。模式变量的好处就是,我们可以给定一种“模式”,
可以把变量定义在符合这种模式的所有目标上。

我们知道,make的“模式”一般是至少含有一个“%”的,所以,我们可以以如下方式给所有以[.o]结尾的目标定义目标变量:
%.o : CFLAGS = -O

同样,模式变量的语法和“目标变量”一样:
<pattern …> :
<pattern …> : override

override同样是针对于系统环境传入的变量,或是make命令行指定的变量。

下面的例子,判断$(CC)变量是否“gcc”,如果是的话,则使用GNU函数编译目标。

libs_for_gcc = -lgnu
normal_libs =

foo: ( o b j e c t s ) i f e q ( (objects) ifeq ( (objects)ifeq((CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
ifeq的意思表示条件语句的开始,并指定一个条件表达式,表达式包含两个参数,以逗号分隔,表达式以圆括号括起。
else表示条件表达式为假的情况。endif表示一个条件语句的结束,任何一个条件表达式都应该以endif结束。

当我们的变量$(CC)值是“gcc”时,目标foo的规则是:
foo: $(objects)
$(CC) -o foo $(objects) $(libs_for_gcc)

而当我们的变量$(CC)值不是“gcc”时(比如“cc”),目标foo的规则是:
foo: $(objects)
$(CC) -o foo $(objects) $(normal_libs)

当然,我们还可以把上面的那个例子写得更简洁一些:

libs_for_gcc = -lgnu
normal_libs =

ifeq ( ( C C ) , g c c ) l i b s = (CC),gcc) libs= (CC),gcc)libs=(libs_for_gcc)
else
libs=$(normal_libs)
endif

foo: $(objects)
$(CC) -o foo $(objects) $(libs)

二、语法

endif

以及:

else endif

其中表示条件关键字,如“ifeq”。这个关键字有四个。

第一个是我们前面所见过的“ifeq”

ifeq (, )
ifeq ‘’ ‘’
ifeq “” “”
ifeq “” ‘’
ifeq ‘’ “”

比较参数“arg1”和“arg2”的值是否相同。当然,参数中我们还可以使用make的函数。如:

ifeq ($(strip $(foo)),)

endif

这个示例中使用了“strip”函数,如果这个函数的返回值是空(Empty),那么就生效。

第二个条件关键字是“ifneq”。语法是:

ifneq (, )
ifneq ‘’ ‘’
ifneq “” “”
ifneq “” ‘’
ifneq ‘’ “”

其比较参数“arg1”和“arg2”的值是否相同,如果不同,则为真。和“ifeq”类似。

第三个条件关键字是“ifdef”。语法是:
ifdef

如果变量的值非空,那到表达式为真。

示例一:
bar =
foo = $(bar)
ifdef foo
frobozz = yes
else
frobozz = no
endif

第一个例子中,“$(frobozz)”值是“yes”,第二个则是“no”。

第四个条件关键字是“ifndef”。其语法是:

特别注意的是,make是在读取Makefile时就计算条件表达式的值,
并根据条件表达式的值来选择语句,所以,你最好不要把自动化变量(如“$@”等)放入条件表达式中,
因为自动化变量是在运行时才有的。

使用函数

一、函数的调用语法

函数调用,很像变量的使用,也是以“$”来标识的,其语法如下:

$( )
或是
${ }

这里,就是函数名,make支持的函数不多。是函数的参数,参数间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。

如使用“ ( s u b s t a , b , (subst a,b, (substa,b,(x))”这样的形式,而不是“ ( s u b s t a , b , (subst a,b, (substa,b,{x})”的形式。因为统一会更清楚,也会减少一些不必要的麻烦。

comma:= ,
empty:=
space:= $(empty) $(empty)
foo:= a b c
bar:= $(subst ( s p a c e ) , (space), (space),(comma),$(foo))

$(comma)的值是一个逗号。
( s p a c e ) 使 用 了 (space)使用了 (space)使(empty)定义了一个空格
$(foo)的值是“a b c”

$(bar)的定义用,调用了函数“subst”,这是一个替换函数,这个函数有三个参数,
第一个参数是被替换字串
第二个参数是替换字串
第三个参数是替换操作作用的字串

这个函数也就是把 ( f o o ) 中 的 空 格 替 换 成 逗 号 , 所 以 (foo)中的空格替换成逗号,所以 (foo)(bar)的值是“
a,b,c”。

二、字符串处理函数

$(subst ,, )
名称:字符串替换函数——subst。
功能:把字串 中的字符串替换成。
返回:函数返回被替换过后的字符串。

$(subst ee,EE,feet on the street),
把“feet on the street”中的“ee”替换成“EE”,返回结果是“fEEt on the strEEt”。

$(patsubst ,, )

三、文件名操作函数

下面我们要介绍的函数主要是处理文件名的。每个函数的参数字符串都会被当做一个或是
一系列的文件名来对待。

$(dir <names…> )
名称:取目录函数——dir。
功能:从文件名序列中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。
返回:返回文件名序列的目录部分。
示例: $(dir src/foo.c hacks)返回值是“src/ ./”。

$(notdir <names…> )
名称:取文件函数——notdir。
功能:从文件名序列中取出非目录部分。非目录部分是指最后一个反斜杠(“/”)之后的部分。
返回:返回文件名序列的非目录部分。
示例: $(notdir src/foo.c hacks)返回值是“foo.c hacks”。

( s u f f i x < n a m e s . . . > ) 名 称 : 取 后 缀 函 数 — — s u f f i x 。 功 能 : 从 文 件 名 序 列 < n a m e s > 中 取 出 各 个 文 件 名 的 后 缀 。 返 回 : 返 回 文 件 名 序 列 < n a m e s > 的 后 缀 序 列 , 如 果 文 件 没 有 后 缀 , 则 返 回 空 字 串 。 示 例 : (suffix <names...> ) 名称:取后缀函数——suffix。 功能:从文件名序列<names>中取出各个文件名的后缀。 返回:返回文件名序列<names>的后缀序列,如果文件没有后缀,则返回空字串。 示例: (suffix<names...>)suffix<names><names>(suffix src/foo.c src-1.0/bar.c hacks)返回值是“.c .c”。

四、foreach 函数
foreach 函数和别的函数非常的不一样。因为这个函数是用来做循环用的,Makefile中的
foreach函数几乎是仿照于Unix标准Shell(/bin /sh)中的for语句,或是C-Shell(/bin
csh)中的foreach语句而构建的。它的语法是:

$(foreach ,, )
这个函数的意思是,把参数中的单词逐一取出放到参数 所指定的变量中,然后再执行 所包含的表达式。
每一次 会返回一个字符串,循环过程中, 的所返回的每个字符串会以空格分隔,最后当整个循环结束时,
所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。

names := a b c d
files := ( f o r e a c h n , (foreach n, (foreachn,(names), ( n ) . o ) 上 面 的 例 子 中 , (n).o) 上面的例子中, (n).o)(name)中的单词会被挨个取出,并存到变量“n”中,“ ( n ) . o ” 每 次 根 据 “ (n).o”每次根据“ (n).o(n)”计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以,$(f
iles)的值是“a.o b.o c.o d.o”。

五、if 函数
$(if , )
或是
$(if ,, )

六、call函数
call函数是唯一一个可以用来创建新的参数化的函数。你可以写一个非常复杂的表达式,这个表达式中,你可以定义许多参数,然后你可以用call函数来向这个表达式传递参数。

$(call ,,,…)
reverse = $(1) $(2)
foo = $(call reverse,a,b)

七、origin函数

origin函数不像其它的函数,他并不操作变量的值,他只是告诉你你的这个变量是哪里来的?其语法是:
$(origin )

注意,是变量的名字,不应该是引用。所以你最好不要在中使用“$”字符。

如果从来没有定义过,origin函数返回这个值“undefined”。
“default”

ifdef bletch

ifeq “$(origin bletch)” “environment”

bletch = barf, gag, etc.

endif

endif

八、shell函数
shell 函数也不像其它的函数。顾名思义,它的参数应该就是操作系统Shell的命令。

contents := $(shell cat foo)
files := $(shell echo *.c)

九、控制make的函数
make提供了一些函数来控制make的运行。通常,你需要检测一些运行Makefile时的运行时信息,并且根据这些信息来决定,你是让make继续执行,还是停止。

$(error <text …> )

示例一:

ifdef ERROR_001

$(error error is $(ERROR_001))

endif

示例二:

ERR = $(error found an error!)

.PHONY: err

err: ; $(ERR)

$(warning <text …> )
这个函数很像error函数,只是它并不会让make退出,只是输出一段警告信息,而make继续执行。

make 的运行

一、make的退出码
0 —— 表示成功执行。
1 —— 如果make运行时出现任何错误,其返回1。
2 —— 如果你使用了make的“-q”选项,并且make使得一些目标不需要更新,那么返回2。

二、指定Makefile
efile的名字是“hchen.mk”,那么,我们可以这样来让make来执行这个文件:
make –f hchen.mk

三、指定目标
有一个make的环境变量叫“MAKECMDGOALS”,
sources = foo.c bar.c
ifneq ( $(MAKECMDGOALS),clean)
include $(sources:.c=.d)
endif

.PHONY: all
all: prog1 prog2 prog3 prog4

从这个例子中,我们可以看到,这个makefile中有四个需要编译的程序——“prog1”, “prog2”, “prog3”和 “prog4”,我们可以使用“make all”命令来编译所有的目标
(如果把all置成第一个目标,那么只需执行“make”),我们也可以使用“make prog2”来单独编译目标“prog2”。

四、检查规则

五、make的参数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值