有关makefile

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_39371711/article/details/82620342

先复习下​​​​​​c语言编译程序的过程:

转自https://www.mianfeiwendang.com/doc/15144a2ebbfbff782e76fc8d/2

1. C源程序
2. 预编译处理(.c)主要包括四个过程

  • 宏定义指令,如#define N 6,#undef等。对于前一个伪指令,预编译所要做的是将程序中的所有N用6替换,请大家注意这里是替换,并不是像作为函数参数那样将6复制进N这个变量。对于后者,则将取消对某个宏的定义,使以后出现的N不再被替换。
  • 条件编译指令,如#ifdef,#ifndef,#endif等。 这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。这样就能在编译阶段减少编译时间,提高效率。
  •  头文件包含指令,如#include "file.h"或#include 等。

    在头文件中一般用伪指令#define定义了大量的宏(最常见的是字符常量);

    同时采用这样的做法可以让我们直接调用一些复杂库函数;

    还可以免去我们在写包含有各种外部符号的声明。 程序时重复做一些定义声明工作的麻烦。试想一下,一旦我们写好头文件,那么以后要用到相关模块就再也不用写这些函数了,直接#include 就OK了。

    #include<>与#include"  "的区别。

    #include<>:这条指令就是告诉编译器去系统默认的路径寻找相关文件。

    #include"  " :这条是告诉编译器先去源程序所在目录下寻找,如果没有就去系统默认路径寻找。

  • 特殊符号,预编译程序可以识别一些特殊的符号。 例如在源程序中出现的LINE标识将被解释为当前行号(十进制数),FILE则被解释为当前被编译的C源程序的名称。预编译程序就是对在源程序中出现的这些特殊符号将用合适的值进行替换。

        预编译阶段基本上是完成对源程序的相关代码进行替换,这样之后程序的原意没有改变,就是代码的内容有所不同,这样为以后的编译做好准备!

3. 编译、优化程序(.s、.asm)
        经过预编译处理,现在我们的程序已经没有宏定义,包含头文件等指令了,只剩下一些变量,常量,关键字等,而编译的主要作用是检查这些代码的语法错误及将这些代码编译成为汇编文件。

        我们有.c文件和.h文件,编译的时候编译的是.c文件(每一个.c文件对应一个.s文件),是和.h文件没有关系的,.h文件是预处理的时候展开加进.c文件里的。

4. 汇编程序(.obj、.o、.a、.ko)
      在这个阶段是将汇编代码翻译成目标文件,这时的文件已经是二进制代码了。在windows环境下文件的后缀名是.obj;而在unix下则有是o、.a、.ko等文件。目标文件由段组成。通常一个目标文件中至少有两个段:前面文章中说的代码段(text)和数据段(data);

       代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。
       数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的

5. 链接程序(.exe、.elf、.axf)
       也许有人会有疑问,上面的目标代码已经是机器码了,也就是说CPU可以识别这些文件了,那为什么我们还要链接程序呢?大家想想我们是不是忘了点什么。。。对!那些被包含的头文件,以及当我们的程序分布于很多源文件时,那么这些源文件该怎么处理呢,这就是连接器的作用,它们被翻译成目标代码后需要被链接到一起才能被执行。
        谈到函数库的链接,我们还需要了解点函数库的知识,函数库分静态链接库(又称静态库*.lib)和链接动态库(又称动态库*.dll)。静态库的链接在编译时会被编译进汇编文件,这样的操作会改变文件大小;而动态库则是在执行时(双击运行),当需要动态库中的文件时才被链接到可执行文件的。                                                                                                                                        编译是一次只编译一个文件,链接是一次性将所有的.o,.a将交叉引用都解决掉,除了用ld链接,也可以用gcc将两个。o链接成可执行文件;

makefile

        小程序的话单个c文件就可以编成可执行文件;                                                                                                                                  代码越来越多的时候,可能需要多个人去修改维护,所以把一个工程按照不同的功能分成许多个.c文件,放在若干目录里,那么编译的时间就会越来越长,因为就算只改了一个文件也会都从头编译一遍,这种情况下产生了makefile文件,makefile定义了一系列的规则,来指定哪些文件需要先编译,哪些文件后编译,哪些文件重新编译。makefile最大的好处就是自动化编译。一旦写好,只需要一个make命令,整个过程都自动编译。极大提高开发的效率。

makefile就像一个工程管理脚本,一个makefile文件包括两块内容:工程结构(文件、依赖关系)、指令(如何编译、创建)

也就是说makefile的内容包括:要对什么文件进行操作,如何操作;我们的规则是:
    1)如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
    2)如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。
    3)如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。

make命令用于解析makefile,并不局限于.c文件编译,

makefile并像程序一样从上到下一行行的执行的

 Makefile 主要的 5个部分 (显示规则, 隐晦规则, 变量定义, 文件指示, 注释)

  1. 显式规则 :: 说明如何生成一个或多个目标文件(包括 生成的文件, 文件的依赖文件, 生成的命令),由makefile的书写者明显指出
  2. 隐晦规则 :: make的自动推导功能所执行的规则
  3. 变量定义 :: Makefile中定义的变量
  4. 文件指示 :: Makefile中引用其他Makefile; 指定Makefile中有效部分; 定义一个多行命令
  5. 注释     :: Makefile只有行注释 "#", 如果要使用或者输出"#"字符, 需要进行转义, "\#"

1.显式规则 :

target ... : prerequisites ...
    command                   //rule,规则前面必须是Tab键,
    ...
  • target              - 目标文件, 可以是 Object File, 也可以是可执行文件目标,prerequisites - 生成 target 所需要的文件或者目标这一行是依赖关系
  • command       - make需要执行的命令 (任意的shell命令), Makefile中的命令必须以 [tab] 开头,这样make才会把这一行看成命令,这一行说的是规则

make命令从上往下读取makefile的时候,它碰见的第一个目标就是最终目标,先检查看all有没有,没有的话就看它的依赖,也就是top,这个时候再查看top的依赖,这样依次进行,生成了一棵树,

  • 当修改了某一个.c或者.h之后运行make指令,
  • make就去检查依赖,一检查依赖都在,
  • make就去检查各个依赖的时间戳,正常情况下target的生成时间戳应该比它的依赖文件的生成时间戳要晚,但是改了.c/.h之后,这些.c/.h的时间戳就比它们对应的.target .o文件晚了,这个时候,make就会只编译不符合这个规则的依赖;

猜你喜欢

转载自blog.csdn.net/weixin_39371711/article/details/82620342