编译工具Make

文章目录

  • make指令
    • 指定目标
    • 隐藏指令
    • 通配符
  • 伪目标
  • 多目标
  • Makefile的命令
  • 变量
    • 变量的基础
    • 赋值变量
        • :=
        • ?=
        • +=
  • 函数调用
    • 字符串操作函数
    • 文件名操作函数
    • 循环函数
    • 条件判断函数
  • 条件判断语句
  • 隐式规则
    • 隐式规则举例
    • 隐式规则中的变量
    • 使用模式规则
      • 模式规则举例
    • 自动化变量

​ Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作(编译和链接)。当明确指出时,Makefile还可以告诉make运行各种命令(例如,作为清理操作而删除某些文件)。
​ 而makefile 文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。

Makefile文件有一些规则(一个目标所形成的结构称为一个规则(rule)),规则的格式如下:

:
[tab] commands

目标(target)…:先决条件(prerequisites,又叫依赖文件)…
命令(command)


通常"目标"对象通常是程序生成的一个文件的名称,例如是一个可执行文件或目标文件。目标也可以是所要采取活动的名字(伪目标),比如all、clean、install、uninstall是具有特殊意义的目标(target),意义如下:

  • all:意思为「全部编译」,通常会是Makefile的第一个规则所用的目标。
  • clean:用来清除编译时产生的所有文件。
  • install:用来安装全部已经被编译好的程序。
  • uninstall:用来移除已经被安装好的程序。

"先决条件"是一个或多个文件名,用作产生目标的输入条件。
而"命令"是make需要执行的操作。
一个规则可以有多个命令,每一个命令自成一行。注意需要在每个命令行之前键入一个Tab键。

make指令

指定目标

make指令可以用来执行Makefile,在什么参数都不加的时候,make只会去执行Makefile中的第一个规则。如果要指定要执行哪个规则的话,可在make指令的第一个参数加上已经定义在Makefile中的目标文件名称,例如make all、make install或是make Hello.class。这也是为什么all这个目标通常会被放在第一个规则的原因,因为这样执行make就等于是执行make all。

隐藏指令

预设写在Makefile的指令在使用make指令来执行的时候都会被显示出来,要隐藏的话可以加上@
Hello.class: Hello.java
@javac Hello.java

###略过执行失败的指令

在相同规则下,只要有某个指令执行失败,其接下来的指令就不会被执行。如果这个指令本身是允许失败的话,想要在其失败后继续执行之后的指令,可以在该指令前加上减号-

###变量

在读取Makefile所定义的变数或是外部的环境变数时,都可以使用以下的语法: $变量名称
但如果想要在指令中使用环境变量,却不想要透过GNU make来事先读取的话,可以使用以下的语法:$$环境变量名称(两个$$在Makefile中所代表的意义就是转义成一个$)

###万用字元%

在目标和依赖文件的路径中,可以使用%来表示任意数量的任意字元。由于%也可以包含空格,如果有多个文件,且%位于最后一个依赖文件前的文件的路径结尾,%后要加上一个冒号:来作分隔。
例如:
install: target/%: data/%

通配符

如果想定义一系列比较类似的文件,我们很自然地就想起使用通配符。make支持三种通配符:* ?和[]
这和UNIX的BShell是相同的。
通配符代替了一系列的文件,如“*.c"表示了所有以后级名为.c的文件。
如果文件名中含有通配符,那么可以用较义字符斜杠“\”。
下面是一个在命令中使用通配符的例子:

clean:
	rm -f *.o

例子的含义是删除所有以.o为后缀名的文件,这是由操作系统的Shell所支特的通配符。
通配符也可以使用在Makefile的规则中,比如:

print: *.c
	Ipr -p $?
	touch print

目标print依赖于所有的.c文件。其中“$?”是一个自动化变量,表示所有比目标新的依赖文件的集合。
通配符还可以使用在变量中,比如:
objects = .o
需要注意的是,这和上面的两种情况不同,这里的
.o不会展开!objects变量的值就是“*.o”
Makefile中的变量其实就是C中的宏。如果要让通配符在变量中展开,也就是让objects的值是所有.o的文件名的集合,那么,可以这样:

objects := $(wildcard *.o)

这种用法是通过Makefile的关键字"wildcard”(通配符)来指示的。

伪目标

clean :
	rm -f *o

这是一个伪目标,伪目标不是一个文件,而只是一个标签。
由于伪目标不是文件,所以make无法根据依赖关系来决定它是否要执行,只有显式地指明这个“目标”才能让其生效。
需要注意的是伪目标的取名不能与文件名重名,不然其就失去其伪目标的意义了。
当然,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显式地指明一个目标是伪目标,向make说明,不管是否有这个文件,这个目标都是伪目标。

.PHONY : clean

只要有这个声明,不管有没有“clean”文件,要运行“clean”这个目标。只要在命令行下输入命令“make clean”即可。整个过程可以这样写

.PHONY: clean
  rm -f *.c

伪目标一般没有依赖文件,但是我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为默认目标,只要将它放在第一个。
比如,如果Makefile需要一口气生成若干可执行文件,但只想简单地输入一个“make”命令就完事,并且,所有的目标文件都写在一个Makefile中,那么便可以使用伪目标这个特性,比如如下所示的代码。
使用伪目标

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 proq3.o sort.o utils.o

我们知道,Makefile中的第一个目标会被作为其默认目标
在上述程序中声明了一个伪目标all,且依赖于其他三个目标prog1、prog2和prog3。
由于伪目标的特性是总是被执行的,所以其依赖的那三个目标总是不如“all”这个目标新,在进行make重编译时其他三个目标的规则就会总是被决议,也就达到了我们一口气生成多个目标的目的。

多目标

Makefile规则中的目标可以不止一个,其支持多目标,当Makefile中的多个目标同时依赖于同一个文件,并且其生成的命令大体类似,就能把它们合并起来。
当然,多个目标的生成规则的执行命令是同一个,这可能会给我们带来麻烦,不过好在我们可以使用一个自动化变量$@,这个变量意味着目前规则中所有的目标的集合。
同时定义多个目标

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

等价于:

bigoutput: text.g	generate text.g-big>;bigoutputlittleoutput: text.g 	generate text.g-little>; littleoutput

其中,$(subst output,,$@)表示执行一个Makefile的函数,函数名为subst,后面的为参数。 subst函数是替换字符串的意思,$@表示目标的集合,就像一个数组,$@依次取出目标,并执行命令。

Makefile的命令

Makefile中,make会按顺序一条一条地执行命令,每条命令必须以"Tab"键开头,除非命令是紧跟在依赖规则后面的分号后的。在命令行之间的空格或者空行会被忽略,但是如果该空格或空行是以“Tab”键开头的,make便会认为其是一个空命令。
当依赖目标新于生成目标时,也就是当规则的最终目标需要被更新时,make会一条一条地执行其后的命令。如果要让上一条命令的结果应用在下一条命令,需要使用分号分隔这两条命令。
比如第一条命令是cd命令,希望第二条命令在cd之后的基础上运行,那么就不能把这两条命令写在两行上,而应该把这两条命令写在一行上,并用分号分隔比如下面这段代码:

exec:	cd /home/zhangfan #进入/home/zhangfan目录   	pwd #打印当前目录

执行“make exec”命令后;会进入/home/zhangfan目录,但是pwd命令打印出的结果仍是当前的 Makefile目录,如果改写成下面这个样子:

exec:    cd /home/zhangfan;  pwd#进入/home/zhangfan目录后,打印当前目录

则打印/home/zhangfan

变量

Makefile的变量就像是CC++语言中的宏,代表了一个文本字串,在 Makefile执行的时候会自动地展开在所使用的地方。与C/C++的宏所不同的是, Makefile变量的值是可以改变的。在 Makefile中,变量可以使用在目标、依赖目标、命令或是 Makefile的其他部分中。

变量的基础

变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有“:”、“#”、“=”或是空字符(空格、回车等)。变量是大小写敏感的,“foo”、“Foo”和“FOO”是三个不同的变量名。
变量在声明时需要给予初值,而在使用时,需要在变量名前加上$符号,最好用小括号“()”或是大括号“{}”把变量给包括起来。如果要使用真实的$字符,那么需要用$$来表示。
Makefile中使用变量

objects= program.o foo.o utils.oprogram: $(objects)   cc -o program $(objects) $(objects): defs.h

变量会在使用它的地方精确地展开,就像C/C++中的宏一样:

objects= program.o foo.o utils.oprogram:program. o foo.o utils.o   cc-o programprogram.o foo.o utils.o   program.o foo.o utils.o: defs.h 

赋值变量

除了所示的代码那样,使用=符号给变量赋值外, Makefile还支持变量的嵌套定义,例如:

 foo =$(bar) bar=$(ugh) ugh=Huh? all:    echo $(foo)

执行make all将会输出变量foo的值是“Huh?”

:=

x:= fooy:=$(x) barx:=later#其等价于:y:= foo barx:= later

使用的是“:=”操作符,使得前面的变量不能使用后面的变量,只能使用前面已定义好了的变量。
比如如果是这样:

y:=$(x) barx:=foo

那么,y的值是bar,而不是foo bar。

?=

FOO?=bar

其含义是,如果变量FOO没有被定义过,那么变量FOO的值就是“bar”,如果FOO先前被定义过,那么这条语句将什么也不做,其等价于:

ifeq ($(origin FOO), undefined)   FOO=barendif

这段代码中使用了ifeq条件判断语句和函数调用origin函数返回变量FOO的出处,ifeq则判断两个参数是否相等。

+=

给变量追加值

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

于是,$( objects)值变成:“main.o foo. o bar. o utils.o another.o” ( another.
o被追加进去了),使用“=”操作可以等效为下面的形式

objects=main.o foo.o bar.o utils.o objects:=$(objects) another.o 

所不同的是,用“+=”更为简洁。

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

variable:= value variable += more #等价于variable:=value variable: = $(variable) more 

函数调用

Makefile可以调用函数,从而让Makefie的书写变得更为灵活和简
。两数调用后,返回值可以当做变量来使用,并且函数的调用
也很像变量的使用,也是以“$”来标识的,其语法如下:

$(  )或是:${  }

是函数名,是两数的参数,参数问以逗号隔开,而两数名和参数之问以空格分隔。两数调用以“$”开头,
以圆括号或花括号把函数名和参数括起。
函数中的参数可以使用变量,为了风格的统一,函数和变量括号最好一样.

字符串操作函数

  1. 字符串替换函数 subst
    格式: $(subst ,,)
    功能:把字串中的字符串替换成
    返回:函数返回被替换过后的字符串。

  2. 模式字符串替换函数 patsubst
    格式: $(patsubst , , )
    功能:找中的単词(単词以“空格”、“Tab”、“回车”或换行”分隔)是否符合模式,如果匹配的话,则以
    模式替换。这里,< pattern>可以包括通配符%来表示任意长度的字串。如果中也包含%”,那么,中的这个“%”将是的那个“%”所代表的字串。(可以用""来转义,以%来表示真实含义的“%”字符)
    返回:函数返回被替换过后的字符串。
    例:

    $(patsubst %.C, %.o, x.c.c bar.c)
    

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

  3. 查找字符串函数findstring
    格式:$(findstring ,)
    功能:在字符串中查找字符串。
    返回:如果找到,则返回字符串,否则返回空字符串。
    示例:

    $(findstring a,a b c)$(findstring a,b c)
    

    第一个两数返回“a”字符串,第二个返回””字符串(空字符串)。

  4. 过滤函数filter
    格式:$(filter ,)
    功能:
    模式过滤字符串中的单词,保留符合模式的单词。
    可以有多个模式。
    返回:返回符合模式的字符串。
    示例:

    sources:=foo.c bar.c baz.s ugh.hfoo: $(sources)    cc $(filter %.c %.s,$(sources)) -o foo
    

$(filter %.c %.s,$(sources))返回的值是"Foo.c bar.c baz.s"

  1. 反过滤函数filter-out

    格式:$filter-out ,)
    功能:以模式过滤字符串中的单词,去除符合模式的单词。可以有多个模式。
    返回:返回不符合模式的字串。
    示例:

    objects=main1.o foo.o main2.o bar.o mains=main1.o main2.o$(filter-out $(mains),$(objects))
    

    返回值是“foo.o bar.o”。

  2. 排序函数sort

    格式:$(sort )

    功能:将字符串中的单词按照字母排序(升序)进行排序,先比较单词的首字母,首字母相同,则比较下一个字母,以此类推。当遇到相同的单词时,sort函数会去自动删掉它们。
    返回:返回排序后的字符串。
    示例一:

    $(sort lose foo bar lost)#返回值为:bar foo lose lost
    

    示例二:

    $(sort programing linux c programing)#返回值为:c linux programing
    
    1. 取单词函数word

      格式:$(word ,)

      功能:从字符串中取出第个单词,单词计数从1开始。
      返回:返回字符串中的第个单词。如果值比中的单词数要大,则返回空字符串

      #示例一:$(word 2,programing linux c programing)#返回值是:linux#示例二:$(word 5,programing linux c programing)#返回值为“”(空字符串)。
      
    2. 取单词串函数wordlist

      格式:$(wordlist ,,)
      功能:从字符串中取出从开始到的单词串,是一个数字。
      返回:返回字符串中从的单词字串。如果中的单词数要大,那么返回空字符串。如果大于的单词数,那么返回从开始,到结束的单词串。
      单词计数从1开始。

      #示例一:$(wordlist 2,4,I like linux c programing)#返回值是:like linuxc#示例二:$(wordlist 6,8,I like linux c programing)#返回值为“”(空字符串)。#示例三:$(wordlist 2,8,I like linux c programing)#返回值是:like linux c programing
      
    3. 单词个数统计函数words

      格式:$(words )
      功能:统计字符串中的单词个数,计数从1开始。
      返回:返回中的单词数。
      示例:

      $(words,I like linux c programing)#返回值是“5”。
      
    4. 首单词函数firstword

      格式:$(firstword )
      功能:取字符串中的第一个单词。
      返回:返回字符串的第一个单词。
      示例:

      $(firstword I like linux c programing)#返回值是单词“I”。
      

文件名操作函数

  1. 取目录函数dir
    格式:$(dir )
    功能:从文件名序列(一个或多个文件名)中取出目录部分。目录部分是指最后一个反斜杠“/”之前的部分,如果没有反斜杠,则返回“./”。
    返回:返回文件名序列的目录部分。
    示例:

    $(dir usr/src/linux-2.4/Makefile hello.c)#返回值是“usr/src/linux-2.4 ./”。
    
  2. 取文件名函数notdir
    格式:$(notdir )
    功能:从文件名序列(一个或多个文件名)中取出非目录部分。非目录部分是指最后一个反斜杠"/”之后的部分,即文件名。
    返回:返回文件名序列的非目录部分。
    示例:

    $(notdir usr/src/linux-2.4/Makefile hello.c)#返回值是“Makefile hello.c”。
    
  3. 取后缀名函数sufix
    格式:$(suffix
    功能:从文件名序列中取出各个文件名的后缀名。
    返回:返回文件名序列的后缀名序列,如果文件没有后缀名,则返回空字符串。
    示例:

    $(suffix usr/src/linux-2.4/Makefile hello.c foo.s)#返回值是“.c .s”(第一个为空字符)。
    
  4. 名称取前缀函数basename
    格式:$(basename )
    功能:从文件名序列中取出各个文件名的前缀部分。
    返回:返回文件名序列的前缀序列,如果文件没有前缀,则返回空字符串。
    示例:

    $(basename usr/src/linux-2.4/kernel/exit.c hello.o home/hacks)#返回值是“usr/src/linux-2.4/kernel/exit hello home/hacks"。
    
  5. 加后缀函数addsuffix
    格式:$(addsuffix ,)
    功能:把后缀加到中的每个单词后面。
    返回:返回已加后缀的文件名序列。
    示例:

    $(addsuffix .c,foo bar hello)#返回值是“foo.c bar.c hello.c”。
    
  6. 加前缀函数addprefix
    格式:$(addprefix ,)
    功能:把前缀加到中的每个单词前面。
    返回:返回加过前缀的文件名序列。
    示例:

    $(addprefix usr/src/linux-2.4/kernel/,exit.c time.c)#返回值是“usr/src/linux-2.4/kernel/exit.c usr/src/linux-2.4/kernel/time.c”。
    

循环函数

foreach函数是用来做循环用的,Makefile中的foreach函数几乎是仿照Unix标准Shell(/bin/sh)中的for语句,或是C-Shell(/bin/csh)中的foreach语句而构建的。它的语法是:

$(foreach ,,)

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

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

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

条件判断函数

下面要介绍的if函数很像make所支持的条件判断语句,if函数的语法是:

$(if ,)#或是:$(if ,,)

可见,if函数可以包含else部分,也可以不含。即if函数的参数可以是两个,也可以是三个。参数是if的表达式,如果其返回的为非空字符串,那么这个表达式就相当于返回真,于是,会被计算,否则会被计算。
而if函数的返回值是,如果为真(非空字符串),那么会是整个函数的返回值,如果为假(空字符串),那么会是整个函数的返回值,此时如果没有被定义,整个函数返回空字符串。所以,只会有一个被执行。

条件判断语句

关键字 典型表达式 含义
ifeq ifeq(,) 比较参数arg1和arg2的值是否相等,相等时表达式为真。
ifneq ifneq(,) 比较参数arg1和arg2的值是否相等,不相等时表达式为真
ifdef ifdef 如果变量的值非空,则表达式为真。可以是一个函数的返回值。ifdef 只是测试一个变量是否有值,其并不会把变量扩展到当前位置。
ifndef ifndef 如果变量的值为空,则表达式为真。

隐式规则

隐式规则即Makefile预先约定好了的,不用再写出来的规则。比如把.c文件编译成.o文件这一规则,不用写出来,make便会自动推导,并生成我们需要的.o文件。隐式规则会使用一些系统变量,可以改变这些系统变量的值来定制隐式规则运行时的参数。如系统变量“CFLAGS”可以控制编译时的编译器参数。还可以通过模式规则的方式自定义隐式规则。

隐式规则举例

如果要使用隐式规则来生成目标,就不需耍写出这个目标的规则,make会试图去自动推导产生这个目标的规则和命令,如果make可以自动推导生成这个目标的规则和命令,那么这个行为就是隐式规则的自动推导。当然,隐式规则是make事先约定好的一些东西。例如,有下面的Makefile文件:

foo:foo.o bar.o 
cc -o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)

可以看到,这个Makefile中并没有写下如何生成foo.o和bar.o这两目标的规则和命令。因为make的隐式规则功能会自动去推导这两个目标的依赖目标和生成命令。也就是说,完全没有必要写出下面的两条规则:

foo.o:foo.c 
	cc -c foo.c $(CFLAGS)
bar.o:bar.c 
	cc -c bar.c $(CFLAGS)

因为这已经是“约定”好了的事,make使用约定好的C编译器"cc"生成.o文件,这就是隐式规则。

隐式规则中的变量

在隐式规则的命令中,基本上都是使用了一些预先设置的变量。可以在Makefile中改变这些变量的值,或是在make的命令行中传入这此值,或是在环境变量中设置这些值,无论怎么样,只要设置了这此特定的变量,那么它们就会对隐式规则起作用。当然,也可以利用make的“-R”或“–no-builtin-variables”参数来取消所定义的变量对隐式规则的作用。
例如,第一条隐式规则——编译C程序的隐式规则的命令是

$(CC) -c $(CFLAGS) $(CPPFLAGS)

Make默认的编译命令是“cc”,如果你把变量$(CC)重定义成“gcc”,把变量$(CFLAGS)重定义成“-g”,那么,隐式规则中的命令全部会以gcc-c-g $(CPPFLAGS)的样子来执行了。

-g可执行程序包含调试信息
-o指定输出文件名
-c只编译不链接

可以把隐式规则中使用的变量分成两种:一种是命令相关的,如
“CC”;一种是参数相关的,如“CFLAGS”。

变量 含义
AR 函数库打包程序,默认命令是“ar"
AS 汇编语言编译程序,默认命令是“as”
CC C语言编译程序。默认命令是“cc”
CXX C++语言编译程序。默认命令是“g++”
CO 从RCS文件中扩展文件程序。默认命令是“co”
CPP c程序的预处理器(输出是标准输出设备)。默认命令是$(CC) -E
FC Fortran和Ratfor的编译器和预处理程序。默认命令是“f77”
GET 从SCCS文件中扩展文件的程序。默认命令是“get”
LEX Lex方法分析器程序(针对于C或Ratfor)。默认命令是“lex”
PC Pascal语言编译程序。默认命令是“pc”
YACC Yacc文法分析器(针对于C程序)。默认命令是“yacc”
YACCR Yacc文法分析器(针对于Ratfor程序)。默认命令是“yacc -r”
MAKEINFO 转换Texinfo源文件(.texi)到Info文件程序。默认命令是“makeinfo”
TEXI2DVI 从Texinfo 源文件创建TeXDVI文件的程序。默认命令是“texi2dvi”
TANGLE 转换Web到Pascal语言的程序。默认命令是“tangle”
CTANGLE 转换C Web到C。默认命令是“ctangle”
RM 删除文件命令。默认命令是“rm -f”

使用模式规则

可以使用模式规则来定义一个隐式规则。模式规则与一般的规则类似,只是在模式规则中,目标的定义需要有“%”字符。“%”定义对文件名的匹配,表示任意长度的非空字符串。在依赖目标中同样可以使用“%”,只是依赖目标中“%”的取值,取决于其目标。

模式规则举例

模式规则中,至少在规则的目标中要包含“%”符号。例如:“%.C"表示以.c结尾的文件名(文件名的长度至少为3),“s.%.C”表示以s.开头,以.c结尾的文件名(文件名的长度至少为5)。如果“%”定义在目标中,那么目标中
“%”的值决定了依赖目标中的”%”的值,也就是说,目标中的模式的”%”决定了依赖目标中”%”的样子。例如有一个模式规则如下:

%.o:%.c;

其含义是,指出了从所有的.c文件生成相应的.o文件的规则。如果要生成的目标是“a.ob.o”,那么“%c”就是“a.c b.c”。
一旦依赖目标中的“%”模式被确定,make便会被要求去匹配当前目录下所有的文件名,一旦找到,make就会执行规则下的命令,所以在模式规则中,目标可能会是多个的,如果有模式匹配出多个目标,make就会产生所有的模式目标,此时,make关心的是依赖的文件名和生成目标的命令这两件事。

自动化变量

在上述的模式规则中,目标和依赖文件都是一系例的文件,那么如何书写一个命令来完成从不同的依赖文件生成相应的目标?因为在每一次对模式规则的解析时,都会是不同的目标和依赖文件。自动化变量就是完成这个功能的。

所谓自动化变量,就是这种变量会把模式中所定义的一系列的文件自动地逐个取出,直至所有的符合模式的文件都取完了。这种自动化变量只应出现在规则的命令中。

Makefile中共有21个自动化变量,后面的14个变量只是在前面7个变量后分别加上了字母"D"和"F",字母"D"代表Directory,就是目录,“F"代表File,就是文件。

另外,对于变量$<$,为了避免产生不必要的麻烦,最好在后面的那个特定字符加上圆括号,比如$(<)”就要比$<要好一些。

变量 含义
$@ 表示规则中的所有目标文件的集合。在模式规则中如果有多个目标,$@就是匹配于目标中模式定义的集合。
$% 表示规则中的目标文件中的函数库名,如果目标不是函数库文件(Unix下是a,Windows下是lib),其值为空。
$< 依赖中的第一个依赖文件的名字,如果依赖是以模式(即“%”)定义的,则$<是符合模式的一系列的文件集。
$? 所有比目标新的依赖的集合,以空格分隔。
$^ 所有的依赖的集合,以空格分隔。如果在依赖中有多个重复的,则自动去除重复的依赖,只保留一份。
$+ $^,也是所有依赖的集合,只是它不去除重复的依赖
$* 目标模式中“%”及其之前的部分。
$(@D) $@的目录部分(不以斜杠作为结尾),如果$@中没有包含斜杠,其值为.(当前目录)。
$(@F) $@的文件部分,相当干函数$(notdir $@)
$(*D) $*文件的目录部分。
$*F $*文件部分,但不取后缀名。
$(%D) 函数库文件成员的目录部分。
$(%F) 函数库文件成员的文件名部分。
$( 依赖中的第一个目标的目录部分。
$( 依赖中的第一个目标的文件名部分。
$(^D) 所有依赖文件中的目录部分(无相同的)
$(^F) 所有依赖文件中的文件名部分(无相同的)
$(+D) 所有依赖文件中的目录部分(可以有相同的)
$(+F) 所有依赖文件中的文件名部分(可以有相同的)
$(?D) 所有被更新文件的目录部分
$(?F) 所有被更新文件的文件名部分

你可能感兴趣的:(计算机,C/C++,linux,gnu)