• 如何编写 Makefile


     1. 目标 依赖 命令

     make会比较targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的话,那么,make就会执行后续定义的命令。

    2. 示例一

    在Makefile中的命令,必须要以[Tab]键开始。

    #include<stdio.h>
    #include"add.h"
    
    int main()
    {
        int result=0;
        result = add(2, 3);
        printf("2+3=%d
    ", result);
    }
    main: main.o add.o
    gcc -o main main.o add.o
    main.o : main.c add.h
        gcc -c main.c
    add.o : add.c add.h
        gcc -c add.c 
    clean:
        rm *.o
    


     在默认的方式下,也就是我们只输入make命令。那么,
    1、make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
    2、如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文件,并把这个文件作为最终的目标文件。
    3、如果edit文件不存在,或是edit所依赖的后面的 .o 文件的文件修改时间要比edit这个文件新,那么,他就会执行后面所定义的命令来生成edit这个文件。
    4、如果edit所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程)
    5、当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件生命make的终极任务,也就是执行文件edit了。

     

    3. 示例二(变量申明)

    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 : 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


     4. 自动推导,隐晦规则

    只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果make找到一个whatever.o,那么whatever.c,就会是whatever.o的依赖文件。并且 cc -c whatever.c 也会被推导出来,于是,我们的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)

    5.

    GNU的make工作时的执行步骤入下:(想来其它的make也是类似)
        1、读入所有的Makefile。
        2、读入被include的其它Makefile。
        3、初始化文件中的变量。
        4、推导隐晦规则,并分析所有规则。
        5、为所有的目标文件创建依赖关系链。
        6、根据依赖关系,决定哪些目标要重新生成。
        7、执行生成命令。

    当make需要去找寻文件的依赖关系时,你可以在文件前加上路径,但最好的方法是把一个路径告诉make,让make在自动去找。Makefile文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。

    VPATH = src:../headers

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

    %.c是GUNmake语法层,表示的是所有以".c"结尾的文件,而*.c则是linux shell 语法层的,比如:rm -f *.o 类似的。

    objects = foo.o bar.o
    all: $(objects)
    $(objects): %.o: %.c
    $(CC) -c $(CFLAGS) $< -o $@

    上面的例子中,指明了我们的目标从$object中获取,“%.o”表明要所有以“.o”结尾的目标,也就是“foo.o bar.o”,也就是变量$object集合的模式,而依赖模式“%.c”则取模式“%.o”的“%”,也就是“foo bar”,并为其加下“.c”的后缀,于是,我们的依赖目标就是“foo.c bar.c”。而命令中的“$<”和“$@”则是自动化变量,“$<”表示所有的依赖目标集(也就是“foo.c bar.c”),“$@”表示目标集(也就是“foo.o bar.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


    有些时候,命令的出错并不表示就是错误的。例如mkdir命令,我们一定需要建立一个目录,如果目录不存在,那么mkdir就成功执行,万事大吉,如果目录存在,那么就出错了。我们之所以使用mkdir的意思就是一定要有这样的一个目录,于是我们就不希望mkdir出错而终止规则的运行。
    为了做到这一点,忽略命令的出错,我们可以在Makefile的命令行前加一个减号“-”(在Tab键之后),标记为不管命令出不出错都认为是成功的。如:
    clean:
    -rm -f *.o
     

    6. 定义命令包

    如果Makefile中出现一些相同命令序列,那么我们可以为这些相同的命令序列定义一个变量。定义这种命令序列的语法以“define”开始,以“endef”结束,如:

    define run-yacc
    yacc $(firstword $^)
    mv y.tab.c $@
    endef

    这里,“run-yacc”是这个命令包的名字,其不要和Makefile中的变量重名。在“define”和“endef”中的两行就是命令序列。这个命令包中的第一个命令是运行Yacc程序,因为Yacc程序总是生成“y.tab.c”的文件,所以第二行的命令就是把这个文件改改名字。还是把这个命令包放到一个示例中来看看吧。

    foo.c : foo.y
    $(run-yacc)

    我们可以看见,要使用这个命令包,我们就好像使用变量一样。在这个命令包的使用中,命令包“run-yacc”中的“$^”就是“foo.y”,“$@”就是“foo.c”(有关这种以“$”开头的特殊变量,我们会在后面介绍),make在执行命令包时,命令包中的每个命令会被依次独立执行。

  • 相关阅读:
    失控
    组织要登信息化这趟高铁要花多少钱买票?
    信息工作的技术(物理)平台
    IT服务系统组成
    传法授业讲缘分
    做好每周工作总结很重要
    编程:对经验世界的析构与建构
    你在哪编程?你的程序原料是什么?
    人的格局与人的底线
    5方与5W
  • 原文地址:https://www.cnblogs.com/xj626852095/p/3648247.html
Copyright © 2020-2023  润新知