• Makefile总结


    gcc编译流程

    1、编写C程序源代码

          2、预处理 (加入文件到源文件 include,)

          3、编译 (目标文件.o)

    4、链接  (可执行文件)   

    gcc  选项

    -c
    编译、汇编到目标代码(.o),不进行链接。如上图所示。

    -o outfile
    输出到指定的文件。

    Makefile的内容

    Makefile里主要包含:

    1、显式规则。显式规则说明了,如何生成一个或多个的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。

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

    3、变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点像C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。

    4、文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令

    5、注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。(如果你要在你的Makefile中使用“#”字符,可以用反斜杠进行转义,如:“\#”)

    Makefile的文件名

    大多数的make都支持“makefile”和“Makefile”这两种默认文件名。

    三、makefile如何知道自己该干嘛

    一句话:从目标到依赖关系,从依赖关系到目标,再从目标到依赖关系。

    四、引用其它的Makefile

    include的语法是:

        include <filename>

    注意事项:filename可以是当前操作系统Shell的文件模式(可以包含路径和通配符)

    include前面可以有一些空字符,但是绝不能是[Tab]键开始。include和<filename>可以用一个或多个空格隔开。

    如果文件都没有指定绝对路径或是相对路径的话,make会在当前目录下首先寻找,如果当前目录下没有找到,那么,make还会在下面的几个目录下找:

        1、如果make执行时,有“-I”或“--include-dir”参数,那么make就会在这个参数所指定的目录下去寻找。

        2、如果目录<prefix>/include(一般是:/usr/local/bin或/usr/include)存在的话,make也会去找。

    五、环境变量 MAKEFILES

    如果你的当前环境中定义了环境变量MAKEFILES,那么,make会把这个变量中的值做一个类似于include的动作。这个变量中的值是其它的Makefile,用空格分隔。只是,它和include不同的是,从这个环境变量中引入的Makefile的“目标”不会起作用,如果环境变量中定义的文件发现错误,make也会不理。

    建议不要使用这个环境变量,因为只要这个变量一被定义,那么当你使用make时,所有的Makefile都会受到它的影响。这也提醒我们,也许有时候你的Makefile出现了怪事,那时候就可以看看当前环境中有没有定义这个变量。如果是的话就只需要取消那些环境变量即可。

    六、makefile的的工作过程总结

    GNU的make工作时的执行步骤如下:

        1、读入所有的Makefile。

        2、读入被include的其它Makefile。

        3、初始化文件中的变量。

        4、推导隐晦规则,并分析所有规则。

        5、为所有的目标文件创建依赖关系链。

        6、根据依赖关系,决定哪些目标要重新生成。

        7、执行生成命令。

    其中1-5为第一阶段,6、7为第二阶段。第一阶段中,如果定义跌变量被使用,那么make会把它展开在使用的位置,如果定义出现在依赖关系之中,那么仅当这条依赖被决定需要使用了,变量才会在其内部展开。

    Makefile一般采用的结构式从上而下,下层目标是用来让上层目标保持在最新状态。

    简单程序介绍:

    一、一个makefile的实例

    test:main.o
            gcc -o test main.o

    main.o:main.c
            gcc -c main.c -o main.o

    clean:
            @rm -vf main.o test

    二、Makefile的书写规则

    1)如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。

        2)如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。

        3)如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。

    规则包括两部分,一个是依赖关系,一个是生成目标的方法。

    在Makefile中,规则的顺序是很重要的,Makefile中只应该有一个最终目标,其它的目标都是被这个目标所连带出来的,所以一定要让make知道你的最终目标是什么。一般来说,定义在Makefile中的目标可能会有很多(至少有两个目标),但是第一条规则中的目标将被确立为最终的目标。如果第一条规则中的目标有很多个,那么,第一个目标会成为最终的目标。整个make所完成的也就是这个目标。

    三、规则举例

        foo.o : foo.c defs.h

                gcc -c -g foo.c

    这个规则告诉我们两件事:

    1、文件的依赖关系,foo.o依赖于foo.c和defs.h的文件,如果foo.c和defs.h的时间戳要比foo.o的要新,或是foo.o不存在,那么依赖关系发生。

        2、如果生成(或更新)foo.o文件。也就是那个gcc命令,其说明了,如何生成foo.o这个文件。(当然foo.c文件include了defs.h文件)

    四、规则的语法

    targets : prerequisites

            command

            ...

          或是这样:

          targets : prerequisites ; command

                command

                ...

    Targets(即目标)多数情况下是文件名,以空格分开,可以使用通配符。目标基本上是一个文件,也可以是许多个文件。

    注意:目标可以使用任何名称,并不一定非得是真实的文件名。

    command是命令行,如果其不与“target:prerequisites”在一行,那么,必须以[Tab键]开头,如果和prerequisites在一行,那么可以用分号做为分隔。第二种写法,目前见过的大多数都采用第一种。

    prerequisites也就是目标所依赖的文件(或依赖目标)。如果其中的某个文件要比目标文件要新,那么,目标就被认为是“过时的”,被认为是需要重生成的。

    注意:当冒号的右边没有指定依赖关系时会怎么样——只有在目标代表的文件不存在时才会进行更新的动作。

    如果命令太长(这涉及到代码的规范化问题,即增进代码的可读性),你可以使用反斜框(‘\’)作为换行符。make对一行上有多少个字符没有限制。规则告诉make两件事,文件的依赖关系和如何成成目标文件。

    五、在规则中使用通配符

    如果我们想定义一系列比较类似的文件,即可使用通配符,当意欲创建适应能力较强的makefile时,通配符就很有用了。make支持三个通配符:“*”,“?”和“[...]”。这是和Unix的B-Shell是相同的。波浪号(“~”)字符在文件名中也有比较特殊的用途。如果是“~/test”,这就表示当前用户的$HOME目录下的test目录。通配符代替了一系列的文件,如“*.c”表示所以后缀为c的文件。一个需要我们注意的是,如果我们的文件名中有通配符,如:“*”,那么可以用转义字符“\”,如“\*”来表示真实的“*”字符,而不是任意长度的字符串。
        一个例子:

    clean:
                  rm -f *.o
          很常见的,这是操作系统Shell所支持的通配符。这是在命令中的通配符。
        print: *.c
                  lpr -p $?
                  touch print
           上面这个例子说明了通配符也可以在我们的规则中,目标print依赖于所有的[.c]文件。注意:其中的“$?”是一个自动化变量。
          objects = *.o
        上面这个例子,表示了,通符同样可以用在变量中。并不是说[*.o]会展开,不!objects的值就是“*.o”。Makefile中的变量其实就是C/C++中的宏。如果你要让通配符在变量中展开,也就是让objects的值是所有[.o]的文件名的集合,那么,你可以这样:
        objects := $(wildcard *.o)
        这种用法由关键字“wildcard”指出。

    wildcard把 指定目录 ./ 和 ./sub/ 下的所有后缀是c的文件全部展开。

    六、文件搜寻

    在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当make需要去找寻文件的依赖关系时,你可以在文件前加上路径,但最好的方法是把一个路径告诉make,让make自动去找。

    方法一:

    Makefile文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。

    VPATH = src:../headers

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

    方法二:

    另一个设置文件搜索路径的方法是使用make的“vpath”关键字(注意这里全小写的),这不是变量,这是一个make的关键字,这和上面提到的那个VPATH变量很类似,但是它更为灵活。它可以指定不同的文件在不同的搜索目录中。这是一个很灵活的功能。它的使用方法有三种:

    1、vpath <pattern>; <directories>;

    为符合模式<pattern>;的文件指定搜索目录<directories>;。
           2、vpath <pattern>;
           清除符合模式<pattern>;的文件的搜索目录。
           3、vpath
           清除所有已被设置好了的文件搜索目录。
        vapth使用方法中的<pattern>;需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”结尾的文件。<pattern>;指定了要搜索的文件集,而<directories>;则指定了<pattern>;的文件集的搜索的目录。例如:
        vpath %.h ../headers
    该语句表示,要求make在“../headers”目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话)我们可以连续地使用vpath语句,以指定不同搜索策略。如果连续的vpath语句中出现了相同的<pattern>;,或是被重复了的<pattern>;,那么,make会按照vpath语句的先后顺序来执行搜索。如:
        vpath %.c foo
        vpath %  blish
        vpath %.c bar
    其表示“.c”结尾的文件,先在“foo”目录,然后是“blish”,最后是“bar”目录。
        vpath %.c foo:bar
        vpath %   blish
    而上面的语句则表示“.c”结尾的文件,先在“foo”目录,然后是“bar”目录,最后才是“blish”目录。

    七、伪目标(也称“假想目标”)

    先看这两行代码:

    clean:
                rm *.o temp
        正像我们前面例子中的“clean”一样,我们并不生成“clean”这个文件。“伪目标”并不是一个文件,只是一个标签,由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它是否要执行。我们只有通过显示地指明这个“目标”才能让其生效。当然,“伪目标”的取名不能和文件名重名,不然其就失去了“伪目标”的意义了。
        当然,为了避免和文件重名的这种情况(make无法区分文件形式的目标和伪目标),我们可以使用 “.PHONY”来显示地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”,还让make知道,不应该像处理一般规则那样,从源文件来建立以下以工作目标为名的文件。因此,make可以优化它的一般规则搜索程序以提高性能。
        .PHONY : clean
        只要有这个声明,不管是否有“clean”文件,要运行“clean”这个目标,只有“make clean”这样。于是整个过程可以这样写:
         .PHONY: clean
        clean:
                rm *.o temp
        伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为“默认目标”,只要将其放在第一个。一个示例就是,如果你的Makefile需要一口气生成若干个可执行文件,但你只想简单地敲一个make完事,并且,所有的目标文件都写在一个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
        我们知道,Makefile中的第一个目标会被作为其默认目标。我们声明了一个“all”的伪目标,其依赖于其它三个目标。由于伪目标的特性是,总是被执行的,所以其依赖的那三个目标就总是不如“all”这个目标新。所以,其它三个目标的规则总是会被决议。也就达到了我们一口气生成多个目标的目的。“.PHONY : all”声明了“all”这个目标为“伪目标”。
        随便提一句,从上面的例子我们可以看出,目标也可以成为依赖。所以,伪目标同样也可成为依赖。看下面的例子:
        .PHONY: cleanall cleanobj cleandiff
        cleanall : cleanobj cleandiff
                rm program
        cleanobj :
                rm *.o
        cleandiff :
                rm *.diff
    “make clean”将清除所有要被清除的文件。“cleanobj”和“cleandiff”这两个伪目标有点像“子程序”的意思。我们可以输入“make cleanall”和“make cleanobj”和“make cleandiff”命令来达到清除不同种类文件的目的。

    clean没有依赖模块,因为没有时间标记可供比较,所以它总被执行;它的实际意图是引出后面的rm命令来删除某些目标文件。我们看到rm命令以-开头,这时即使表示make将忽略命令结果,所以即使没有目标供rm命令删除而返回错误时,make clean依然继续向下执行。

     摘自百度文库

  • 相关阅读:
    题解 UVA10213 【How Many Pieces of Land ?】
    NOIP 2018 游记
    POJ 1821 Fence(单调队列优化DP)
    HDU 2196 Computer(经典树形DP)
    POJ 2228 Naptime(DP+环形处理)
    POJ 1742 Coins(多重背包?)
    POJ 2311 Cutting Game(SG函数)
    BZOJ 2560(子集DP+容斥原理)
    HDU2841 Visible Trees(容斥原理)
    HDU 1796 How many integers can you find(容斥原理)
  • 原文地址:https://www.cnblogs.com/wnnily/p/4558815.html
Copyright © 2020-2023  润新知