学习makefile时的笔记总结

makefile学习笔记(2016-8-16):

一下内容有一些是自己总结的,一些是网上的,因为不知道哪里是出处所以不贴明了。。。

1.在makefile 中’\’是换行的意思

2.目标文件包含:执行文件和中间目标文件(*.o)

3.依赖文件就是冒号后面的那些.c文件和.h文件

4.*.o文件是可执行文件的依赖文件

5.make伪目标 执行在终端中执行伪目标的命令

6.$(变量名)是一个宏定义,扩展变量名所代表的字符串

7.隐晦规则:insert.o:insert.cdefs.h buffer.h

           cc –c insert.o insert.c defs.hbuffer.h

由隐晦规则可以简化为:insert.o:defs.h buffer.h

8.依赖文件之间用空格隔开

9.-f可以指定特定的makefile. eg:make –f abs.mk

10.includefoo.make *.mk 在makefile中加入这句可以将其他目录的makefile展开在当前makefile中,当在终端执行make时要make –I 文件目录名…

11.在makefile命令中加入‘-’可以让终端在执行该命令出错而忽略继续执行其他命令,eg:-includemakefile

12.只有在变量被使用时,make会把它展开在使用的位置;第一个规则中的目标会被确立为最终目标,如果第一个规则有多个目标,那么第一个目标会是最终目标

13.object=*.o,当使用到变量object时,*.o不会展开$(object)=*.o,如果要让使用object时,*.o自动展开,就要object:=$(wildcard*.o)

14.”%”的意思是匹配一个或若干字符,eg:”%.h”表示所有以”.h”结尾的文件

15.当.c和.h文件不再当前makefile目录中而依赖文件中有他们,这时可以通过VPATH=sre:../home 来指定src和../home这两个目录,在当前目录搜索不到时就去src,又搜索不到就去../home,VPATH是一个变量

16.还有一个比VPATH更灵活的vpath,它是makefile的关键字,vpath有三种模式:

vpath<pattern><directories>为符合pattern的文件指定搜索目录

vpath<pattern>清除符合pattern的路径

vpath清除所有设置好的文件搜索路径

17.如果有连续相同的vpath语句出现:

vpath%c foo:bar

vpath% bish

那么搜索路径是foo->bar->bish

18.伪目标在终端中make时并不执行,而是在终端make 伪目标才会执行

19.当需要生成多个可执行文件时,可以将可执行文件作为依赖目标,将伪目标作为最终目标,只要make 伪目标就可以生成多个可执行文件

20.如果没有先.PHONY:伪目标  定义伪目标,那么当makefile所在文件夹中有和伪目标同名的文件时,make  伪目标就不会被执行

21.如果有多个伪目标且相互之间可以调用,那么伪目标可以成为其他伪目标的依赖目标

22.”>”是重定向符号,将前面的输入定向或输出到后面的文件或流中

23.”$@”表示所有目标的集合,就像一个数组,会一个一个取出目标;如果有多个目标且生成目标的命令类似,可以用静态模式简化一下:

eg:   objects=foo.o bar.o

      all:$(objects)

      $(objects):%.o:%.c

        $(CC)–c $(CFLAGS) $< -o $@.

24.在终端中执行gcc –MM main.c

输出:main.o:main.c defs.h

-MM可以查找.c文件中包含其他的.c和.h文件

25.makefile可以把每个.c文件的依赖关系和命令放到.d文件中,我们可以在主makefile中写出.c和.d文件的依赖关系,让make更新或自动生成.d文件

eg:sources=foo.c bar.c

include $(sources:.c=.d)

这里的.d不仅包含了依赖关系还有生成规则,不过.c文件要注意顺序,因为第一个会成为最终目标

26.通常make会把其要执行的命令行在执行前输出到屏幕中,当我们用”@”字符在命令前,难么这个命令就不会被显示出来

27.在终端中,make –n 可以只显示makefile的内容而不执行makefile;而make –s 则是只执行而不显示命令和其他信息

28.在makefile中,如果要让上一行的命令结果应用于下一条命令,那么应该让这两条命令用”;”隔开,eg:

.PHONY:exec

exec:

   cd /home/hchen;pwd

如果cd和pwd没有用”;”隔开的话那么pwd就只显示当前的目录

29.命令运行时,make会检测每个命令的返回码,0表示成功,非0失败且可能影响其他命令的执行

30.全局忽略make出错的方式:make –i;

31.如果有很多makefile且在不同的目录下,那么我们可以写一个总控makefile来管理这些零散的makefile。eg:假如有一个makefile在sbdir子目录下,那么总控makefile可以这样写:

subsystem:

        $(MAKE) –C sbdir

这个语句的意思是进入sbdir然后执行里面的makefile

其中:-C 目录 表示切换到指定目录后执行make

32.总控makefile的变量可以传递到调用的makefile中(但同级的makefile之间没法传递,只能由高优先级的传给低优先级的),但不会覆盖下层makefile的变量(有点像全局变量),形式:export<variable…>;如果要传递所有的变量给下层makefile,那么单独一个export就可以了(makefile内置变量MAKELEVEL可以查看当前makefile的level,MAKELEVEL=make调用层数),export还可以在终端中设置环境变量

33.SHELL是一个环境变量,代表/bin/bash

34.makefile可以使用环境变量来编译

35.make–w 会让你看到当前执行的makefile的工作目录,当有很多makefile是很方便调试

36.makefile中可以为一串命令定义一个变量(命令包)

eg:   define run-yacc

      yacc $(firstword $^)

mv y.tab.c $@

endif

其中run-yacc 就是这两条命令的变量,通过define和endef来定义命令包,当要使用命令包时:$(run-yacc)展开就可以了

37.makefile变量名推荐使用大小写搭配,如果:MakeFlags,可以避免和系统变量名冲突

38.makefile变量可以使用另一个变量,但是最好用”:=”,”:=”右边的变量必须在前面有定义了的。eg: x:=foo 

                                  y:=$(x)bar

那么$(y)=foo bar,如果前面没有定义过x就拿来定义y,那么$(y)=bar且不会报错

39.在makefile中定义一个空格变量:

nullstring:=

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

nullstring是一个什么也没有的变量,用来标量space变量的开始,采用#注释来表示变量定义的结束

40.如果我们定义一个这样的变量:

dir:=/foo/bar   #end of the line

那么dir的值为/foo/bar,后面跟两个空格,用这个变量指定别的目录”$(dir)/file”那么就完蛋了

41.”?=”:foo?=bar  如果foo没有被定义过,那么foo=bar,如果有,那么什么都不做

42.变量替换: foo:=a.o b.o c.o

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

将foo中的.o转换成bar的.c

43.”+=”可以在变量追加值

44.makefile可以定义目标变量(类似C语言的局部变量)

eg:

   prog:CFLAGS=-g

   prog:prog.o foo.o bar.o

      $(CC) $(CFLAGS) prog.o foo.o bar.o

prog.o:prog.c

45.makefile中的四条条件表达式:

   ifeq(<arg1>,<arg2>) <text>else <text-if-false> endif

   ifneq(<arg1>,<arg2>) <text> else<text-if-eq> endif

   ifdef(variable-name) <text> else<text-if-ndef> endif

   ifndef<variable-name> <text>else <text-if-def> endif

上面的ifeq…和else都不能用[Tab]开始,最好不要把自动化变量放在条件表达式中,因为自动化变量是在运行时展开的。

46.makefile的函数:

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


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


示例:


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


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




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


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


示例:


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


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


备注:


这和我们前面“变量章节”说过的相关知识有点相似。如:


“$(var:<pattern>=<replacement> )”
相当于
“$(patsubst <pattern>,<replacement>,$(var))”,


而“$(var: <suffix>=<replacement> )”
则相当于
“$(patsubst %<suffix>,%<replacement>,$(var))”。


例如有:objects = foo.o bar.o baz.o,
那么,“$(objects:.o=.c)”和“$(patsubst%.o,%.c,$(objects))”是一样的。


$(strip <string> )


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


$(strip a b c )


把字串“a b c ”去到开头和结尾的空格,结果是“a b c”。


$(findstring <find>,<in> )


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


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


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


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


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


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”。


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


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


objects=main1.o foo.o main2.o bar.o
mains=main1.o main2.o


$(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。


$(sort <list> )


名称:排序函数——sort。
功能:给字符串<list>中的单词排序(升序)。
返回:返回排序后的字符串。
示例:$(sort foo bar lose)返回“barfoo lose” 。
备注:sort函数会去掉<list>中相同的单词。


$(word <n>,<text> )


名称:取单词函数——word。
功能:取字符串<text>中第<n>个单词。(从一开始)
返回:返回字符串<text>中第<n>个单词。如果<n>比<text>中的单词数要大,那么返回空
字符串。
示例:$(word 2, foo bar baz)返回值是“bar”。


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


名称:取单词串函数——wordlist。
功能:从字符串<text>中取从<s>开始到<e>的单词串。<s>和<e>是一个数字。
返回:返回字符串<text>中从<s>到<e>的单词字串。如果<s>比<text>中的单词数要大,那
么返回空字符串。如果<e>大于<text>的单词数,那么返回从<s>开始,到<text>结束的单
词串。
示例: $(wordlist 2, 3, foo bar baz)返回值是“bar baz”。


$(words <text> )


名称:单词个数统计函数——words。
功能:统计<text>中字符串中的单词个数。
返回:返回<text>中的单词数。
示例:$(words, foo bar baz)返回值是“3”。
备注:如果我们要取<text>中最后的一个单词,我们可以这样:$(word $(words <text> 
),<text> )。


$(firstword <text> )


名称:首单词函数——firstword。
功能:取字符串<text>中的第一个单词。
返回:返回字符串<text>的第一个单词。
示例:$(firstword foo bar)返回值是“foo”。
备注:这个函数可以用word函数来实现:$(word1,<text> )。


以上,是所有的字符串操作函数,如果搭配混合使用,可以完成比较复杂的功能。这里,
举一个现实中应用的例子。我们知道,make使用“VPATH”变量来指定“依赖文件”的搜索
路径。于是,我们可以利用这个搜索路径来指定编译器对头文件的搜索路径参数CFLAGS,
如:


override CFLAGS += $(patsubst %,-I%,$(subst :, ,$(VPATH)))


如果我们的“$(VPATH)”值是“src:../headers”,那么“$(patsubst %,-I%,$(subst :
, ,$(VPATH)))”将返回“-Isrc -I../headers”,这正是cc或gcc搜索头文件路径的参数


三、文件名操作函数


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


$(dir <names...> )


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


$(notdir <names...> )


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


$(suffix <names...> )


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


$(basename <names...> )

名称:取前缀函数——basename。
功能:从文件名序列<names>中取出各个文件名的前缀部分。
返回:返回文件名序列<names>的前缀序列,如果文件没有前缀,则返回空字串。
示例:$(basename src/foo.c src-1.0/bar.c hacks)返回值是“src/foo src-1.0/bar h
acks”。


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

名称:加后缀函数——addsuffix。
功能:把后缀<suffix>加到<names>中的每个单词后面。
返回:返回加过后缀的文件名序列。
示例:$(addsuffix .c,foo bar)返回值是“foo.cbar.c”。

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

名称:加前缀函数——addprefix。
功能:把前缀<prefix>加到<names>中的每个单词后面。
返回:返回加过前缀的文件名序列。
示例:$(addprefix src/,foo bar)返回值是“src/foo src/bar”。

$(join <list1>,<list2> )

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

names := a b c d

files := $(foreach n,$(names),$(n).o)

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

注意,foreach中的<var>参数是一个临时的局部变量,foreach函数执行完后,参数<var>的变量将不在作用,其作用域只在foreach函数当中。


五、if函数
if函数很像GNU的make所支持的条件语句——ifeq(参见前面所述的章节),if函数的语法是:


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

或是

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

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

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

所以,<then-part>和<else-part>只会有一个被计算。

六、call函数


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

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

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

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

那么,foo的值就是“a b”。当然,参数的次序是可以自定义的,不一定是顺序的,如:
reverse = $(2) $(1)
foo = $(call reverse,a,b)
此时的foo的值就是“b a”。

七、origin函数

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

$(origin <variable> )
注意,<variable>是变量的名字,不应该是引用。所以你最好不要在<variable>中使用“$”字符。Origin函数会以其返回值来告诉你这个变量的“出生情况”,下面,是origin函
数的返回值:
“undefined”
如果<variable>从来没有定义过,origin函数返回这个值“undefined”。
“default”

如果<variable>是一个默认的定义,比如“CC”这个变量,这种变量我们将在后面讲述。

“environment”

如果<variable>是一个环境变量,并且当Makefile被执行时,“-e”参数没有被打开。


“file”
如果<variable>这个变量被定义在Makefile中。
“command line”
如果<variable>这个变量是被命令行定义的。
“override”
如果<variable>是被override指示符重新定义的。
“automatic”
如果<variable>是一个命令运行中的自动化变量。关于自动化变量将在后面讲述。


这些信息对于我们编写Makefile是非常有用的,例如,假设我们有一个Makefile其包了一个定义文件Make.def,在Make.def中定义了一个变量“bletch”,而我们的环境中也有一
个环境变量“bletch”,此时,我们想判断一下,如果变量来源于环境,那么我们就把之重定义了,如果来源于Make.def或是命令行等非环境的,那么我们就不重新定义它。于是
,在我们的Makefile中,我们可以这样写:

ifdef bletch
ifeq "$(origin bletch)" "environment"
bletch = barf, gag, etc.
endif
endif

当然,你也许会说,使用override关键字不就可以重新定义环境中的变量了吗?为什么需要使用这样的步骤?是的,我们用override是可以达到这样的效果,可是override过于粗
暴,它同时会把从命令行定义的变量也覆盖了,而我们只想重新定义环境传来的,而不想重新定义命令行传来的。

八、shell函数
shell 函数也不像其它的函数。顾名思义,它的参数应该就是操作系统Shell的命令。它和反引号“`”是相同的功能。这就是说,shell函数把执行操作系统命令后的输出作为函数
返回。于是,我们可以用操作系统命令以及字符串处理命令awk,sed等等命令来生成一个变量,如:

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

注意,这个函数会新生成一个Shell程序来执行命令,所以你要注意其运行性能,如果你的Makefile中有一些比较复杂的规则,并大量使用了这个函 数,那么对于你的系统性能是有害的。特别是Makefile的隐晦的规则可能会让你的shell函数执行的次数比你想像的多得多。

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

$(error <text ...> )

产生一个致命的错误,<text ...>是错误信息。注意,error函数不会在一被使用就会产生错误信息,所以如果你把其定义在某个变量中,并在后续的脚本中使用这个变量,那么也
是可以的。例如:

示例一:

ifdef ERROR_001
$(error error is $(ERROR_001))
endif

示例二:

ERR = $(error found an error!)
.PHONY: err
err: ; $(ERR)

示例一会在变量ERROR_001定义了后执行时产生error调用,而示例二则在目录err被执行时才发生error调用。
$(warning <text ...> )
这个函数很像error函数,只是它并不会让make退出,只是输出一段警告信息,而make继续执行。

makefile中,应该把字符串的处理看出对多个单词的处理,很多对字符串的处理都是对单词的处理

“MAKECMDGOALS”是make的一个环境变量,存放所指的终极目标的列表,eg: sources=foo.c bar.c

      ifneq($(MAKECMDGOALS),clean)

      include $(sources:.c=.d)

      endif

如果不是make clean那么会把.d文件包含进来

makefile中的一些预定义变量(自动化变量):

$*:不包括扩展名的目标文件名称

$+:所有的依赖文件,以空格分开,可以包含重复文件

$<:第一个依赖文件的名称

$?:所有更新过的依赖文件集合

$@:所有目标集合

$^:所有的依赖文件,以空格分开,不包含重复文件

$%:目标是归档成员,eg:mytarget.so(image.o),则$@=mytarget.so

$%=image.o

隐含规则:从文件”foo.c”生成目标可执行文件”foo”,按道理make会编译生成”foo.o”,但实际却会跳过直接:cc –o foo foo.c

变量和函数的展开发生在make载入makefile时,模式中的”%”则发生在运行时,”%”表示一个或者多个任意字符,不能为空

自动化变量只应出现在命令中

%.a表示静态库文件,%.so表示动态库文件

文件”src/eat”与模式”e%t”会匹配哦

依赖文件的“%”会传递给目标的”%”

双后缀(老式makefile规则):.c.o:

                            $(CC)–c $(CFLAGS) –o $@ $<

.c是源文件的后缀,.o是目标文件的后缀,双后缀不能有依赖文件

.o文件是二进制文件,静态函数库文件(.a)是对.o文件的打包过程,由ar命令来完成,格式:archive(member)

 

 

            

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值