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

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

          • 相关阅读:
            LeetCode——Generate Parentheses
            LeetCode——Best Time to Buy and Sell Stock IV
            LeetCode——Best Time to Buy and Sell Stock III
            LeetCode——Best Time to Buy and Sell Stock
            LeetCode——Find Minimum in Rotated Sorted Array
            Mahout实现基于用户的协同过滤算法
            使用Java对文件进行解压缩
            LeetCode——Convert Sorted Array to Binary Search Tree
            LeetCode——Missing Number
            LeetCode——Integer to Roman
          • 原文地址:https://www.cnblogs.com/jaletech/p/3371190.html
          Copyright © 2011-2022 走看看