zoukankan      html  css  js  c++  java
  • make 和 makefile 的关系

    程序的 编译 和 链接

    要先总结 make 和 makefile,就需要先了解下面这个过程:

    1. 预编译:也叫预处理,进行一些文本替换工作,比如将 #define 定义的内容,在代码中进行替换;
    2. 编译:将预处理得到的代码,进行词法分析、语法分析、中间代码……;如果是在Windows下,中间代码就是 .obj 文件;在Linux系统下,中间代码就是 .o 文件;
    3. 汇编:将编译得到的汇编代码,通过汇编程序得到 0 和 1 机器语言;
    4. 链接:链接各种静态链接库和动态链接库得到可执行文件。

    make 和 makefile 能干啥?

    一个工程,那么多源文件,一堆的 cpp 和 h 文件,怎么编译啊?编译一个大型工程,如果Rebuild可能就需要好几个小时,甚至十几个小时,那我们就可能要问了。

    1. 如何像VS那样,一键就能编译整个项目?
    2. 如何修改了哪个文件,就编译修改的那个文件,而不是重新编译整个工程?

    好吧,make 和 makefile 就能搞定这些。makefile 定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile 就像一个Shell脚本一样,其中也可以执行操作系统的命令。makefile 带来的好处就是——“自动化编译”,一旦写好,只需要一个 make 命令,整个工程完全自动编译,极大的提高了软件开发的效率。

    make 是一个命令工具,是一个解释 makefile 中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux 下 GNU 的 make。可见,makefile 都成为了一种在工程方面的编译方法。make 命令执行时,需要一个 makefile 文件,以告诉 make 命令需要怎么样的去编译和链接程序。

    现在,应该明白了吧。make 是一个命令,用来解析 makefile 文件;makefile 是一个文件,用来告诉 make 命令,如何编译整个工程,生成可执行文件。再打个比方:

    导演 == make

    剧本 == makefile

    演员 == MAKE调用的外部命令,如编译器、链接器等

    电影 == 生成的程序

     

    解决问题举例

    怎么就出现了make这个东西了呢?还记得你入门C语言时,写下的Hello World程序么?

    #include <stdio.h>
    int main()
    {
        printf("Hello World
    ");
        return 0;
    }

    当你在终端中输入 gcc HelloWorld.c 命令时,就会生成一个 a.out 文件(如果不用 -o 参数指定输出文件名的话,默认为 a.out),然后就可以神奇的使用 ./a.out 执行该文件,打印出了 Hello World。这是一件让初学者兴奋的事情。问题来了,现在就仅仅是一个 HelloWorld.c 文件,如果有多个代码文件,而多个代码文件之间又存在引用关系,这个时候,该如何去编译生成一个可执行文件呢?比如现在有一下源文件:

    add.h
    add.c
    sub.h
    sub.c
    mul.h
    mul.c
    divi.h
    divi.c
    main.c

    这些代码文件的定义分别如下:

    add.h 文件

    #ifndef _ADD_H_
    #define _ADD_H_
    int add(int a, int b);
    #endif

    add.c 文件

    #include "add.h"
    int add(int a, int b)
    {
        return a + b;
    }

    sub.h 文件

    #ifndef _SUB_H_
    #define _SUB_H_
    int sub(int a, int b);
    #endif

    sub.c 文件

    #include "sub.h"
    int sub(int a, int b
    {
        return a - b;
    }

    mul.h 文件

    #ifndef _MUL_H_
    #define _MUL_H_
    int mul(int a, int b);
    #endif

    mul.c 文件

    #include "mul.h"
    int mul(int a, int b)
    {
        return a * b;
    }

    divi.h 文件

    #ifndef _DIVI_H_
    #define _DIVI_H_
    int divi(int a, int b);
    #endif

    divi.c 文件

    #include "divi.h"
    int divi(int a, int b)
    {
        if (b == 0)
        {
            return 0;
        }
        return a / b;
    }

    main.c 文件

    #include <stdio.h>
    #include "add.h"
    #include "sub.h"
    #include "mul.h"
    #include "divi.h"
    
    int main()
    {
        int a = 10;
        int b = 2;
    
        printf("%d + %d = %d
    ", a, b, add(a, b));
        printf("%d - %d = %d
    ", a, b, sub(a, b));
        printf("%d * %d = %d
    ", a, b, mul(a, b));
        printf("%d / %d = %d
    ", a, b, divi(a, b));
        return 0;
    }

    你也看到了,在 main.c 中要引用这些文件,那现在如何编译,生成一个可执行文件呢?

    最笨的解决方法

    最笨的解决方法就是依次编译所有文件,生成对应的 .o 目标文件。参考如下:

    $ gcc -c sub.c -o sub.o
    $ gcc -c add.c -o add.o
    $ gcc -c sub.c -o sub.o
    $ gcc -c mul.c -o mul.o
    $ gcc -c divi.c -o divi.o
    $ gcc -c main.c -o main.o

    然后再使用如下命令对所生成的单个目标文件进行链接,生成可执行文件。

    $ gcc -o main add.o sub.o mul.o divi.o main.o

    然后就可以得到一个可执行程序 main,可以直接使用 ./main 进行运行。还不错,虽然过程艰辛,至少也可以得到可执行程序。那么有没有比这更简单的方法呢?如果一个项目,几千个文件,这么写下去,还不得累死人啊。办法是有的,我接着总结。

    使用makefile文件

    使用上面那种最笨的办法,效率是非常低得,当添加新的文件,或者修改现有文件时,维护起来也是非常难得。基于此,现在就来说说使用 makefile 文件来搞定这一切。

    关于什么是 makefile,在文章的开头我就已经总结了,至于它和 make 的关系,在文章的开头也说的非常清楚了,现在就来看看如何使用 makefile 来完成上面同样的任务,生成一个 main 的可执行文件。

    #target:dependency-file
    main:main.o add.o sub.o mul.o divi.o
        gcc -o main main.o add.o sub.o mul.o divi.o
    main.o:main.c add.h sub.h mul.h divi.h
        gcc -c main.c -o main.o
    add.o:add.c add.h
        gcc -c add.c -o add.o
    sub.o:sub.c sub.h
        gcc -c sub.c -o sub.o
    mul.o:mul.c mul.h
        gcc -c mul.c -o mul.o
    divi.o:divi.c divi.h
        gcc -c divi.c -o divi.o
    clean:
        rm -f *.o

    上面就是 makefile 文件的内容,对于 makefile 的内容的编写规则,这里先不说。

    现在你可以在 makefile 的同目录下执行 make 命令,然后就可以看到生成了一堆 .o 目标文件,还有那个可执行的 main 文件;接着运行 make clean,那些 .o 文件就全部被删除了。为什么是这样?好了,你先照着做一遍吧。

     

    makefile文件编写规则

    上面只是给出了一个简单的 makefile 文件,你肯定好奇这个 makefile 的书写规则是什么样子的?

    makefile 的规则大体上就是以下格式:

    target 是一个目标文件,可以是 Object File(.o文件),也可以使最终的执行文件,而 dependency-file 是生成对应 target 所需要依赖的文件或者其它的 target,command 就是最终由 make 执行的命令。

    上面说了一段话,简短而言就是:生成一个 target,需要依赖的文件,而使用命令来将依赖文件生成对应的 target 的规则,是在 command 中定义的。如果 dependency-file 中有一个或者多个文件比 target 文件要新的话,command 所定义的命令就会被执行,这就是 makefile 的规则,也就是 makefile 最核心的内容。

    makefile 文件中可以定义变量,可以使用函数,还有各种判断,内容繁多,这里就不一一总结了,更详细的介绍,可以看看大牛陈皓的系列博客《跟我一起写makefile》。

    参考:

    http://www.jellythink.com/archives/810?utm_source=tuicool&utm_medium=referral

    http://bbs.csdn.net/topics/390143962

  • 相关阅读:
    poj 3616 Milking Time
    poj 3176 Cow Bowling
    poj 2229 Sumsets
    poj 2385 Apple Catching
    poj 3280 Cheapest Palindrome
    hdu 1530 Maximum Clique
    hdu 1102 Constructing Roads
    codeforces 592B The Monster and the Squirrel
    CDOJ 1221 Ancient Go
    hdu 1151 Air Raid(二分图最小路径覆盖)
  • 原文地址:https://www.cnblogs.com/52php/p/5681729.html
Copyright © 2011-2022 走看看