规则:

目标 : 依靠

命令

make是怎样工作的:

(1)make在当前文件夹下寻找makefile或Makefile。

(2)假设找到,他会寻找文件里的第一个目标文件(target)。并把这个文件作为第一个目标。

(3)假设目标文件不存在,或者目标文件所依赖的.o文件改动时间要比目标文件新,那么。就会运行后面所定义的命令来生成目标文件。

(4)假设目标文件所以依赖的.o文件也存在。那么make会在当前文件里寻找目标为.o文件所依赖性,假设找到则再依据这一规则生成.o文件。

(这有些像堆栈的过程。)

(5)于是make生成.o文件,然后再用.o文件生成make的终极目标。

这就是make的依赖性,make会一层又一层的去找文件的依赖关系。直到终于编译出第一个目标文件。

在寻找的过程中,假设出现错误,比方最后被依赖的文件找不到。那么make就会直接退出,并报错,而对于所定义的命令的错误。或者编译不成功。make根本就不理会。make仅仅会管文件的依赖性。

所以。像clean那样,没有被第一个目标文件直接或者间接关联。那么他后面所定义的命令就不会被自己主动运行,只是。我们能够显式的要make运行---make clean

Makefile的 组成:

(1)显示规则

说明怎样生成一个或多个目标文件,须要作者明白指出要生成的文件、文件的依赖文件,生成的命令。

(2)隐晦规则

由于make有自己主动推到功能,所以隐晦的规则能够让我们比較粗糙简略的书写Makefile。

(3)变量的定义

Makefile中我们须要定义一些变量,变量一般都是字符串。这个有点像C语言中的宏,当Makefile被运行时,变量会自己主动扩展到对应的引用位置。

(4)文件指示

包括三部分:

a.在一个Makefile中引用还有一个Makefile。像C语言中的include。

b.依据某些情况制定Makefile中的有小部分。像C语言中的预编译#if。

c.定义一个多行的命令

(5)凝视

Makefile中仅仅有行凝视。用#字符,假设在Makefile要使用#,则使用反斜杠进行转义。比如"\#" 。

GNU的make工作步骤:(其它的make类似)

(1)读入全部的Makefile

(2)读入被include的其它Makefile

(3)初始化文件里的变量

(4)推导隐晦规则,并分析全部规则

(5)为全部的目标文件创建依赖关系链

(6)依据依赖关系。决定哪些目标要又一次生成

(7)运行生成命令

当中1~5属于第一阶段,6~7属于第二阶段。

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

在规则中使用通配符:

假设想定义一系列比較类似的文件,则通配符是较好的选择。make支持的通配符有三种。

(1) *

比如*.c表示后缀为c的文件,须要注意的是。假设文件名称中有通配符,如*,则能够使用转义字符\*来表示真实的*字符。

(2) ?

(3) [..]

文件搜索:

大的project中,有大量的源文件。我们通常的做法是把这些源文件分类,病存放在不同的文件夹中。

所以,当make须要去找文件的依赖关系式。能够再文件前加上路径名,但有一个最好的方法就是把一个路径告诉make。让make自己去找。

Makefile中的特殊变量VPATH就是完毕这任务的。假设没有知名这个变量。make仅仅会在当前的文件夹中寻找依赖关系和目标文件。假设定义了这个变量,那么make就会在当前文件夹找不到的情况下,去指定的文件夹去寻找文件了。

VPATH
= src:../headers

指明了两个文件夹。src和../headers,make会依照这个顺序去搜索。文件夹“冒号”分开(可是,当前文件夹永远都是最高优先搜索的地方)。

另外一个设置文件搜索路径的方法就是使用make的keywordvpath(注意是小写)。这个不是变量。这是一个makekeyword,这和上面的VPATH变量类似,可是它更加灵活。他能够指定不同的文件在不同的搜索文件夹中。这是一个灵活的功能,用法由三种:

(1)vpath  <pattern>  <directories>

为符合模式<pattern>的文件指定搜索文件夹<directories>。

(2)vpath <pattern>

清除符合模式<pattern>的文件的搜索文件夹。

(3)vpath

清除全部被设置好了的文件搜素文件夹。

vpath是哦那个方法中的pattern须要包括%字符。

%字符意思是匹配零或者若干字符,比如,%.h表示全部以.h结尾的文件。<pattern>指定了要搜素的文件集,而<directories>则指定了<pattern>的文件的搜索文件夹。

比如:vpath %.h    ../headers表示make在../headers文件夹下搜索全部的.h结尾的文件。

(假设某些文件在当前文件夹没有找到的话)

伪目标:

一般的Makefile最后的"clean"的目标,就是一个伪目标。

像前面所说的那样,既然我们生成了很多编译文件,我们也须要一个清楚他们的“目标”以备完整的又一次编译而用。

我们并非生成"clean"这个文件。“伪目标”并非一个文件,仅仅是一个标签,因为“伪目标”不是文件,所以make无法为生产它的依赖关系和决定它是否要运行,我们仅仅有通过显式的指明须要生成这个目标,才会使其生效。当然,“伪目标”的取名不能和文件名称重名,不然其就失去了“伪目标”的意义。

当然,为了更好的避免和文件重名,我们一般使用一个特殊的标记".PHONY"来指明一个目标是“伪目标”。向make说明,不论是否有这个文件。这个目标就是“伪目标”

1
2
3
4
5
.PHONY:clean
 
clean:
 
rm *.o 
temp

静态模式:

静态模式能够更加easy定义多目标的规则。语法为:

1
2
3
4
5
<target...>:<target-pattern>:<prereq-patterns...>
 
<command>
 
......

target定义了一系列的目标文件,能够有通配符。

是一个目标的集合。

target-pattern是指明了targets的模式,也就是目标集模式。

prereq-patterns是目标文件的依赖模式,它对target-pattern形成的模式在进行一次依赖目标的定义。比如:<target-pattern>定义成%.c,意思是我们的<target>集合中都是以".o"结尾的,二假设我们的<prereq-patterns>定义成为"%.c",意思是对<target-pattern>所形成的目标集进行二次定义。其计算方法是:取<target-pattern>模式的"%“(也就是去掉[.o]这个结尾),并为其加上[.c]这个结尾。形成新的集合。

所以,”目标模式“或是”依赖模式“都能够有"%"这个字符。假设文件名称中有"%",那么能够使用"\%"表示。

比如:

1
2
3
4
5
6
7
objects
= foo.o bar.o
 
all
= $(objects)
 
$(objects):%.o:%.c
 
$(cc)
-c $(CFLAGS) $< -o $@

指明了我们的目标从$(objects)中获取,"%.o”表明要全部以".o"结尾的目标,也就是"foo.o bar.o",也就是变量$object集合的模式,而依赖模式"%.c"则驱魔师"%.o"的"%",也就是"foo bar ",并为其加上".c"后缀。于是,我们的依赖目标就是"foo.c
bar.c"。

而命令中的"$<"和"$@"都是自己主动化变量。"S<"表示全部的依赖目标集(也就是"foo.c  bar.c")。"$@"则表示目标集(也就是"foo.o  bar.o")。

于是上面的规则展开后就是:

1
2
3
4
5
6
7
foo.o 
:  foo.cc
 
$(cc) 
-c  $(CFLAGS)  foo.c  -0  foo.o
 
bar.o
: bar.c
 
$(cc)
-c   $(CFLAGS)   bar.c  -o  bar.c

假设"%.o"有几百个。那么这样的简单的"静态模式规则"能够写完一大堆的规则,效率非常高。

命令出错:

每当命令运行完后,make会检查每个命令的返回码。假设命令返回成功,那么make会运行下一条命令。假设一个规则中的某一个命令错了(命令退出码非零)。那么make就会终止当前的规则,将有可能终止全部的规则的运行。

有时候,命令的出错并不代表就是错误的,比如mkdir。假设当前文件夹不存在须要创建的文件,就会成功执行。可是假设。当前文件夹中有一个这种同名文件,那么就会出错二终止规则的执行。为了忽略出错,我们能够再Makefile的命令行前面加一个减号“-”(在TAB键之后),标记为无论命令是否出错都觉得是成功的。

比如:

1
2
3
clean:
 
-rm -f
*.o

以下定义一个变量。其值为空格:

1
2
3
nullstring
:=
 
space
:= $(nullstring) 
#end
of line

nullstring是一个Empty变量,当中什么也没有,而我们的space的值是一个空格。由于在操作符右边非常难描写叙述一个空格。这里採用的技术非常管用,先用一个Empty变量来标明变量的值開始了,而后採用“#”凝视符来表明变量的定义的终止。

这样。我们就能够定义出其值为空格的变量。

显式命令:

通常,make会把要运行的命令显示在屏幕,当使用"@"字符在命令行前,那么这个命令就不会别显式出来。

比如:

echo 正在编译这个模块...

运行效果为,屏幕显示:

echo 正在编译这个模块...

正在编译这个模块...

但。使用"@。屏幕显示:

正在编译这个模块...

命令运行:

要注意,须要在上一条命令的结果须要作用于下一条命令。应该使用分好隔离这两条命令。比如:

1
2
3
cd  /home/yangrui/study
 
pwd

而使用";"。即:

1
cd /home/yangrui/study  pwd

当中,运行make exec时,第一个样例中的cd没有作用。pwd会打印出当前Makefile的文件夹,而第二个cd 就会起作用。pwd会打印出"/home/yangrui/study"

定义命令包:

假设Makefile中出现一些同样的命令序列。那么我们能够为这些同样的序列定义一个变量。定义这样的命令序列以"define"開始,以"endef"结束。

比如:

1
2
3
4
5
define
run-yacc
 
run-yacc 
$(firstword s^)
 
mv y.tab.c 
$@

这里的run-yacd就是这个命令报的名字。其不能够和Makfile中的变量重名。(类似于C语言的函数)

变量:

(1)变量基础

变量在声明时须要赋初值,而在使用时须要在变量名之前加上"$",但最好用()或者{}包含起来,若须要使用真正的‘$'字符,则须要使用“$$"。

比如:

1
2
3
4
5
6
7
objects
= program.o   foo.o   utils.objects
 
program
: $(objects)
 
cc
-o program $(objects)
 
$(objects)
: defs.h

变量会在使用的地方展开,像c/c++中的宏。

比如:

1
2
3
4
5
foo
= c
 
prog.o
: prog.$(foo)
 
$(foo)$(foo) 
-$(foo)  prog.$(foo)

展开后成为:

1
2
3
prog.o 
: prog.cc
 
cc
-c prog.c

(可是一般不会像这样去做)

(2)变量中的变量

定义变量时。还能够使用其它变量来够着变量的值。在Makefile中有两种定义变量值得方法:

<1>使用"="

"=“左边是变量,右边是其它变量。且右側的其它变量也试试文件里的不论什么一处,也就是说。右側的值不一定是已经定义好的值 。也能够使用后面定义的值。

比如:

1
2
3
4
5
6
7
8
9
foo
= $(bar)
 
bar
= $(ugh)
 
ugh
= Huh?

 
all:
 
echo :
$(foo)

则使用make all时,结果为"Huh?"

这样的方法有优点也有坏处,比方递归定义时:

1
2
3
a
= $(b)
 
b
= $(a)

这样,会让make嵌入无限的变量展开过程中去,当然,make是有能力检查这样的错误并报错的,可是效率太低。

<2>使用":="

这样的方法中变量的值不能使用后面的变量。仅仅能使用前面已经定义好的变量。

比如:

1
2
3
y
:=  $(x)  bar
 
x
:= foo

则,y的值为”bar“。而不是”foo
 bar“.

技巧:怎样定义一个值为空格的变量

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

nullstring是一个Empty变量,当中什么都没有。而我们space值为空格。由于在操作符右边非常难去描写叙述一个空格,这里的技术非常管用。先使用一个Empty的变量来表明变量的開始,而后面採用"#"凝视来表明变量定义的终止,这样,我们就能够定义出一个值为空格的变量。

(3)变量的高级使用方法

<1>变量值的替换

a.能够替换变量中共同拥有的部分。

格式为

$(var:a=b) 或者 ${var:a=b}

意思是吧变量"var"中所有以"a"字符结“结尾”的“a"所有替换为“b”字串。这里“结尾”的意思是“空格”或者“结束符”。

比如:

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

这时候$(bar)的值就是“a.c  b.c  c.c”

b.第二种变量替换的技术以"静态模式"定义。

比如:

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

这个样例让$(bar)变量的值为"a.c b.c c.c"。

<2>把变量的值在当变量

比如:

1
2
3
4
5
x
= y
 
y
= z
 
a:=$($(x))

这里,$(a)的值就是"z"。

这样的方式中,还能够使用多个变量组成一个变量的名字,然后取值:

比如:

1
2
3
4
5
6
7
first_second
= hello
 
a
= first
 
b
= second
 
all=$($a_$b)

这里。$(all)就是"hello"。

追加变量值:

能够使用"+="操作符给变量追加值。

override指示符:

假设有变量是通过make的命令行參数设置的。那么Makefile中对这个变量的赋值会被忽略。假设想在Makefile中设置这类參数,那么,能够使用"override"指示符,语法是:

override <variable> = <value>

override <variable> := <value>

当然,还能够追加:

override <variable> +=<more text>

对于多行的命令符。我们使用define指示符。在define指示符前,也相同能够使用override指示符。比如:

1
2
3
4
5
override 
define foo
 
bar
 
endef

多行变量:

使用definekeyword设置变量的值能够换行。这有利于一系列的命令(前面的"命令包"就利用这样的keyword)。

define指示符后面跟的是变量的名字,重起一行是变量的值。定义以endefkeyword结束。

工作方式和"="同样。

比如:

1
2
3
4
5
6
7
define
two-lines
 
echo foo
 
echo $(bar)
 
endef

环境变量:

make执行时的系统环境变量能够再make開始执行时被加载到Makefile中,可是假设Makefile中已经定义了这个变量,或者这个变量由make命令带入,那么系统的环境变量的值就会被覆盖。(假设make指定了"-e"參数,那么系统环境变量将会覆盖Makefile中定义的变量)。

因此,假设我们在环境变量中设置了"CFLAGS"环境变量。那么我们就能够在全部的Makefile中使用这个变量了。

这对于我们使用统一的编译參数有较大的优点。假设Makefile中定义了CFLAGS,那么则会使用Makfile中的变量,假设未定义则使用系统环境变量的值,一个共性和个性的统一,像"全局变量"和"局部变量"的特性。

当make嵌套使用时,上层的Makfile定义的变量会以系统环境变量的方式传递到下层的Makefile中。当然。在默认情况下,仅仅有通过命令行设置的变量才会被传递。而定义在文件里的变量,假设要向下层Makefile传递。则须要使用exportkeyword。

注意:不建议把环境变量定义在系统环境中。

目标变量:

前面所说的都是"全局变量",在整个文件里,我们都能够訪问这些变量。当然"自己主动化变量"除外。如"$<"这样的自己主动化变量就属于"规则性变量"。这样的变量的值依赖于规则的目标和依赖目标的定义。当然,也能够为某个目标设置局部变量,这样的变量被称为"Target-specific
Variable".他能够和全局变量同名,由于他的作用于就是这个规则以及连带的规则,所以仅仅在作用范围内有效,而不会影响到规则链以外的全局变量的值。

语法是:

<target ...> : <variable-assignment>

<target ...> : overirde<variable-assignment>

<variable-assignment>能够使前面提到的各种赋值表达式,如"="、":="、"+="或者"?

="。第二个语法是针对make命令带入的变量,或者是系统环境变量。这个特性很实用。当我们设置这样一个变量,这个变量会作用到由这个目标引发的全部的规则中去。

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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

这个样例中,无论全局的"CFLAGS"值是什么,在prog目标。以及其所引发的全部规则中(prog.o  foo.o  bar.o 的规则中)。$(CFLAGS)的值都是"-g"。

模式变量:

在GNU的make中,还支持模式变量(Pattern-specific  Variable)。通过上面的目标变量,我们知道,变量还能够定义在目标上。模式变量的优点是:我们能够定义一种"模式",然后把变量定义在符合这样的模式的全部目标上。

我们知道,make的"模式"通常是至少含有一个"%"的。所以,能够例如以下的方式给全部以[.o]结尾的目标定义目标变量:

%.o : CFLAGS -o

相同的,模式变量的语法和"目标变量"一样:

<pattern ...> : <variable-assignment>

<pattern ...> : overirde <variable-assignment>

overirde相同是针对系统环境传入的变量,或者make命令行指定的变量。

使用条件推断:

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
libs_for_gcc
= -l gnu
 
normal_libs
=
 
 
 
foo
: $( objects)
 
ifeq($(cc)
, gcc)
 
$(cc)
-o  $(objects)  $(libs_for_gnu)
 
else
 
$(cc)
-o  $(objects)  $(normal-libs)
 
endif

含义:推断是否使用"gcc"。假设是。则使用"GUN"函数编译目标。

上面的样例中有三个keyword:ifeq  else   endif 。

上面的样例还能够写的更加简洁些:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
libs_for_gcc
= -l gnu
 
normal_libs
=
 
 
 
ifeq($(cc)
, gcc)
 
libs
= $(libs_for_gcc)
 
else
 
libs
= $(normal_libs)
 
endif
 
 
 
foo:
$(objects)
 
$(cc)
-o  foo $(objects)  $(libs)

语法:

<1>

<condiitonal-directive>

<text-if-true>

endif

<2>

<condiitonal-directive>

<text-if-true>

else

<text-if-false>

endif

这里的<condiitonal-directive>表示keyword共同拥有四个:

<1>keywordifeq

ifeq   (<arg1> , <arg2>)

ifeq   ‘<arg1>’ , '<arg2>'

ifeq   ”<arg1>“ , "<arg2>"

ifeq   "<arg1>" , '<arg2>'

ifeq   '<arg1>' , "<arg2>"

<2>keywordifneq

ifneq   (<arg1> , <arg2>)

ifneq   ‘<arg1>’ , '<arg2>'

ifneq   ”<arg1>“ , "<arg2>"

ifneq   "<arg1>" , '<arg2>'

ifneq   '<arg1>' , "<arg2>"

<3>keywordifdef

<4>keywordifndef

当中,<condiitonal-directive>这一行,多余的空格是同意的,可是不能以[TAB]作为開始,否则就会被觉得是命令。

注意:make实在读取Makefile时就会计算条件表达式的值。并依据表达式的值来选择语句。所以,你最好不要把自己主动化变量(如"$@"等)放入条件表达式,由于自己主动化变量是在执行时才有的。

使用函数:

(1)函数的调用语法

$(<function>  <arguments>)或者${<function>  <arguments>}

这里。<function>就是函数名,make支持的函数不多.<arguments>就是函数的參数。參数间以逗号","分隔,而函数名和參数之间以“空格”分隔。函数调用以"$"开头。

(2)字符串处理函数

<1>subst

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

名称:字符串替换函数----subst

功能:把字符串<text>中的<from>字符串替换为<to>

返回:函数返回替换过来的字符串

样例:

1
$(subst
ee,EE,feet  on  the street)

返回的结果就是"fEEt
 on  the strEEt"

<2>patsubst

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

名称:模式字符串替换函数----patsubst

功能:查找<text>中的单词(单词以“空格”、“TAB”或者“回车”、“换行”分隔)是否符合模式<pattern>,假设匹配,则以<replacement>替换。

这里的<pattern>能够使通配符"%"。表示随意长度的字串,假设<replacement>中冶包括"%",那么,<replacement>中的这个"%"将是<pattern>里的"%"所代表的字串。

(也能够使用"'\"来转义,用"\%"表示)。

返回:被替代过后的字符串。

样例:

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

返回结果是:"x.c.o
 bar.o"

这个前面提到的“变量”章节有关知识非常相似:

$(var:<pattern> = <replacement>)

相当于:

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

<3>strip

$(strip <string>)

名称:去空格函数----strip

功能:去掉<string>字串中开头和结尾的空字符。

返回:被去掉空格的字符串值。

样例:

1
$(strip  
a  b   c  )

返回结果是:"a b c"

<4>findstring

$(findstring <find>,<in>)

名称:在字串<in>中查找<find>字串。

样例:

1
2
3
$(findstring 
a,a b c)
 
$(findstring 
a ,b c)

返回结果。第一个返回"a"字符串。第二个返回“”字符串(空字符)。

<5>filter

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

名称:过滤函数----filter

功能:以<pattern>模式过滤<text>字符串的单词,保留符合模式<pattern>的单词。

能够有多个模式。

返回:符合模式<pattern>的字串。

样例:

1
2
3
4
5
sources
:= foo.c  bar.c  baz.s  ugh.h
 
foo
: $(sources)
 
cc
$(filter %.c %.s , $(sources))  -o  foo

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

<6>filter-out

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

名称:反过滤函数----filter-out

功能:以<pattern>模式过滤<teext>字串中的单词,去除符合模式<pattern>的单词。能够有多个模式。

返回:不符合模式<pattern>的字串

样例:

1
2
3
4
5
objects
= main1.o  foo.o  main2.o  bar.o
 
mains
= main1.o  main2.o
 
$(filter-out-out 
$(mains),$(objects))

则返回值为"foo.o  bar.o"

<7>sort

$(sort<list>)

名称:排序函数----sort

功能:给字符串<list>中的单词排序(升序)

返回:排序后的字符串

样例:

1
$(sort  foo
bar  lose)

返回结果为: "bar  foo  lose"

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

<8>word

$(word  <n>,<text>)

名称:取单词函数----word

功能:取字符串<text>中第n个单词。(从第一个開始)

返回:字符串<text>的第<n>个单词。

假设<n>比<text>中的单词数要大,那么返回空字符串。

样例:

1
$(word
2 , foo bar  baz )

返回:"bar"

<9>wordlist

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

名称:取单词函数----worklist

功能:从字符串<text>中取从<s>開始到<e>的单词,<s>和<e>都是数字。

返回:返回字符串<text>中从<s>到<e>的单词。假设<s>比<text>的单词数要打,那么返回空字符串,假设<e>大于<text>的单词数。那么返回<s>開始,到<text>结束的单词。

样例:

1
$(wordlist
2,3  ,foo bar  baz)

返回:"bar  baz"

<10>works

$(words <text>)

名称:单词个数统计函数----words

功能:统计<text>中字符串的单词个数

返回:<text>中的单词数

样例:

1
$(words
,foo bar  baz)

返回值是"3".

备注:假设我们要提取<text>最后一个单词。能够这样:

$(word $(words<text>),<text>)

<11>firstword

$(firstword <text>)

名称:首单词函数----firstword

功能:取字符串<text>的第一个单词。

返回:字符串<text>的第一个单词。

样例:

1
$(firstword 
foo bar )

返回值:"foo"。

备注:这个函数还能够用word实现:

$(word  1,<text>)

文件名称操作函数:

<1>dir

$(dir  <names...>)

名称:取文件夹函数----dir

功能:从文件名称序列<name>中取出文件夹部分。

文件夹部分是值最后一个反斜杠"/"之前的部分。假设没有反斜杠。就返回"./"。

返回:返回文件名称序列<name>的文件夹部分。

样例:

1
$(dir  src/soo.c 
hacks)

返回值:"src/   ./"

<2>notdir

$(notdir  <names...>)

名称:取文件函数----notdir

功能:从文件名称序列<names>中取出非文件夹部分。非文件夹部分是指最后一个反斜杠“/”之后的部分。

返回:文件名称序列<names>的非文件夹部分。

样例:

1
$(notdir 
src
/foo.c 
hacks )

返回:"foo.c  hacks"

<3>suffix

$(suffix <names>)

名称:取后缀函数----suffix

功能:从文件名称序列<names>中取出各个文件名称的后缀。

返回:文件名称序列<names>的后缀序列,假设文件没有后缀,则返回空字符。

样例:

1
$(suffix 
src
/foo.c 
src-1.0
/bar.c 
hacks)

返回值:".c  .c "

<4>basename

$(basename <names...>)

名称:取前缀函数----basename

功能:从文件名称序列<names>中取出各个文件名称的前缀部分。

返回:文件名称序列<names>的前缀序列,假设文件没有前缀,则返回空字符。

样例:

1
$(basename  src/foo.c 
src-1.0
/bar.c 
hacks)

返回值:"src/foo   src-1.0/bar  hacks"。

<5>addsuffix

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

名称:加前缀函数----addsuffix

返回:加过后缀的文件名称序列。

样例:

1
$(addsuffix 
.c , foo  bar)

返回值:“foo.c  bar.c ”

<6>addprefix

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

名称:加前缀函数----addprefix

功能:把前缀<prefix>加在<names>的每一个单词后面。

返回:加过前缀的文件名称序列。

样例:

1
$(addprefix
src/,foo  bar)

返回:"src/foo  src/bar "

<7>join

$(join  <list1> , <list2>)

名称:连接函数

功能:把<list2>中的单词相应的增加到<list1>的单词的后面。

假设<list1>的单词比<list2>要多,那么<list1>中多出来的单词将保持原样。

假设<list2>的单词比<list1>多,则<list2>多出来的单词将会被拷贝到<list2>中。

样例:

1
$(join  aaa 
bbb  ,111  222  333)

返回:"aaa111 bbb222  333"

foreach函数:

foreach函数和别的函数很不一样。由于这个函数是用来做循环的,Makefile中的foreach函数差点儿是仿照Unix的标准Shell(/bin/sh)中的for语句,或者是C-Shell(/bin/csh)中的foreach语句而构建的。他的语法是:

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

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

所以,<var>最好是一个变量名。<list>能够是一个表达式,而<text>通常会使用<var>这个參数来枚举<list>中的单词。

样例:

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

则files的值为"a.o  b.o  c.o  d.o"

注意:foreach的<var>參数是一个暂时的局部变量。foreach函数运行完后,參数<var>的变量将不再作用,其作用域仅仅在foreach函数中。

if函数:

if函数很像GNU的make所支持的条件语句----ifeq。其语法是:

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

或者:

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

返回值:假设<condition>为真,(非空字符串),那么<then-part>会是整个函数的返回值。假设<condition>位假。(空字符串)。那么<else-part>会是整个函数的返回值,此时假设<else-part>没有被定义。则整个函数返回空字串。

call函数:

call函数是唯一一个能够用来创建新的參数化的函数。能够写一个复杂的表达式,在表达式中。能够定义非常多參数。然后你能够用call函数向这个表达式传递參数,语法:

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

当make运行这个函数时,<expression>參数中的变量。如$(1),$(2),$(3)等,会被參数<的返回值就是call函数的返回值。

样例:

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

那么foo的值就是"a b"当然,參数的次序能够自有定义。不一定是顺序的。

样例:

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

那么,foo的值就是"b  a"。

origin函数:

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

$(origin <variable>)

注意:<variable>是变量的名字。不应该是引用。

最好不要在<variable>中使用"$"字符。origin会以返回值的方式告诉你这个变量的"出生情况"。有一下情况:

(1)“undefined”

就是没有定义的变量。

(2)“default”

变量<variable>是一个默认的值。比方"CC"这个变量。

(3)"file"

假设变量<variable>被定义在Makefile中。

(4)"command line"

假设变量<variable>被命令行定义。

(5)“override”

假设<variable>是被override指示符又一次定义的。

(6)“automatic”

假设<variable>是一个命令执行中的自己主动变量。

shell函数:

shell函数不像其它函数。他的參数是操作系统的Shell命令。它和反引號" ‘ "是同样的功能。

样例:

1
2
3
contents
:= $(shell  
cat foo)
 
files
:= $(shell  eccho *.c)

注意。这个函数会生成还有一个shell程序运行命令,所以必须注意其运行性能。假设你的Makefile中有一个复杂的规则,并大量利用了这个函数。那么对你的系统是有害的。特别是Makefile的隐晦规则可能让你的shell函数运行次数比你想象的要多。

控制Makefile的函数:

make提供了一下函数控制make的执行。通常,你须要检測一些执行Makefile时的执行信息。而且依据这个信息来决定,你是让make继续执行,还是停止。

(1)error

$(error <text.....>)

产生一个致命错误,<text...>是错误信息。注意。error函数不会在一被使用就产生错误信息,所以假设你把其定义在某个变量中,并在兴许脚本中使用了这个变量,那么,能够这样:

范例一:

1
2
3
4
5
$ifdef 
ERROR_001
 
$(error
error is $(ERROR_001))
 
endif

范例二:

1
2
3
4
5
ERR
= $(error found an error !)
 
.PHONY:err
 
err
: ; $(ERR)

(2)warning

$(warning  <text...>)

这个函数非常想error函数。仅仅是它并不会让make退出。仅仅是输出一段警告信息,而make继续运行。

make的执行:

(1)make的退出码(三个):

0 - 表示成功运行

1 - 假设make执行时出现不论什么错误,其返回1。

2 - 假设你使用了make的"-q"选项,而且make使得一些目标不须要更新。那么返回2。

(2)运行Makefile

前面提到。GNU  make默认寻找的Makefile的规则是在当前文件夹下依次寻找三个文件——"GUNmakefile"、"makefile"、"Makefile"。依照顺序寻找三个文件,一旦找到,就開始读取这个文件并运行。

当前,我们也能够给make命令制定一个特殊的名字Makefile。

要达到这样效果须要使用make的"-f"或者“--file”參数("--makefile"參数也能够)。比如我们的Makefile的名字是"hchen.mk"。则能够:

make -f  hchen.mk

来执行。

(3)指定目标

一般来说,make的终于目标是makefile中的第一个目标。而其它的目标通常是由这个目标连带出来的。

这是make的默认行为。一般来说。你的makefile的第一个目标是由多个目标组成的。你能够指定make,让其完毕你指定的目标,须要在make命令后直接跟着目标的名字就能够了。(比如之前说到的"make
clean"形式)。

不论什么在makefile中的目标都能够成为终于目标。可是除了"-"打头,或者包括了"="的目标。由于有这些字符的目标。会被解析为命令行參数或者变了。甚至没有被我们明白写出来的目标也能够成为make的终极目标。也就是说,仅仅要make能够找到其隐含规则推导规则,那么这个隐含目标相同能够成为终极目标。

有一个make的环境变量"MAKECMDGOALS",这个变量会存在你指定的终极目标的列表中。假设在命令行上。你没有指定目标,那么,这个变量是空值。

这个变量能够让你使用在一些特殊的情况下,比如:

sources = foo.c  bar.c
ifneq ($(MAKECMDGOALS) ,clean)
includes $(sources :.c=.d)
endif

基于上面的样例。仅仅要我们输入的命令不是"make clean"。那么makefile会自己主动包括"foo.d"和"bar.d"这两个makefile。

使用指定终极目标的方法能够方便的编译我们的程序,比如:

.PHONY : all
all : prog1 prog2 prog3 prog4

能够看出。这个make中有四个须要编译的程序。我们能够使用"make all"编译全部的目标(假设把all作为第一个目标,那么仅仅须要运行"make"),也能够使用"make prog2"来单独编译目标"prog2"。

既然make能够指定全部的makef中的目标,那么也包括"为目标",于是我们依据这样的性质来让我们的makefile依据不同的目标完毕不同的事情。

在Unix中,软件公布时。特别是GNU这样的开源的软件公布时,其makefile都包括了编译、安装、打包等功能,我们能够依据这样的规则来书写我们的makefile的目标。

<1>"all"

这个伪目标是全部目标的目标。其功能通常是编译全部的目标。

<2>“clean”

这个伪目标是为了删除全部被make创建的文件。

<3>"install"

这个文目标是为了安装已经编译好的程序,事实上就是把目标运行文件复制到指定的目标中去。

<4>"print"

这个伪目标功能室列出改变过的源文件。

<5>"tar"

这个伪目标是为了把源程序打包,也就是一个tar文件。

<6>“dist”

这个伪目标功能就是创建一个压缩文件,通常是把tar文件压缩成为Z文件,或者gz文件。

<6>"TAGS"

这个伪目标功能室更新全部的目标。以备完整的又一次编译。

<7>"check"和"test"

这两个伪目标一般用来測试makefile的流程。

当一个项目的makefile不一定要书写这些目标。这些东西都是GNU的东西,可是假设这样写,一是充实,而是专业。而且,假设要书写这些功能,最好使用这样的名字来命令你的目标。

检查规则:

有时候。我们不希望makefile中的规则运行起来,我们仅仅是检查一下我们的命令。或者运行的序列。能够使用:

"-n"

"--just-printf"

"--dry-run"

"--recon"

仅仅打印,不运行

"-t"

"--touch"

把目标文件的时间更新,但不更改目标文件,也就是make假装编译目标,可是不是真正编译目标,仅仅是把目标变成已编译的状态。

“-q”

"--question"

假设目标存在,那就什么都不输出。什么也不运行,假设目标不存在,打印一条出错信息。

"-W <file>"

“--what-if=<file>”

"--assume-new=<file>"

"--new-file=<file>"

这个參数须要制定一个文件。通常是源文件(或者是依赖文件),Make依据推到规则来执行依赖于这个文件的命令,一般来说,能够和"-n"參数一同使用,来查看这个依赖文件所发生的规则命令。

make參数:

频率较高:

"-b"

"-m"

用于忽略和其它版本号的兼容性。

"-B"

"--always-make"

觉得全部的目标都须要更新(重编译)

“-C  <dir>”

“--directory=<dir>”

指定读取makef的文件夹。

若有多个"-C "參数,make的解释是后面的路径曾经面的作为相对路径,并以最后的文件夹作为被指定的文件夹。如"make -C ~hchen/test -C  prog"相当于"make -C ~hchen/test/prog"

“-debug[=<options>]”

输出make的调试信息,它有集中不同的级别可供选择,假设没有參数,那就是输出最简单的调试信息。<option>的取值:

a——也就是all,输出全部的调试信息。

b——basic,仅仅输出简单的调试信息。

v——也就是verbose,在b的选项级别上。

输出的I信息包含那个makefile被解析,不须要被又一次编译的依赖文件(或依赖目标)等。

...........................(其它省略)

自己主动化变量:

自己主动化变量。就是这样的变量会把模式中定义的一系列文件自己主动的挨个取出,直至全部的符合模式的文件都被取完了。这样的自己主动化变量仅仅应该出如今规则的命令中。

(1)$@

表示规则中的目标文件。

在模式规则中,假设由多个目标,那么,"$@"就是匹配于目标模式定义的集合。

(2)$%

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

(3)$<

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

(4)$?

全部比目标新的依赖目标的集合。以空格分隔。

(5)$^

全部的依赖目标的集合。

以空格分隔。假设在依赖目标中由多个反复的。那么这个变量会出去反复的依赖目标。仅仅保留一份。

(6)$+

这个变量非常像"S^",也是全部依赖目标的集合。仅仅是它不除去反复的依赖目标。

(7)$*

这个变量表示目标模式中"%"及其之前的部分。假设目标是"dir/a.foo.b"。那么目标的模式是"a.%.b",那么。"$*"值就是"dir/a.foo"。这个变量对于构造有关的文件名称是比較快的。假设目标中没有模式的定义,那么"$*"也就不能推到出来。可是假设目标文件的后缀是make所识别的,name"$*"就是除了后缀的那一部分。比如:假设目标是"foo.c",由于".c"是make所能识别的后缀名,所以,"$*"的值就是"foo",这个特性是GNU
  make的。非常有可能不能兼容其它版本号的make。所以,应该尽量避免使用"$* ",除非是在隐含规则或者静态模式中。

假设目标中的后缀是make面目全非。然后"$*"它是空的值。

Linux注意到Makefile的更多相关文章

  1. Linux kernel的 Makefile和Kconfig以及Make menuconfig的关系【转】

    本文转载自:http://blog.sina.com.cn/s/blog_4ba5b45e0102e6vp.html 熟悉内核的Makefile对开发设备驱动.理解内核代码结构都是非常重要的linux ...

  2. ti processor sdk linux am335x evm Makefile hacking

    # # ti processor sdk linux am335x evm Makefile hacking # 说明: # 本文主要对TI的sdk中的Makefile脚本进行解读,是为了了解其工作机 ...

  3. 【总结】嵌入式linux内核中Makefile、Kconfig、.config的关系及增加开机Hello World【转】

    本文转载自:http://blog.csdn.net/fengyuwuzu0519/article/details/73772109 为了弄清内核的组织结构,我们先来实现下面这个简单的例子. 一.增加 ...

  4. [转]Linux中configure/makefile

    本文教你如何使用autoconf.automake等来制作一个以源代码形式(.tar.gz)发布的软件.并可在执行configure时使用自定义参数. 一.概述和基础知识 在Linux下得到一个以源代 ...

  5. linux 下如何 makefile

    本文目的: 尝试着把makefile讲解清楚.非原创,仅仅是学习笔记和备忘录之用. makefile 的目的和好处: 一个工程中的源文件不计数,其按类型.功能.模块分别放在若干个目录中,makefil ...

  6. linux内核的makefile.txt讲解

    linux内核的linux-3.6.5\Documentation\kbuild\makefiles.txt Linux Kernel Makefiles This document describe ...

  7. Linux编程(3) MakeFile

    1. 在Linux中,make工具可以维护程序模块关系和生成可执行程序.它可根据程序模块的修改情况重新编译链接生成中间代码或最终的可执行程序.执行make命令,需要一个名为Makefile的文本文件, ...

  8. [转]Linux下的Makefile

    Makefile 介绍——————— make命令执行时,需要一个 Makefile 文件,以告诉make命令需要怎么样的去编译和链接程序. 首先,我们用一个示例来说明Makefile的书写规则.以便 ...

  9. linux源码Makefile详解(完整)【转】

    转自:http://www.cnblogs.com/Daniel-G/p/3286614.html 随着 Linux 操作系统的广泛应用,特别是 Linux 在嵌入式领域的发展,越来越多的人开始投身到 ...

  10. linux源码Makefile详解

    1.Makefile的作用 (1)决定编译哪些文件 (2)怎样编译这些文件 (3)怎样连接这些文件,最重要的是它们的顺序如何 2.Linux内核Makefile分类 ***************** ...

随机推荐

  1. oracle rac 在完成安装错误。

    今天是2014.05.26,离别N继续使用长今博客.成交一直忙于最近.该条目加上家庭网络还没有缴纳会费,我开始变得不太安全.学习是一个需要冷静情绪.心脏的声音是. 由于改变笔记本,特别需要重新建立Ra ...

  2. TForm.ShowModal只是接管消息循环,禁止外部键盘和鼠标输入到别的窗口,但并不封锁其它窗口继续获取消息(比如WM_TIMER消息仍可被发送到别的窗口上)

    窗体上放一个TTimer,然后双击输入: procedure TForm1.Timer1Timer(Sender: TObject); var cvs: TCanvas; Rect: TRect; S ...

  3. Access Violation at address 00000000.Read of address 00000000 解决办法

    是数组越标或没有初始化某个对象之类的问题,搂住细细检查一下代码, 使用指针前未做检查,而这个指针未初始化. 可能是new后没有delete,这样出现溢出的可能性比较大     检查代码或者跟踪试试 使 ...

  4. 禁用掉用户帐号,用户Lync客户端仍然可以登录!

    问题: 有这样的一个情况,一位具有LYNC权限的用户离职了,AD账号已经禁用.LYNC和邮箱功能暂时保留.可用户离职4天了,还能够正常登录到LYNC,能够正常发送和接收即时消息.我经过测试,确实AD账 ...

  5. 学习日记之命令模式和Effective C++

    命令模式(Command): 讲一个请求封装为一个对象.从而使你可用不同的请求对客户进行參数化.对请求队列或记录请求日志.以及支持可撤销的操作. 命令模式长处: (1),它能较easy地设计一个命令队 ...

  6. ORACLE:plsql优化

      1.登录后默认自动选中My Objects? 设置方法:Tools菜单--Brower Filters会打开Brower Filters的定单窗口,把“My Objects”设为默认即可.? 同理 ...

  7. 每日一小练——高速Fibonacci数算法

    上得厅堂,下得厨房,写得代码,翻得围墙,欢迎来到睿不可挡的每日一小练! 题目:高速Fibonacci数算法 内容:先说说Fibonacci数列,它的定义是数列:f1,f2....fn有例如以下规律: ...

  8. zoj3640(概率dp)

    题目连接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4808 题意: 一个吸血鬼,每次可以随机的选择n个洞中的任意一个,如果 ...

  9. 一步一步学android之事件篇——单选按钮监听事件

    在平常使用软件的时候,我们经常会碰见一些选择题,例如选择性别的时候,在男和女之间选,前面说过这个情况要用RadioGroup组件,那么点击了之后我们该怎么获取到选择的那个值呢,这就是今天要说的OnCh ...

  10. cocos2d-x 3.0游戏实例学习笔记《卡牌塔防》第八部---怪物出场

    /* 说明: **1.本次游戏实例是<cocos2d-x游戏开发之旅>上的最后一个游戏,这里用3.0重写并做下笔记 **2.我也问过木头本人啦,他说:随便写.第一别全然照搬代码.第二能够说 ...