Makefile学习笔记(很全)

目录

1、Makefile总体介绍

1.1、架构

1.2、示例

1.3、变量的使用

1.4、隐晦规则

1.5、引用别的Makefile

1.6、通配符的使用

1.7、VPATH与vpath(有一个小问题)

1.8、伪目标

1.9、多目标

1.10、静态模式

1.11、自动生成依赖性(小模糊)

1.12、显示命令

1.13、命令执行

1.14、命令出错

1.15、嵌套执行make

1.16、定义命令包

1.17、语法

2、变量使用的详细介绍

2.1、变量中的变量

2.2、变量的高级用法

2.3、目标变量与模式变量

3、函数

3.1、函数的调用语法

3.2、字符串处理函数

3.3、文件名操作函数

3.4、其它函数

4、make的运行

4.1、make的退出码

4.2、指定Makefile

4.3、指定目标

4.4、make的参数

5、模式规则

5.1、模式规则介绍

5.2、自动化变量

5.3、后缀规则


1、Makefile总体介绍

1.1、架构

target: prerequisites
    command

target: prerequisites; command
    command

target:目标文件或执行文件或标签。

prerequisites:要生成target所需要的文件或目标。

command:make需要执行的命令,要以一个Tab键开头。

总结:prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。

注意几个变量:$@--目标文件,$^--所有的依赖文件,$<--第一个依赖文件。

1.2、示例

edit: main.o kbd.o command.o display.o \
    insert.o search.o files.o utils.o
    cc -o edit main.o kbd.oo command.o display.o \
        insert.o search.o files.o utils.o

main.o: main.c defs.h
    cc -c main.c

kbd.o: kbd.c defs.h command.h
    cc -c kbd.c

command.o: command.c defs.h command.h
    cc -c command.c

display.o: display.c defs.h buffer.h
    cc -c display.c

insert.o: insert.c defs.h buffer.h
    cc -c insert.c

search.o: search.c defs.h buffer.h
    cc -c search.c

files.o: files.c defs.h buffer.h command.h
    cc -c files.c

utils.o: utils.c defs.h
    cc -c utils.c

clean:
    rm edit main.o kbd.o command.o display.o \
        insert.o search.o files.o utils.o

反斜杠(\)是换行符的意思。

make:生成可执行文件edit。make会在当前目录下找名字叫“Makefile”或“makefile”的文件。如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文件,并把这个文件作为最终的目标文件。

make clean:删除可执行文件和所有中间目标文件。(这里clean是标签)

1.3、变量的使用

objects = main.o kbd.o command.o display.o \
    insert.o search.o files.o utils.o

edit: $(objects)
    cc -o edit $(objects)

main.o: main.c defs.h
    cc -c main.c

kbd.o: kbd.c defs.h command.h
    cc -c kbd.c

command.o: command.c defs.h command.h
    cc -c command.c

display.o: display.c defs.h buffer.h
    cc -c display.c

insert.o: insert.c defs.h buffer.h
    cc -c insert.c

search.o: search.c defs.h buffer.h
    cc -c search.c

files.o: files.c defs.h buffer.h command.h
    cc -c files.c

utils.o: utils.c defs.h
    cc -c utils.c

clean:
    rm edit $(objects)


# 真实的$
$$

1.4、隐晦规则

GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令,只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中。

objects = main.o kbd.o command.o display.o \
    insert.o search.o files.o utils.o

edit: $(objects)
    cc -o edit $(objects)

main.o: defs.h

kbd.o: defs.h command.h

command.o: defs.h command.h

display.o: defs.h buffer.h

insert.o: defs.h buffer.h

search.o: defs.h buffer.h

files.o: defs.h buffer.h command.h

utils.o: defs.h

.PHONY: clean
clean:
    rm edit $(objects)

“.PHONY”表示,clean是个伪目标文件(后续再探讨)。

上述代码可以写成下面这个样子:(不推荐)

objects = main.o kbd.o command.o display.o \
    insert.o search.o files.o utils.o

edit: $(objects)
    cc -o edit $(objects)

$(objects): defs.h

kbd.o command.o files.o: command.h

display.o insert.o search.o files.o: buffer.h

.PHONY: clean
clean:
    rm edit $(objects)

通常,一个被makefile指定成目标或是依赖目标的文件不能被当作中介。然而,你可以明显地说明一个文件或是目标是中介目标,你可以使用伪目标“.INTERMEDIATE”来强制声明。(如:.INTERMEDIATE : mid )

你也可以阻止make自动删除中间目标,要做到这一点,你可以使用伪目标“.SECONDARY”来强制声明(如:.SECONDARY : sec)。你还可以把你的目标,以模式的方式来指定(如:%.o)成伪目标“.PRECIOUS”的依赖目标,以保存被隐含规则所生成的中间文件。

1.5、引用别的Makefile

假设有几个Makefile:a.mk、b.mk、c.mk,还有一个文件叫foo.make,以及一个变量$(bar),其包含了e.mk和f.mk

include foo.make *.mk $(bar)

1.6、通配符的使用

"~":如果是“~/test”,这就表示当前用户的$HOME目录下的test目录。“~hchen/test”则表示用户hchen的宿主目录下的test目录。

"*":“*.c”表示所以后缀为c的文件。

# 不会展开,objects就是*.o
objects = *.o

# 会展开
objects = $(wildcard *.o)

1.7、VPATH与vpath(有一个小问题)

VPATH:特殊变量。make会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。

例如VPATH = src:../headers,目录之间由冒号分隔。

vpath:make关键字。

vpath <pattern> <directories>
vpath <pattern>
vpath

# make在“../headers”和“src”目录下(有先后顺序)搜索所有以“.h”结尾的文件。
vpath %.h ../headers:src

1.8、伪目标

伪目标同样可以作为“默认目标”,只要将其放在第一个即可。

all: prog1 prog2 prog3

.PHONY : all

prog1: prog1.o utils.o
    cc -o prog1 prog1.o utils.o

prog2: prog2.o
    cc -o prog2 prog2.o

prog3: prog3.o sort.o utils.o
    cc -o prog3 prog3.o sort.o utils.o

由于all这个伪目标成了默认目标,所以make的时候,all要生成(要执行),意味着all是“新”的了,那么它所依赖的prog1 prog2 prog3就会被生成,也就达到了一个make同时生成多个目标:prog1 prog2 prog3

另外,伪目标也可以成为依赖。

.PHONY: cleanall cleanobj cleandiff

cleanall: cleanobj cleandiff
    rm program

cleanobj:
    rm *.o

cleandiff:
    rm *.diff

make cleanobj 删除*.o

make cleandiff 删除*.diff

make cleanall 删除所有(小问题不确定)

1.9、多目标

bigoutput littleoutput: text.g
    generate text.g -$(subst output,,$@) > $@

# 等价于

bigoutput: text.g
    generate text.g -big > bigoutput

littleoutput: text.g
    generate text.g -little > littleoutput

1.10、静态模式

objects = foo.o bar.o

all: $(objects)

$(objects): %.o: %.c
    $(CC) -c $(CFLAGS) $< -o $@

# 等价于

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

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

1.11、自动生成依赖性(小模糊)

%.d: %.c
    @set -e; rm -f $@; \
    $(CC) -M $(CPPFLAGS) $< > $@.; \
    sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' < $@.> $@; \
    rm -f $@.

为每一个“name.c”的文件都生成一个“name.d”的Makefile文件。[.d]文件中就存放对应[.c]文件的依赖关系

总而言之,这个模式要做的事就是在编译器生成的依赖关系中加入[.d]文件的依赖

即把依赖关系:

 main.o: main.c defs.h

转化成:

main.o main.d: main.c defs.h

1.12、显示命令

@echo 正在编译XXX模块

make执行时,会输出

正在编译XXX模块

如果没有@,make执行时,会输出

echo 正在编译XXX模块

正在编译XXX模块

# 只显示命令,不执行。利于调试
make -n
make --just-print

# 全面禁止命令的显示
make -s
make --slient

1.13、命令执行

# 示例一:
exec:
    cd /home/hchen
    pwd

# 示例二:
exec:
    cd /home/hchen; pwd

当我们执行“make exec”时,第一个例子中的cd没有作用,pwd会打印出当前的Makefile目录,而第二个例子中,cd就起作用了,pwd会打印出“/home/hchen”。

1.14、命令出错

忽略命令的出错,我们可以在Makefile的命令行前加一个减号“-”(在Tab键之后),标记为不管命令出不出错都认为是成功的。

clean:
    -rm -f *.o
# Makefile中所有命令都会忽略错误
make -i
make --ignore-errors

如果一个规则是以“.IGNORE”作为目标的,那么这个规则中的所有命令将会忽略错误。

# 如果某规则中的命令出错了,那么就终目该规则的执行,但继续执行其它规则。
make -k
make --keep-going

1.15、嵌套执行make

# 以下两个均是去subdir目录下执行make
# $(MAKE) 也许我们的make需要一些参数,所以定义成一个变量比较利于维护
subsystem:
    $(MAKE) -C subdir

subsystem:
    cd subdir && $(MAKE)



# 总控Makefile的变量可以传递到下级的Makefile中(显示的声明),但是不会覆盖下层的Makefile中所定义的变量,除非指定了“-e”参数
export<variable ...>   # 传递变量到下级Makefile中
unexport<variable ...>    # 不想传递

# 如果你要传递所有的变量,那么,只要一个export就行了。后面什么也不用跟,表示传递所有的变量。
export

1.16、定义命令包

define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
enddef


# 使用
foo.c: foo.y
    $(run-yacc)

1.17、语法

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


示例二:
foo =
ifdef foo
frobozz = yes
else
frobozz = no
endif

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

在<conditional-directive>这一行上,多余的空格是被允许的,但是不能以[Tab]键做为开始(不然就被认为是命令)。而注释符“#”同样也是安全的。“else”和“endif”也一样,只要不是以[Tab]键开始就行了。
特别注意的是,make是在读取Makefile时就计算条件表达式的值,并根据条件表达式的值来选择语句,所以,你最好不要把自动化变量(如“$@”等)放入条件表达式中,因为自动化变量是在运行时才有的。

2、变量使用的详细介绍

2.1、变量中的变量

foo = $(bar)
bar = $(ugh)
ugh = Huh?

all:
echo $(foo)

= 支持前面的变量使用后面的变量。

y := $(x) bar
x := foo

# 结果y的值是“bar”,而不是“foo bar”

:= 不支持前面的变量使用后面的变量。

nullstring :=
space := $(nullstring) # end of the line

定义一个变量的值为空格,其中# end of the line 是有作用的,表示变量定义的终止。

dir := /foo/bar    # directory to put the frobs in

注意:dir后面还跟了四个空格

?= 含义是,如果没有定义过,那么执行,定义过了,便不执行。

+= 含义是追加变量。

2.2、变量的高级用法

foo := a.o b.o c.o
bar := $(foo:.o=.c)

foo := a.o b.o c.o
bar := $(foo:%.o=%.c)


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

2.3、目标变量与模式变量

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


# 也可以使用模式变量
%.o: CFLAGS= -O

在这个示例中,不管全局的$(CFLAGS)的值是什么,在prog目标,以及其所引发的所有规则中(prog.o foo.o bar.o的规则),$(CFLAGS)的值都是“-g”

3、函数

3.1、函数的调用语法

$(<function> <arguments>)

<function>是函数名,<arguments>是函数的参数,参数间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。

3.2、字符串处理函数

$(subst <from>,<to>,<text>)

名称:字符串替换函数——subst。
功能:把字串<text>中的<from>字符串替换成<to>。
返回:函数返回被替换过后的字符串。

$(patsubst <pattern>,<replacement>,<text>)


# 示例
$(patsubst %.c,%.o,x.c.c bar.c)

把字串“x.c.c bar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.o bar.o”

$(strip <string>)

名称:去空格函数——strip。
功能:去掉<string>字串中开头和结尾的空字符。
返回:返回被去掉空格的字符串值。

$(findstring <find>,<in>)

名称:查找字符串函数——findstring。
功能:在字串<in>中查找<find>字串。
返回:如果找到,那么返回<find>,否则返回空字符串。

$(filter <pattern...>,<text>)

名称:过滤函数——filter。
功能:以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。可以有多个模式。
返回:返回符合模式<pattern>的字串。

$(filter-out <pattern...>,<text>)

名称:反过滤函数——filter-out。
功能:以<pattern>模式过滤<text>字符串中的单词,去除符合模式<pattern>的单词。可
以有多个模式。
返回:返回不符合模式<pattern>的字串。

$(sort <list>)

名称:排序函数——sort。
功能:给字符串<list>中的单词排序(升序)。
返回:返回排序后的字符串。

备注:sort函数会去掉<list>中相同的单词。

$(word <n>,<text>)

名称:取单词函数——word。
功能:取字符串<text>中第<n>个单词。(从1开始)
返回:返回字符串<text>中第<n>个单词。如果<n>比<text>中的单词数要大,那么返回空字符串。

$(wordlist <s>,<e>,<text>)

名称:取单词串函数——wordlist。
功能:从字符串<text>中取从<s>开始到<e>的单词串。<s>和<e>是一个数字。
返回:返回字符串<text>中从<s>到<e>的单词字串。如果<s>比<text>中的单词数要大,那么返回空字符串。如果<e>大于<text>的单词数,那么返回从<s>开始,到<text>结束的单词串。

$(words <text>)

名称:单词个数统计函数——words。
功能:统计<text>中字符串中的单词个数。
返回:返回<text>中的单词数。

$(firstword <text>)

名称:首单词函数——firstword。
功能:取字符串<text>中的第一个单词。
返回:返回字符串<text>的第一个单词。

3.3、文件名操作函数

$(dir <names...>)

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

$(notdir <names...>)

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

$(suffix <names...>)

名称:取后缀函数——suffix。
功能:从文件名序列<names>中取出各个文件名的后缀。
返回:返回文件名序列<names>的后缀序列,如果文件没有后缀,则返回空字串。

$(basename <names...>)

名称:取前缀函数——basename。
功能:从文件名序列<names>中取出各个文件名的前缀部分。
返回:返回文件名序列<names>的前缀序列,如果文件没有前缀,则返回空字串。

$(addsuffix <suffix>,<names...>)

名称:加后缀函数——addsuffix。
功能:把后缀<suffix>加到<names>中的每个单词后面。
返回:返回加过后缀的文件名序列。

$(addprefix <prefix>,<names...>)

名称:加前缀函数——addprefix。
功能:把前缀<prefix>加到<names>中的每个单词后面。
返回:返回加过前缀的文件名序列。

$(join <list1>,<list2>)

名称:连接函数——join。
功能:把<list2>中的单词对应地加到<list1>的单词后面。如果<list1>的单词个数要比<list2>的多,那么,<list1>中的多出来的单词将保持原样。如果<list2>的单词个数要比<list1>多,那么,<list2>多出来的单词将被复制到<list2>中。
返回:返回连接过后的字符串。

示例:$(join aaa bbb , 111 222 333)返回值是“aaa111 bbb222 333”。

3.4、其它函数

$(foreach <var>,<list>,<text>)

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

names := a b c d
files := $(foreach n,$(names),$(n).o)

结果 $(files) 的值为a.o b.o c.o d.o

$(if <condition>,<then-part>,<else-part>)

# 或

$(if <condition>,<then-part>)

即if函数的参数可以是两个,也可以是三个。<condition>参数是if的表达式,如果其返回的为非空字符串,那么这个表达式就相当于返回真,于是,<then-part>会被计算,否则<else-part> 会被计算。

$(call <expression>,<parm1>,<parm2>,<parm3>...)

当 make执行这个函数时,<expression>参数中的变量,如$(1),$(2),$(3)等,会被参数<parm1>,<parm2>,<parm3>依次取代。而<expression>的返回值就是 call函数的返回值。

$(origin <variable>)

rigin函数不像其它的函数,他并不操作变量的值,他只是告诉你你的这个变量是哪里来的。返回值有“undefined”“default”“environment”“file”“command line”“override”“automatic”

contents := $(shell cat foo)

files := $(shell echo *.c)

shell 函数也不像其它的函数。顾名思义,它的参数应该就是操作系统Shell的命令。它和反引号“`”是相同的功能。这就是说,shell函数把执行操作系统命令后的输出作为函数返回。

$(error <text ...>)

产生一个致命的错误,<text ...>是错误信息。

$(warning <text ...>)

这个函数很像error函数,只是它并不会让make退出,只是输出一段警告信息,而make继续执行。

4、make的运行

4.1、make的退出码

0 —— 表示成功执行。

1 —— 如果make运行时出现任何错误,其返回1。

2 —— 如果你使用了make的“-q”选项,并且make使得一些目标不需要更新,那么返回2。

4.2、指定Makefile

make –f hchen.mk
make --file hchen.mk
make --makefile hchen.mk

4.3、指定目标

“all”              这个伪目标是所有目标的目标,其功能一般是编译所有的目标。

“clean”       这个伪目标功能是删除所有被make创建的文件。

“install”       这个伪目标功能是安装已编译好的程序,其实就是把目标执行文件拷贝到指定的目标中去。

“print”         这个伪目标的功能是例出改变过的源文件。

“tar”             这个伪目标功能是把源程序打包备份。也就是一个tar文件。

“dist”           这个伪目标功能是创建一个压缩文件,一般是把tar文件压成Z文件。或是gz文件。

“TAGS”        这个伪目标功能是更新所有的目标,以备完整地重编译使用。

“check”和“test”    这两个伪目标一般用来测试makefile的流程。

以上是规范命名,你可以不这样做。

4.4、make的参数

“-n”
“--just-print”
“--dry-run”
“--recon”
不执行参数,这些参数只是打印命令,不管目标是否更新,把规则和连带规则下的命令打印出来,但不执行,这些参数对于我们调试makefile很有用处。

“-t”
“--touch”
这个参数的意思就是把目标文件的时间更新,但不更改目标文件。也就是说,make假装编译目标,但不是真正的编译目标,只是把目标变成已编译过的状态。

“-q”
“--question”
这个参数的行为是找目标的意思,也就是说,如果目标存在,那么其什么也不会输出,当然也不会执行编译,如果目标不存在,其会打印出一条出错信息。

“-W <file>”
“--what-if=<file>”
“--assume-new=<file>”
“--new-file=<file>”
这个参数需要指定一个文件。一般是是源文件(或依赖文件),Make会根据规则推导来运行依赖于这个文件的命令,一般来说,可以和“-n”参数一同使用,来查看这个依赖文件
所发生的规则命令

“-b”
“-m”
这两个参数的作用是忽略和其它版本make的兼容性。

“-B”
“--always-make”
认为所有的目标都需要更新(重编译)。

“-C <dir>”
“--directory=<dir>”
指定读取makefile的目录。如果有多个“-C”参数,make的解释是后面的路径以前面的作为相对路径,并以最后的目录作为被指定目录。如:“make –C ~hchen/test –C prog”
等价于“make –C ~hchen/test/prog”。

“—debug[=<options>]”
输出make的调试信息。它有几种不同的级别可供选择,如果没有参数,那就是输出最简单的调试信息。下面是<options>的取值:

a —— 也就是all,输出所有的调试信息。(会非常的多)

b —— 也就是basic,只输出简单的调试信息。即输出不需要重编译的目标。

v —— 也就是verbose,在b选项的级别之上。输出的信息包括哪个makefile被解析,不需要被重编译的依赖文件(或是依赖目标)等。

i —— 也就是implicit,输出所以的隐含规则。

j —— 也就是jobs,输出执行规则中命令的详细信息,如命令的PID、返回码等。

m —— 也就是makefile,输出make读取makefile,更新makefile,执行makefile的信息。

“-d”
相当于“--debug=a”。

“-e”
“--environment-overrides”
指明环境变量的值覆盖makefile中定义的变量的值。

“-f=<file>”
“--file=<file>”
“--makefile=<file>”
指定需要执行的makefile。

“-h”
“--help”
显示帮助信息。

“-i”
“--ignore-errors”
在执行时忽略所有的错误。

“-I <dir>”
“--include-dir=<dir>”
指定一个被包含makefile的搜索目标。可以使用多个“-I”参数来指定多个目录

“-j [<jobsnum>]”
“--jobs[=<jobsnum>]”
指同时运行命令的个数。如果没有这个参数,make运行命令时能运行多少就运行多少。如果有一个以上的“-j”参数,那么仅最后一个“-j”才是有效的。

“-k”
“--keep-going”
出错也不停止运行。如果生成一个目标失败了,那么依赖于其上的目标就不会被执行了。

“-l <load>”
“--load-average[=<load]”
“—max-load[=<load>]”
指定make运行命令的负载。

“-o <file>”
“--old-file=<file>”
“--assume-old=<file>”
不重新生成的指定的<file>,即使这个目标的依赖文件新于它。

“-p”
“--print-data-base”
输出makefile中的所有数据,包括所有的规则和变量。这个参数会让一个简单的makefile都会输出一堆信息。如果你只是想输出信息而不想执行 makefile,你可以使用“make -q
p”命令。如果你想查看执行makefile前的预设变量和规则,你可以使用“make –p –f /dev/null”。这个参数输出的信息会包含着你的makefile文件的文件名和行号,所以,用
这个参数来调试你的makefile会是很有用的,特别是当你的环境变量很复杂的时候。
 

“-r”
“--no-builtin-rules”
禁止make使用任何隐含规则。

“-R”
“--no-builtin-variabes”
禁止make使用任何作用于变量上的隐含规则。

“-s”
“--silent”
“--quiet”
在命令运行时不输出命令的输出。

“-S”
“--no-keep-going”
“--stop”
取消“-k”选项的作用。因为有些时候,make的选项是从环境变量“MAKEFLAGS”中继承下来的。所以你可以在命令行中使用这个参数来让环境变量中的“-k”选项失效。

“-v”
“--version”
输出make程序的版本、版权等关于make的信息。

“-w”
“--print-directory”
输出运行makefile之前和之后的信息。这个参数对于跟踪嵌套式调用make时很有用。

“--no-print-directory”
禁止“-w”选项。

“--warn-undefined-variables”
只要make发现有未定义的变量,那么就输出警告信息。

5、模式规则

5.1、模式规则介绍

模式规则中,至少在规则的目标定义中要包含"%",否则,就是一般的规则。

%.o : %.c ; <command ......>

其含义是,指出了怎么从所有的[.c]文件生成相应的[.o]文件的规则。如果要生成的目标是"a.o b.o",那么"%c"就是"a.c b.c"。

5.2、自动化变量

$@
表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。

$%
仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。

$<
依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。

$?
所有比目标新的依赖目标的集合。以空格分隔。

$^
所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。

$+
这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标。

$*
这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b",并且目标的模式是"a.%.b",那么,"$*"的值就是"dir /a.foo"。这个变量对于构造有关联的文件名是比较有用。如果目标中没有模式的定义,那么"$*"也就不能被推导出,但是,如果目标文件的后缀是 make所识别的,那么"$*"就是除了后缀的那一部分。例如:如果目标是"foo.c",因为".c"是make所能识别的后缀名,所以,"$*"的值就是"foo"。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用"$*",除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么"$*"就是空值。

"D"的含义就是Directory,就是目录,"F"的含义就是File,就是文件。

$(@D)
表示"$@"的目录部分(不以斜杠作为结尾),如果"$@"值是"dir/foo.o",那么"$(@D)"就是"dir",而如果"$@"中没有包含斜杠的话,其值就是"."(当前目录)。

$(@F)
表示"$@"的文件部分,如果"$@"值是"dir/foo.o",那么"$(@F)"就是"foo.o","$(@F)"相当于函数"$(notdir $@)"。

"$(*D)"
"$(*F)"

和上面所述的同理,也是取文件的目录部分和文件部分。对于上面的那个例子,"$(*D)"返回"dir",而"$(*F)"返回"foo"

"$(%D)"
"$(%F)"

分别表示了函数包文件成员的目录部分和文件部分。这对于形同"archive(member)"形式的目标中的"member"中包含了不同的目录很有用。

"$(<D)"
"$(<F)"

分别表示依赖文件的目录部分和文件部分。

"$(^D)"
"$(^F)"

分别表示所有依赖文件的目录部分和文件部分。(无相同的)

"$(+D)"
"$(+F)"

分别表示所有依赖文件的目录部分和文件部分。(可以有相同的)

"$(?D)"
"$(?F)"

分别表示被更新的依赖文件的目录部分和文件部分。

5.3、后缀规则

后缀规则有两种方式:"双后缀"和"单后缀"。双后缀规则定义了一对后缀:目标文件的后缀和依赖目标(源文件)的后缀。如".c.o"相当于"%o : %c"。单后缀规则只定义一个后缀,也就是源文件的后缀。如".c"相当于"% : %.c"。如果一个后缀是make所认识的,那么这个规则就是单后缀规则,而如果两个连在一起的后缀都被make所认识,那就是双后缀规则。例如:".c"和".o"都是make所知道。因而,如果你定义了一个规则是".c.o"那么其就是双后缀规则,意义就是".c" 是源文件的后缀,".o"是目标文件的后缀。

.c.o:
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<

后缀规则不允许任何的依赖文件,如果有依赖文件的话,那就不是后缀规则,那些后缀统统被认为是文件名,如:

.c.o: foo.h
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<

这个例子,就是说,文件".c.o"依赖于文件"foo.h",而不是我们想要的这样:

%.o: %.c foo.h
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<

而要让make知道一些特定的后缀,我们可以使用伪目标".SUFFIXES"来定义或是删除,如:

.SUFFIXES: # 删除默认的后缀
.SUFFIXES: .c .o .h # 定义自己的后缀
  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值