zoukankan      html  css  js  c++  java
  • makefile 学习笔记

    参考资料:陈浩,《跟我一起写makefile》 :http://blog.csdn.net/haoel/article/details/2886/

    Makefile的格式和规则

        target ... : prerequisites ...
                command
                ...
                ...

    target是目标文件,Object File或者可执行文件,或者标签

    prerequisites是生成target依赖的所有文件。

    command是make需要执行的命令。(任意的Shell命令)

    需要注意的是command前必须是一个[Tab]。

    这是个文件的依赖关系:生成target依赖于prerequisites 里面的文件,生成规则由command给出。实际上,Makefile中最核心的内容是,当prerequisites 中有文件比target新,就执行command中的指令。

    make的自动推导

    make识别到一个XXX.o时,可以自动将XXX.c加入到依赖文件中,并且cc –o XXX.c 也能推导出来。因此在书写makefile的时候可以省去那些可以由makefile推导出来的东西。比如以上的makefile简化之后:

    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)

    可以看出,每一个XXX.o的依赖文件只列出了头文件,下面的command也省略了,因为这两个东西都可以被make自动推导出来。

    clean规则

    每一个makefile都应该有一个清理规则,clean总是应该放在makefile的最后部分。一般为:

            clean:
                rm edit $(objects)

    更好的做法是:

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

    .PHONY表明clean是一个伪目标,-rm前面的减号意思是当对某些文件操作失败时,继续后面的操作。

    make的工作流程

      读入所有的Makefile。
      读入被include的其它Makefile。
      初始化文件中的变量。
      推导隐晦规则,并分析所有规则。
      为所有的目标文件创建依赖关系链。
      根据依赖关系,决定哪些目标要重新生成。
      执行生成命令。

    makefile的最开始部分应该是make的最终目标,一般情况下一个makefile都只有一个最终目标。

            makefile的文件搜寻

            当工程的文件放在不同的目录时,写makefile时需要指定搜寻的目录和规则。

            VPATH = ….

            makefile中有一个特殊变量VPATH,通过指定VPATH告诉make在VPATH所指的目录寻找依赖的文件(当然是在当前目录没有找到的前提下),多个目录用冒号隔开。

            vpath <pattern> <directories>

            <pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”结尾的文件。<pattern>指定了要搜索的文件集,而<directories>则指定了<pattern>的文件集的搜索的目录,多个目录用冒号隔开。

            vpath和上面的VPATH并不相同,vpath是makefile中的关键字,它可以指定不同的文件在不同的搜索目录中,比如:

            vpath %.h ../headers

            表示在../headers目录搜索.h头文件。

            伪目标

            前面提到过伪目标,伪目标其实和真正的目标区别不大,最大的区别就是伪目标只是一个便签,不生成真正的目标文件。我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”。如:

            .PHONY clean

            伪目标可以非常有用。之前我们说过,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

            这里的最终目标是一个伪目标all,在执行make 时候伪目标总是会被执行,因此依赖的三个文件会被生成。

            再比如,我们在清理的时候可能需要执行不同级别的清理,那么也可以用伪目标来区分。

            多目标

            上文提到要生成多个目标时候可以使用伪目标来实现,然而实际上makefile的target可以不止一个。有些情况下,这一组目标有某个或者某些相同的依赖文件而且生成的规则相似,那么就可以使用makefile的多目标来生成。如:

              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

            左边的规则与右边的等价。简单解释一下,由于bigoutput和littleoutput都依赖文件text.g,且生成规则相似,则写成下面一行的命令形式。其中-$(subst output,,$@)中的”$”表示要执行一个makefile函数,函数名是subst,后面都是参数。“$@”表示目标的集合。这个函数的作用是截取字符串。

            在定义多目标规则时,利用静态模式将更加有用。静态模式的基本语法是:

            <targets ...>: <target-pattern>: <prereq-patterns ...>
                        <commands>
                        ...

            targets定义了一系列的目标文件,可以有通配符。是目标的一个集合。

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

            prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义。

            换一种更加通俗的说法,target定义的是一个目标的集合,里面包含了很多的目标,而target-pattern定义的是本次命令执行的目标模式,也就是说从target目标集合里选出匹配target-pattern模式的目标来作为本次命令的真正目标。而prereq-patterns定义的是依赖文件的匹配模式。举个简单的例子,如果target-pattern为“%.o”,那么目标集合就是target中所有以[.o]为后缀的目标文件。而如果prereq-patterns定义为“%.c”,于是依赖的文件就是target-pattern匹配的所有的[.o]目标文件对应的[.c]文件。

            一个例子:

                objects = foo.o bar.o

                all: $(objects)

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

            由之前的分析可以看出,target-pattern为[%.o]匹配了objects中所有的[.o]目标文件,即foo.o bar.o,而[%.c]表明命令的依赖文件是foo.c bar.c。命令中的[$<]表示所有的依赖目标集(也就是foo.c bar.c),[$@]表示目标集(也就是foo.o bar.o)。

            自动生成依赖性

            makefile的变量

            makefile的变量在定义时需要赋给一个初值,使用时前面要有一个$符合,一般的使用惯例是“${}”或“$()”,若要使用真正的“$”符号则表示为“$$”。一个简单的例子:

            edit : main.o kbd.o command.o display.o /
                       insert.o search.o files.o utils.o
                        cc -o edit main.o kbd.o 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

            这个makefile的edit依赖文件很多,可以用一个变量来代替它。

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

            于是,第一行就可以变为:

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

            这样为书写makefile,以及在大的工程中修改makefile带了很大的便利,也让makefile更加简洁易懂。makefile中的目标、依赖、命令都可以用变量来表示。makefile定义变量的方法有三种,一种是像通常的编程语言一样使用类似如下的方式。这种方式定义变量的好处在于可以将变量的定义放在后面,换句话说,前面的命令可以使用后面定义的变量。

            objects = main.o kbd.o command.o display.o

            另外一种变量定义方式是使用“:=”操作符,这种定义方式不能使用后面定义的变量。例如:

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

            等价于

                y := foo bar
                x := later

            而,

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

            却等价于

                y := bar
                x := foo

            还有一种用于定义变量的操作符“?=”,

            x ?= foo

            表示如果x没有定义过,那么x = foo,否则什么也不做。

            变量值的替换的例子:

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

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

            上面两种表示的效果是一样的——变量替换,让$(bar)变量的值为“a.c b.c c.c”,但是有一点点不同。左边$(foo:.o=.c)表示的是,变量foo中所有.o结尾的字符串,替换成.c结尾的字符串。右边$(foo:%.o=%.c)这种形式是静态模式的表示形式,变量foo中匹配模式%.o的替换成了%.c,%是必需的。

            我们可以使用“+=”操作符给变量追加值,即增加一个值。

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

            define关键字可以用来定义多行变量,如:

                define two-lines
                echo foo
                echo $(bar)
                endef

            以define开头,以endef结尾。命令前面必需有[Tab],相当于
            two-lines = foo $(bar)

            有一种非常有用的变量叫目标变量,定义语法和举例如下:

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

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

                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

            当设置了目标变量时,这个变量会作用到由这个目标所引发的所有的规则中去。如上例,prog : CFLAGS = -g中定义了目标变量CFLAGS,因此在所有由prog引起的命令中,这个CFLAGS都会起作用。target还可以用模式变量。如:

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

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

                %.o : CFLAGS = -O

            条件判断

              libs_for_gcc = -lgnu
                normal_libs =

                foo: $(objects)
                ifeq ($(CC),gcc)
                        $(CC) -o foo $(objects) $(libs_for_gcc)
                else
                        $(CC) -o foo $(objects) $(normal_libs)
                endif

            条件判断由ifeq开始,后面括号里是进行比较的两个变量或者常量,ifeq ($(CC),gcc)表示如果变量CC是gcc的话,执行下一条命令,否则(else)执行else下的命令,判断由endif结束。实际上,ifeq可以由下面几种方式来使用,而上例使用的是第一种方式。

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

            另外一个条件判断关键字是ifneq,意思很明白,和ifeq的恰好相反,但是用法相同。

            第三个条件关键字是“ifdef”,

                ifdef <variable-name>

            如果变量<variable-name>的值非空,那到表达式为真。否则,表达式为假。实际上,ifdef就是测试变量是否有值。另外还有一个与之对应的ifndef,意思非常明确就不多说了。

            在条件判断中最好不要出现自动化变量(如”$@”),因为自动化变量在运行时才会有值。

            makefile的函数

            实际上在之前的例子中已经多次提到过makefile的函数了。当然这个函数和我们程序语言的函数有相同点也有很大的不同,makefile函数调用后有返回值,而且我们可以使用这个返回值,但是makefile的函数只有那么多,不能由用户自己来定义,不过也够用了。函数的调用语法如下:

                $(<function> <arguments1,arguments2,arguments3>)  或者使用{}括号

            <function>是函数名,后面是参数列表,函数名和参数之间用空格隔开,参数之间用逗号隔开。

            字符串处理函数:

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

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

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

                名称:模式字符串替换函数——patsubst。
                功能:查找<text>中的单词(单词以“空格”、“Tab”或  “回车”“换行”分隔)是否符合模式<pattern>,如果匹配的话,则以<replacement>替换。

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

            $(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>个单词。(从一开始)
                返回:返回字符串<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>中的单词数。

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

            $(firstword <text>)

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

            文件名操作函数:

            $(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>中。
                返回:返回连接过后的字符串。

            特殊函数:

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

            foreach函数和shell中的for语句类似,用于循环控制。这个命令的解释是:将<list>中的字符串逐一取出放入临时变量<var>中(循环完成,<var>便不复存在),然后再执行<text>中包含的命令,最后将执行之后的结果返回。每次返回的结果由一个空格隔开,循环完成之后的返回值就是由空格隔开的一系列结果。

            例如:

            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 <condition>,<then-part>,<else-part>)

            if函数是一个条件控制语句,首先计算<condition>,如果<condition>返回非空字符,则计算<then-part>,否则计算<else-part>,当然和编程语言一样<else-part>是可选的。最后函数返回计算部分的结果。

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

            call函数计算<expression>的结果,<expression>中的变量,如$(1),$(2),$(3),会被parm1,parm2,parm3替换。最后函数返回<expression>的结果。

            例如:

            reverse = $(1) $(2)

            foo = $(call reverse,a,b)

            此时的foo的结果就是“a b”。

            $(origin <variable>)

            origin函数返回变量<variable>是在哪儿定义的。一般不在<variable>中使用$,因为此处指的是变量名。

            函数的返回值有几种情况:

            • “undefined”

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

            • “default”

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

            • “environment”

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

            • “file”

            如果<variable>这个变量被定义在Makefile中。

            • “command line”

            如果<variable>这个变量是被命令行定义的。

            • “override”

            如果<variable>是被override指示符重新定义的。

            • “automatic”

            如果<variable>是一个命令运行中的自动化变量。

            $(shell <command> <var>)

            shell函数的作用是在makefile中调用shell命令来处理变量,返回值就是shell命令执行的结果。比如:

            files := $(shell echo *.c)

            files最后的值就是所有.c文件。

            make的控制函数:

            $(error <text ...>)

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

            $(warning <text ...>)

            产生一个warning信息,并继续执行make。

            GNU makefile中的一些常见目标

                 “all”
                    这个伪目标是所有目标的目标,其功能一般是编译所有的目标。
                 “clean”
                    这个伪目标功能是删除所有被make创建的文件。
                 “install”
                    这个伪目标功能是安装已编译好的程序,其实就是把目标执行文件拷贝到指定的目标中去。
                 “print”
                    这个伪目标的功能是例出改变过的源文件。
                 “tar”
                    这个伪目标功能是把源程序打包备份。也就是一个tar文件。
                 “dist”
                    这个伪目标功能是创建一个压缩文件,一般是把tar文件压成Z文件。或是gz文件。
                 “TAGS”
                    这个伪目标功能是更新所有的目标,以备完整地重编译使用。
                 “check”和“test”
                    这两个伪目标一般用来测试makefile的流程。

            makefile的检查规则

                 “-n”
                “--just-print”
                “--dry-run”
                “--recon”
                不执行参数,这些参数只是打印命令,不管目标是否更新,把规则和连带规则下的命令打印出来,但不执行

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

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

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

            make的参数

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

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

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

            “—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>]”
            指同时运行命令的个数。

            “-k”
            “--keep-going”
            出错也不停止运行。

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

            “-n”
            “--just-print”
            “--dry-run”
            “--recon”
            仅输出执行过程中的命令序列,但并不执行。

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

            “-p”
            “--print-data-base”
            输出makefile中的所有数据,包括所有的规则和变量。

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

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

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

            “-S”
            “--no-keep-going”
            “--stop”
            取消“-k”选项的作用

            隐含规则

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

            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文件(由不同的源文件生成)的也有效。

            隐含规则使用的变量:

            1、关于命令的变量。

            AR
                函数库打包程序。默认命令是“ar”。
            AS
                汇编语言编译程序。默认命令是“as”。
            CC
                C语言编译程序。默认命令是“cc”。
            CXX
                C++语言编译程序。默认命令是“g++”。
            CO
                从 RCS文件中扩展文件程序。默认命令是“co”。
            CPP
                C程序的预处理器(输出是标准输出设备)。默认命令是“$(CC) –E”。

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

            ARFLAGS
                函数库打包程序AR命令的参数。默认值是“rv”。
            ASFLAGS
                汇编语言编译器参数。(当明显地调用“.s”或“.S”文件时)。
            CFLAGS
                C语言编译器参数。
            CXXFLAGS
                C++语言编译器参数。

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

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

            自动化变量

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

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

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

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

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

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

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

            $*
               这个变量表示目标模式中"%"及其之前的部分。

            $(@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)"

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

            隐含规则搜索算法

            比如我们有一个目标叫 T。下面是搜索目标T的规则的算法。请注意,在下面,我们没有提到后缀规则,原因是,所有的后缀规则在Makefile被载入内存时,会被转换成模式规则。如果目标是"archive(member)"的函数库文件模式,那么这个算法会被运行两次,第一次是找目标T,如果没有找到的话,那么进入第二次,第二次会把"member"当作T来搜索。

            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使用。

            一旦规则被找到,就会执行其相当的命令,而此时,我们的自动化变量的值才会生成。

          • 相关阅读:
            简析Java RMI 与 .NET Remoting
            实现一个压缩Remoting传输数据的Sink:CompressionSink (转载)
            智能部署与更新
            第二章:.NET Remoting基础知识
            创建以Microsoft .NET Remoting为基础的分布式应用架构
            vs2005如何防止代码被反编译
            使用.NET Remoting开发分布式应用——配置文件篇(转载)
            Remoting概述
            高级 .NET Remoting
            第一章:Remoting技术简介
          • 原文地址:https://www.cnblogs.com/jaletech/p/3371190.html
          Copyright © 2011-2022 走看看