学习笔记|Makefile

一.编译&链接

1.编译:

    1)每个源文件都对应于一个中间目标文件(O文件或是OBJ文件);

    2)编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。

2.链接:

    1)中间目标文件生成执行文件;    

    2)给中间目标文件打个包,在Windows下这种包叫“库文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件;

    3)链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error),在VC下,这种错误一般是:Link 2001错误,意思说是说,链接器未能找到函数的实现。你需要指定函数的ObjectFile.

二.命令

1. “make”:生成执行文件;

2. “make clean”:删除执行文件和所有的中间目标文件。

三.makefile规则

target... : prerequisites ...

command

1. target:一个目标文件,可以是Object File,也可以是执行文件,还可以是一个标签(Label)

2. prerequisites:要生成那个target所需要的文件或是目标

3. command:make需要执行的命令(任意的Shell命令),一定要以一个Tab键作为开头

四.Makefile 总述

1.显式规则:显式规则说明了,如何生成一个或多的的目标文件;

2.隐晦规则:make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的;

3.变量的定义:在Makefile中我们要定义一系列的变量,变量一般都是字符串,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上;

4.文件指示:

    1)在一个Makefile中引用另一个Makefile,就像C语言中的include一样;

        I.include<filename>filename可以是当前操作系统Shell的文件模式(可以保含路径和通配符);

        II.在include前面可以有一些空字符,但是绝不能是[Tab]键开始;

        II.如果make执行时,有“-I”或“--include-dir”参数,那么make就会在这个参数所指定的目录下去寻找;如果目录/include(一般是:/usr/local/bin或/usr/include)存在的话,make也会去找;

    2)根据某些情况指定Makefile中的有效部分,就像C语言中的预编译if一样;

    3)定义一个多行的命令;

5.注释:

    Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符。如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:“\#”。

五.Makefile规则

1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件;如果找到,它会找文件中的第一个目标文件(target),并把这个文件作为最终的目标文件;

2. make只管文件的依赖性,对于所定义的命令的错误,或是编译不成功,make根本不理;

3. make可以自动推导文件以及文件依赖关系后面的命令:

make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,cc -c whatever.c 也会被推导出来

注意:在make的“隐含规则库”中,每一条隐含规则都在库中有其顺序,越靠前的则是越被经常使用的,所以,这会导致我们有些时候即使我们显示地指定了目标依赖,make也不会管。如下面这条规则(没有命令):foo.o : foo.p

依赖文件“foo.p”(Pascal程序的源文件)有可能变得没有意义。如果目录下存在了“foo.c”文件,那么我们的隐含规则一样会生效,并会通过 “foo.c”调用C的编译器生成foo.o文件。因为,在隐含规则中,Pascal的规则出现在C的规则之后,所以,make找到可以生成foo.o的 C的规则就不再寻找下一条规则了。如果你确实不希望任何隐含规则推导,那么,你就不要只写出“依赖规则”,而不写命令

4. 重复的[.h],可以合并起来

5. 伪目标

 .PHONY : clean

       clean :

               -rm edit $(objects)

    1).PHONY意思表示clean是一个“伪目标”,label其冒号后什么也没有,make就不会自动去找文件的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令,就要在make命令后明显得指出这个label的名字;

    2)如果你的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 prog3.o sort.o utils.o

6.  VPATH = src:../headers

上面的的定义指定两个目录,“src”和“../headers”,make会按照这个顺序进行搜索(当前目录永远是最高优先搜索的地方)

7. vpath的使用:

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

vpath < pattern>                              清除符合模式< pattern>的文件的搜索目录。

vpath                                                 清除所有已被设置好了的文件搜索目录

< pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符;

如果连续的vpath语句中出现了相同的< pattern>,或是被重复了的< pattern>,那么make会按照vpath语句的先后顺序来执行搜索

8. 自动生成依赖

例:gcc-MM main.c的输出则是:main.o: main.c defs.h

GNU组织建议把编译器为每一个源文件的自动生成的依赖关系放到一个文件中,为每一个“name.c”的文件都生成一个“name.d”的Makefile文件,[.d]文件中就存放对应[.c]文件的依赖关系。

9. 当我们用“@”字符在命令行前,这个命令将不被make显示出来

10. 如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令。

11. 在某个命令前面加了一个小减号表示忽略该命令的错误;

    1)给make加上“-i”或是“--ignore-errors”参数,那么,Makefile中所有命令都会忽略错误;

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

    3)make的参数的是“-k”或是“--keep-going”:如果某规则中的命令出错了,那么就终目该规则的执行,但继续执行其它规则。

12. 嵌套执行make

subsystem:

           cd subdir && $(MAKE)

等价于

 subsystem:

           $(MAKE) -C subdir

    1)如果你要传递变量到下级Makefile中,那么你可以使用这样的声明:export<variable ...>

        如果你不想让某些变量传递到下级Makefile中,那么你可以这样声明:unexport<variable ...>

    2)有两个变量,一个是SHELL,一个是MAKEFLAGS,这两个变量不管你是否export,其总是要传递到下层Makefile中,如果你不想往下层传递参数,那么

  subsystem:

           cd subdir && $(MAKE) MAKEFLAGS=

13. 定义命令包

以“define”开始,以“endef”结束

define run-yacc

   yacc $(firstword $^)

   mv y.tab.c $@

   endef

14.函数库文件

函数库文件也就是对Object文件(程序编译的中间文件)的打包文件

以如下格式指定函数库文件及其组成:archive(member),这个不是一个命令,而一个目标和依赖的定义。一般来说,这种用法基本上就是为了"ar"命令来服务的。如:

foolib(hack.o) : hack.o

ar cr foolib hack.o

如果要指定多个member,那就以空格分开,如:

foolib(hack.o kludge.o)

六.Makefile变量

1. 在makefile中我们可以使用变量,比如objects = main.o kbd.o command.o display.o,然后用“$(objects)”的方式来使用这个变量,但最好用小括号“()”或是大括号“{}”把变量给包括起来

2.“=”操作符不需要保证使用的变量已经定义好;

使用“:=”操作符,前面的变量不能使用后面的变量,只能使用前面已定义好了的变量

3.FOO ?= bar

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

4.采用“#”注释符来表示变量定义的终止,我们可以定义出其值是一个空格的变量

5.“$(var:a=b)”或是“${var:a=b}”,其意思是,把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串

“静态模式”,如:

foo := a.o b.o c.o

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

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

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

8.可以为某个目标设置局部变量

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

9. $@:目标文件,$^:所有的依赖文件,$<:依赖目标

  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


七.Makefile语法

1.条件语句

“ifeq”、“ifneq”、“ifdef”、"ifndef"

<conditional-directive>

<text-if-true>

else

<text-if-false>

endif

不能以[Tab]键做为开始(不然就被认为是命令)。而注释符“#”同样也是安全的

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

2.字符串处理函数

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

1)字符串替换函数——subst

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

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

    返回:函数返回被替换过后的字符串。

    示例:$(subst ee,EE,feet on the street),返回结果是“fEEt on the strEEt”

2)模式字符串替换函数——patsubst

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

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

    返回:函数返回被替换过后的字符串。

    示例:$(patsubst %.c,%.o,x.c.c bar.c),返回结果是“x.c.o bar.o”

3)排序函数——sort

    $(sort <list> )

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

    返回:返回排序后的字符串。

    示例:$(sort foo bar lose)返回“bar foo lose” 

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

4)去空格函数——strip

    $(strip )

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

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

    示例:$(strip a b c ),结果是“a b c”

5)查找字符串函数——findstring

    $(findstring <find>,<in> )

    功能:在字串<in>中查找<find>字串。

    返回:如果找到,那么返回<find>,否则返回空字符串。

    示例:$(findstring a,a b c)、$(findstring a,b c),第一个函数返回“a”字符串,第二个返回“”字符串(空字符串)

6)过滤函数——filter

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

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

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

    示例:sources := foo.c bar.c baz.s ugh.h,$(filter %.c %.s,$(sources))返回的值是“foo.c bar.c baz.s”

7)反过滤函数——filter-out

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

    功能:以<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”

8)取单词函数——word

    $(word <n>,<text> )

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

    返回:返回字符串<text>中第<n>个单词。如果<n>比<text>中的单词数要大,那么返回空字符串。

    示例:$(word 2, foo bar baz)返回值是“bar”

9)取单词串函数——wordlist

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

    功能:从字符串<text>中取从<s>开始到<e>的单词串。<s>和<e>是一个数字。

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

    示例: $(wordlist 2, 3, foo bar baz)返回值是“bar baz”。

10)单词个数统计函数——words

    $(words <text> )

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

    返回:返回<text>中的单词数。

    示例:$(words, foo bar baz)返回值是“3”。

    备注:如果我们要取<text>中最后的一个单词,我们可以这样:$(word $(words <text> ),<text> )。

11)首单词函数——firstword

    $(firstword <text> )

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

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

    示例:$(firstword foo bar)返回值是“foo”。

    备注:这个函数可以用word函数来实现:$(word 1,<text> )。

3.文件名操作函数

1)取目录函数——dir

    $(dir <names...> )

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

    返回:返回文件名序列<names>的目录部分。

    示例: $(dir src/foo.c hacks)返回值是“src/ ./”

2)取文件函数——notdir

    $(notdir <names...> )

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

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

    示例: $(notdir src/foo.c hacks)返回值是“foo.c hacks”。

3)取后缀函数——suffix

    $(suffix <names...> )

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

    返回:返回文件名序列<names>的后缀序列,如果文件没有后缀,则返回空字串。   

    示例:$(suffix src/foo.c src-1.0/bar.c hacks)返回值是“.c .c”

4)取前缀函数——basename

    $(basename <names...> )

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

    返回:返回文件名序列<names>的前缀序列,如果文件没有前缀,则返回空字串。

    示例:$(basename src/foo.c src-1.0/bar.c hacks)返回值是“src/foo src-1.0/bar hacks”。

5)加后缀函数——addsuffix

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

    功能:把后缀<suffix>加到<names>中的每个单词后面。

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

    示例:$(addsuffix .c,foo bar)返回值是“foo.c bar.c”。

6)加前缀函数——addprefix

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

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

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

    示例:$(addprefix src/,foo bar)返回值是“src/foo src/bar”

7)连接函数——join

    $(join <list1>,<list2> )

    功能:把<list2>中的单词对应地加到<list1>的单词后面。如果<list1>的单词个数要比<list2>的多,那么,<list1>中的多出来的单词将保持原样。如果<list2>的单词个数要比<list1>多,那么,<list2>多出来的单词将被复制到<list2>中。

    返回:返回连接过后的字符串。

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

4.foreach 函数

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

    示例:names := a b c d、files := $(foreach n,$(names),$(n).o),$(files)的值是“a.o b.o c.o d.o”

5.if 函数

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

6.call函数

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

    功能:唯一一个可以用来创建新的参数化的函数,<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”

7.origin函数

    $(origin <variable> )

    功能:只是告诉你这个变量是哪里来的。<variable>是变量的名字,不应该是引用。所以你最好不要在<variable>中使用“$”字符

    返回:

        1)“undefined”:如果<variable>从来没有定义过,origin函数返回这个值“undefined”。

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

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

        4)“file”:如果<variable>这个变量被定义在Makefile中。

        5)“command line”:如果<variable>这个变量是被命令行定义的。

        6)“override”:如果<variable>是被override指示符重新定义的。

        7)“automatic”:如果<variable>是一个命令运行中的自动化变量。关于自动化变量将在后面讲述。

8.shell函数

    注意:这个函数会新生成一个Shell程序来执行命令,所以你要注意其运行性能

9.控制make的函数

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

2)$(warning <text ...> )


八.隐含规则命令参数变量

ARFLAGS:函数库打包程序AR命令的参数。默认值是“rv”。

ASFLAGS:汇编语言编译器参数。(当明显地调用“.s”或“.S”文件时)。

CFLAGS:C语言编译器参数。

CXXFLAGS:C++语言编译器参数。

COFLAGS:RCS命令参数。

CPPFLAGS:C预处理器参数。( C 和 Fortran 编译器也会用到)。

FFLAGS:Fortran语言编译器参数。

GFLAGS:SCCS “get”程序参数。

LDFLAGS:链接器参数。(如:“ld”)

LFLAGS:Lex文法分析器参数。

PFLAGS:Pascal语言编译器参数。

RFLAGS:Ratfor 程序的Fortran 编译器参数。

YFLAGS:Yacc文法分析器参数。


九.隐含规则命令的变量

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

TEX:从TeX源文件创建TeX DVI文件的程序。默认命令是“tex”。

TEXI2DVI:从Texinfo源文件创建军TeX DVI 文件的程序。默认命令是“texi2dvi”。

WEAVE:转换Web到TeX的程序。默认命令是“weave”。

CWEAVE:转换C Web 到 TeX的程序。默认命令是“cweave”。

TANGLE:转换Web到Pascal语言的程序。默认命令是“tangle”。

CTANGLE:转换C Web 到 C。默认命令是“ctangle”。

RM:删除文件命令。默认命令是“rm –f”。


十.隐含规则

1、编译C程序的隐含规则。

    “<n>.o”的目标的依赖目标会自动推导为“<n>.c”,并且其生成命令是“$(CC) –c $(CPPFLAGS) $(CFLAGS)”

2、编译C++程序的隐含规则。

    “<n>.o” 的目标的依赖目标会自动推导为“<n>.cc”或是“<n>.C”,并且其生成命令是“$(CXX) –c $(CPPFLAGS) $(CFLAGS)”。(建议使用“.cc”作为C++源文件的后缀,而不是“.C”)

3、编译Pascal程序的隐含规则。

    “<n>.o”的目标的依赖目标会自动推导为“<n>.p”,并且其生成命令是“$(PC) –c $(PFLAGS)”。   

4、编译Fortran/Ratfor程序的隐含规则。

    “<n>.o”的目标的依赖目标会自动推导为“<n>.r”或“<n>.F”或“<n>.f”,并且其生成命令是:

        “.f” “$(FC) –c $(FFLAGS)”

        “.F” “$(FC) –c $(FFLAGS) $(CPPFLAGS)”

        “.f” “$(FC) –c $(FFLAGS) $(RFLAGS)”

5、预处理Fortran/Ratfor程序的隐含规则。

    “<n>.f”的目标的依赖目标会自动推导为“<n>.r”或“<n>.F”。这个规则只是转换Ratfor或有预处理的Fortran程序到一个标准的Fortran程序。其使用的命令是:

        “.F” “$(FC) –F $(CPPFLAGS) $(FFLAGS)”

        “.r” “$(FC) –F $(FFLAGS) $(RFLAGS)”

6、编译Modula-2程序的隐含规则。

    “<n>.sym” 的目标的依赖目标会自动推导为“<n>.def”,并且其生成命令是:“$(M2C) $(M2FLAGS) $(DEFFLAGS)”。“<n.o>” 的目标的依赖目标会自动推导为“<n>.mod”,并且其生成命令是:“$(M2C) $(M2FLAGS) $(MODFLAGS)”。

7、汇编和汇编预处理的隐含规则。

    “<n>.o” 的目标的依赖目标会自动推导为“<n>.s”,默认使用编译品“as”,并且其生成命令是:“$(AS) $(ASFLAGS)”。“<n>.s” 的目标的依赖目标会自动推导为“<n>.S”,默认使用C预编译器“cpp”,并且其生成命令是:“$(AS) $(ASFLAGS)”。

8、链接Object文件的隐含规则。

    “<n>” 目标依赖于“<n>.o”,通过运行C的编译器来运行链接程序生成(一般是“ld”),其生成命令是:“$(CC) $(LDFLAGS) <n>.o     $(LOADLIBES) $(LDLIBS)”。这个规则对于只有一个源文件的工程有效,同时也对多个Object文件(由不同的源文件生成)的也有效。

    例如如下规则:

x : y.o z.o

    并且“x.c”、“y.c”和“z.c”都存在时,隐含规则将执行如下命令:

cc -c x.c -o x.o

cc -c y.c -o y.o

cc -c z.c -o z.o

cc x.o y.o z.o -o x

rm -f x.o

rm -f y.o

rm -f z.o

    如果没有一个源文件(如上例中的x.c)和你的目标名字(如上例中的x)相关联,那么,你最好写出自己的生成规则,不然,隐含规则会报错的。

9、Yacc C程序时的隐含规则。

    “<n>.c”的依赖文件被自动推导为“n.y”(Yacc生成的文件),其生成命令是:“$(YACC) $(YFALGS)”。(“Yacc”是一个语法分析器,关于其细节请查看相关资料)

10、Lex C程序时的隐含规则。

    “<n>.c”的依赖文件被自动推导为“n.l”(Lex生成的文件),其生成命令是:“$(LEX) $(LFALGS)”。(关于“Lex”的细节请查看相关资料)

11、Lex Ratfor程序时的隐含规则。

    “<n>.r”的依赖文件被自动推导为“n.l”(Lex生成的文件),其生成命令是:“$(LEX) $(LFALGS)”。

12、从C程序、Yacc文件或Lex文件创建Lint库的隐含规则。

    “<n>.ln” (lint生成的文件)的依赖文件被自动推导为“n.c”,其生成命令是:“$(LINT) $(LINTFALGS) $(CPPFLAGS) -i”。对于“<n>.y”和“<n>.l”也是同样的规则。


十一.模式规则

 1.一个模式规则就好像一个一般的规则,只是在规则中,目标的定义需要有"%"字符。"%"的意思是表示一个或多个任意字符。在依赖目标中同样可以使用"%",只是依赖目标中的"%"的取值,取决于其目标。

2.自动化变量

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

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

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

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

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

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

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

    这七个自动化变量还可以取得文件的目录名或是在当前目录下的符合模式的文件名,只需要搭配上"D"或"F"字样。"D"的含义就是Directory,就是目录,"F"的含义就是File,就是文件。

3.你可以重载内建的隐含规则(或是定义一个全新的),例如你可以重新构造和内建隐含规则不同的命令,如:

%.o : %.c

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

你可以取消内建的隐含规则,只要不在后面写命令就行。如:

%.o : %.s


十二.后缀规则

1.双后缀规则定义了一对后缀:目标文件的后缀和依赖目标(源文件)的后缀。如".c.o"相当于"%o : %c"。

单后缀规则只定义一个后缀,也就是源文件的后缀。如".c"相当于"% : %.c"。

2.后缀规则不允许任何的依赖文件

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

.SUFFIXES: .hack .win


十三.隐含规则搜索算法

1、把T的目录部分分离出来。叫D,而剩余部分叫N。(如:如果T是"src/foo.o",那么,D就是"src/",N就是"foo.o")

2、创建所有匹配于T或是N的模式规则列表。

3、如果在模式规则列表中有匹配所有文件的模式,如"%",那么从列表中移除其它的模式。

4、移除列表中没有命令的规则。

5、对于第一个在列表中的模式规则:

    1)推导其"茎"S,S应该是T或是N匹配于模式中"%"非空的部分。

    2)计算依赖文件。把依赖文件中的"%"都替换成"茎"S。如果目标模式中没有包含斜框字符,而把D加在第一个依赖文件的开头。

    3)测试是否所有的依赖文件都存在或是理当存在。(如果有一个文件被定义成另外一个规则的目标文件,或者是一个显式规则的依赖文件,那么这个文件就叫"理当存在")

    4)如果所有的依赖文件存在或是理当存在,或是就没有依赖文件。那么这条规则将被采用,退出该算法。

6、如果经过第5步,没有模式规则被找到,那么就做更进一步的搜索。对于存在于列表中的第一个模式规则:

    1)如果规则是终止规则,那就忽略它,继续下一条模式规则。

    2)计算依赖文件。(同第5步)

    3)测试所有的依赖文件是否存在或是理当存在。

    4)对于不存在的依赖文件,递归调用这个算法查找他是否可以被隐含规则找到。

    5)如果所有的依赖文件存在或是理当存在,或是就根本没有依赖文件。那么这条规则被采用,退出该算法。

7、如果没有隐含规则可以使用,查看".DEFAULT"规则,如果有,采用,把".DEFAULT"的命令给T使用。


参考:

Makefile教程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350