《Makefile 进阶之路一》 这里有你对Makefile所有的畅想

  由于自身基础薄弱,需要恶补各种知识,makefile作为必须要跨过的坎,使我受尽折磨。网上对makefile的描述通常都非常的肤浅,读完之后往往对大项目的makefile还是一筹莫展。各种教程抄来炒去,查起来非常浪费时间。为了尽快的完成进阶之路,只好手撕makefile手册,并写下一个尽量完整,尽量周到的教程。如果有错误的地方,告诉我,一起进步。

感谢 大神徐海兵 对手册的翻译,如有兴趣直接看手册,请移步百度一下。

----------------------------------------------此线以下为正文

1.1 概述

  Makefile是一个用于描述项目编译、链接规则的文件。使用这个文件可以达到一行命令(make)完成“自动化编译”的功能。Makefile非常重要,Linux 环境下的程序员如果不会使用 GNU make 来构建和管理自己的工程,应该
不能算是一个合格的专业程序员,至少不能称得上是 Unix 程序员。

1.2 准备知识

  一个项目从源码到可执行文件需要两步(以C为例),即编译和链接。编译可以使“.c”文件转化为“.o”文件,链接可以使“.o”文件转化为可执行文件。如下:

  编译:gcc -c hello.c -o hello.o   #这条命令将会生成hello.o文件

  链接:gcc hello.o -o hello          #这条命令将会生成hello(可执行)文件

  在没有Makefile的情况下,我们需要手敲这两行代码来编译源文件,在如此简单的情况下无可厚非,但是如果在一个大项目中,很可能有成百上千个文件,如果采用这种方式,显然是疯狂的。这也是编写Makefile的必要性所在。

1.3 make

  编译、链接一个项目,仅仅有一个Makefile是不够的,还需要一个工具:make。make是一个命令,它在执行时,可以识别Makefile中定义的规则,并根据规则执行相应操作,(尽可能快的)完成项目的编译工作。为什么说make在编译的时候是尽可能快的呢,这是因为它遵守以下规则:

  1. 所有的源文件没有被编译过,则对各个 C 源文件进行编译并进行链接,生成最后的可执行程序;
  2. 每一个在上次执行 make 之后修改过的 C 源代码文件在本次执行 make 时将会被重新编译;
  3. 头文件在上一次执行 make 之后被修改。则所有包含此头文件的 C 源文件在本次执行 make 时将会被重新编译。

1.4 Makefile规则

  Makefile作为项目的管理文件,它主要是由一条条的规则组成的,而每条规则的写法也是有模板的,如下:

  TARGET : PREREQUISITES
    COMMAND

  这就是Makefile中的一条规则的格式,首先,TARGET用于表示这条规则的目标。其次,PREREQUISITES用于表示为了生成这个TARGET所需要的文件(依赖)。最后,COMMAND是一条命令,用于说明如何从PREREQUISITES生成TARGET。make在识别这个规则的时候,首先查目标是什么,然后查所需要的文件,最后执行命令生成目标文件。

  规则包含了文件之间的依赖关系和更新此规则目标所需要的命令

  一个规则可以有多个命令行,每一条命令占一行。注意:每一个命令行必须以[Tab]字符开始,[Tab]字符告诉 make 此行是一个命令行。make 按照命令完成相应的动作。这也是书写 Makefile 中容易产生,而且比较隐蔽的错误。

1.5 示例

  假设我们有三个头文件“.h”,八个C文件“.c”。项目的Makefile如下:

  #sample Makefile
  edit : main.o kbd.o command.o display.o insert.o search.o files.o utils.o
    cc -o edit main.o kbd.o command.o display.o insert.o search.o files.o utils.o
  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
  search.o : search.c defs.h buffer.h
    cc -c search.c
  files.o : files.c defs.h buffer.h command.h
    cc -c files.c
  utils.o : utils.c defs.h
    cc -c utils.c
  clean :
    rm edit main.o kbd.o command.o display.o
      insert.o search.o files.o utils.o

  首先书写时,可以将一个较长行使用反斜线()来分解为多行,这样可以使我们的Makefile书写清晰、容易阅读理解。但需要注意:反斜线之后不能有空格(这也是大家最容易犯的错误,错误比较隐蔽)。推荐将一个长行分解为使用反斜线连接得多个行的方式。在完成了这个Maekfile以后;需要创建可执行程序“edit”,所要做的就是在包含此Makefile的目录(当然也在代码所在的目录)下输入命令“make”。删除已经此目录下之前使用“make”生成的文件(包括那些中间过程的.o文件),也只需要输入命令“make clean”就可以了。

  命令就是在任何一个目标的依赖文件发生变化后重建目标的动作描述。一个目标可以没有依赖而只有动作(指定的命令)。比如 Makefile 中的目标“clean”,此目标没有依赖,只有命令。它所定义的命令用来删除 make 过程产生的中间文件(进行清理工作)。目标“clean”不是一个文件,它仅仅代表执行一个动作的标识。正常情况下,不需要执行这个规则所定义的动作,因此目标“clean”没有出现在其它任何规则的依赖列表中。因此在执行make时,它所指定的动作不会被执行。除非在执行make时明确地
指定它。需要执行“clean”目标所定义的命令,可在shell下输入:make clean。

1.6 make 如何工作

  默认的情况下,make执行的是Makefile中的第一个规则,此规则的第一个目标称之为“最终目的”或者“终极目标”。上例的 Makefile,目标“edit”在 Makefile 中是第一个目标,因此它就是 make 的“终极目标”。

  当在 shell 提示符下输入“make”命令以后。make 读取当前目录下的 Makefile 文件,并将 Makefile 文件中的第一个目标作为其执行的“终极目标”,开始处理第一个规则(终极目标所在的规则)。在我们的例子中,第一个规则就是目标“edit”所在的规则。规则描述了“edit”的依赖关系,并定义了链接.o 文件生成目标“edit”的命令; make在执行这个规则所定义的命令之前,首先处理目标“edit”的所有的依赖文件(例子中的那些.o 文件)的更新规则(以这些.o 文件为目标的规则)。对这些.o 文件为目标的规则处理有下列三种情况:
  1. 目标.o 文件不存在,使用其描述规则创建它;
  2. 目标.o 文件存在,目标.o 文件所依赖的.c 源文件、 .h 文件中的任何一个比目标.o文件“更新”(在上一次 make 之后被修改)。则根据规则重新编译生成它;
  3. 目标.o 文件存在,目标.o 文件比它的任何一个依赖文件(的.c 源文件、.h 文件)“更新”(它的依赖文件在上一次 make 之后没有被修改),则什么也不做。
  这些.o 文件所在的规则之所以会被执行,是因为这些.o 文件出现在“终极目标”的依赖列表中。在 Makefile 中一个规则的目标如果不是“终极目标”所依赖的(或者“终极目标”的依赖文件所依赖的),那么这个规则将不会被执行,除非明确指定执行
这个规则(可以通过 make 的命令行指定重建目标,那么这个目标所在的规则就会被执行,例如 “make clean”)。

完成了对.o 文件的创建(第一次编译)或者更新之后,make 程序将处理终极目标“edit”所在的规则,分为以下三种情况:
  1. 目标文件“edit”不存在,则执行规则以创建目标“edit”。
  2. 目标文件“edit”存在,其依赖文件中有一个或者多个文件比它“更新”,则根据规则重新链接生成“edit”。
  3. 目标文件“edit”存在,它比它的任何一个依赖文件都“更新”,则什么也不做。

  总结一下:对于一个 Makefile 文件,“make”首先解析终极目标所在的规则(例子中的第一个规则),根据其依赖文件(例子中第一个规则的 8 个.o 文件)依次(按照依赖文件列表从左到右的顺序)寻找创建这些依赖文件的规则。首先为第一个依赖文件(main.o)寻找创建规则,如果第一个依赖文件依赖于其它文件(main.c、defs.h),则同样为这个依赖文件寻找创建规则(创建 main.c 和 defs.h 的规则,通常源文件和头文件已经存在,也不存在重建它们的规则)......,直到为所有的依赖文件找到合适的创建规则。之后 make 从最后一个规则(上例目标为 main.o 的规则)回退开始执行,最终完成终极目标的第一个依赖文件的创建和更新。之后对第二个、第三个、第四个......终 极 目 标 的 依 赖 文 件 执 行 同 样 的 过 程 ( 上 例 的 的 顺 序 是 “ main.o ”、“ kbd.o ”、“command.o”......)。

  更新(或者创建)终极目标的过程中,如果任何一个规则执行出现错误 make 就立即报错并退出。整个过程 make 只是负责执行规则,而对具体规则所描述的依赖关系的正确性、规则所定义的命令的正确性不做任何判断。就是说,一个规则的依赖关系是否正确、描述重建目标的规则命令行是否正确,make 不做任何错误检查。

----------------------------------------------此线以下非正文

  到这里呢,我们基本上就明白最简单的Makefile是如何编写的了,由于以上讲解并没有对真正的实例写Makefile,可能刚刚入手的同行还是不太明白(我就是这样)。我在这里写了一个helloworld程序和Makefile。以下是代码:

1 #include<stdio.h>
2 int main(int argc,char** argv){
3     printf("hello world!
");
4     return 0;
5 }

  以下是Makefile:

1 hello:hello.o
2         gcc hello.o -o hello
3 hello.o:hello.c
4         gcc -c hello.c -o hello.o
5 clean:
6         rm hello.o 
7         rm hello

  将hello.c和Makefile放入相同文件夹。打开终端输入make,就会生成hello.o和hello。hello就是最后的可执行程序。输入make clean就会删掉生成的文件。效果如下图:

尊重原创,转载请注明出处

原文地址:https://www.cnblogs.com/PPWEI/p/10547840.html