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

  • 相关阅读:
    LeetCode——376.摆动序列
    kaggle——分销商产品未来销售情况预测
    LeetCode——264. 丑数 II
    LeetCode——71.简化路径
    LeetCode——15. 三数之和
    kaggle——NFL Big Data Bowl 2020 Official Starter Notebook
    LeetCode——199. 二叉树的右视图
    数据结构与算法——哈希函数和哈希表等(2)
    数据结构与算法——哈希函数与哈希表等(1)
    Python——Pandas 时间序列数据处理
  • 原文地址:https://www.cnblogs.com/52php/p/5681729.html
Copyright © 2011-2022 走看看