简单的Makefile文件

make工具需要一个由开发人员创建的输入文件来描述将要生成的项目。 GNUmake使用Makefile作为这个输入文件的默认文件名。因此,输入make命令调用make工具时,会在当前目录寻找一个名为Makefile的文件来指导make工具如何生成项目。下面的清单展示了如何使用最基本的Makefile文件来生成这个简单的项目。

  1. appexp: main.o app.o bar.o lib.o
  2.               gcc -o appexp main.o app.o bar.o lib.o
  3.  
  4. maino: src/main.c src/lib.h src/app.h
  5.             gcc -c -o main.o src/main.c
  6.  
  7. app.o: src/app.c src/lib.h src/app.h
  8.             gcc -c -o app.o src/app.c

  9. bar.o: src/bar.c src/lib.h
  10.            gcc -c -o bar.o src/bar.c
  11.  
  12. lib.o: src/lib.c src/lib.h
  13.          gcc -c -o lib.o src/lib.c
从第一行就可以了解Makefile语法的基本结构: 规则(rule)。 规则语句的冒号前的部分叫“目标”, 冒号后面的部分叫“依赖“。 规则后面常常跟随着将依赖条件转化为目标的命令。以第一行为例, 规则指明了目标是appexp应用程序, 这个目标的生成依赖于main.o, app.o,bar.o和lib.o的存在。第二行则给出了将main.o,app.o, bar.o和lib.o这4个文件链接成可执行文件appexp的命令。注意,Makefile语法中有一个特别的规定: Makefile文件中的命令的前面要加一个硬的制表符(Tab), make工具就是通过这个制表符把命令和其他的Makefile内容区别开来。make工具通过输入文件中指明的规则来决定如何生成项目。 通过命令调用make工具时, 这个工具首先解析Makefile文件,找到所有的目标规则。然后它会尝试生成名为all的目标;如果没有名为all的目标, 那么make会生成它在Makefile文件中遇到的第一个目标。
就上面的清单而言,目标程序是appexp应用程序, 因为它的规则最先出现在Makefile文件中的(第一行)。 Makefile规则语法最优雅的地方在于make工具可以把这些规则链接到一起,生成 整个完整的生成过程。调用make工具时, 他首先尝试生成默认规则:示例Makefile中的一条规定。 第一行的这个规定告诉make工具生成的目标是appexp,它必须有如下4个文件才能完成这个目标:main.o, app.o, bar.o和lib.o。 make工具会检查那些文件是否存在以判定它是否具备了生成目标应用程序的必备条件。如果某个需求的文件缺失或者该依赖文件比目标文件新, make工具就会开始查找生成那个文件的规则。
找到那个文件的规则后, 就会开始检查这条新的规则的要求条件是否具备。就这样,make工具把这些规则链接到一起,形成生成最初的那个目标所需要的依赖树。然后make工具从该树的叶节点的规则对应的命令开始执行, 逐级生成更高级节点所需要的必备条件,直到该树的根节点。在这个示例Makefile中,这个过程按照默认的设置从第一行的规则开始。如果需要的目标文件尚不存在,make工具就会去寻找创建他们所需要的规则。首先,他会找到出现在第四行的main.o的规则。然后,make工具会检查第四行这条规则所要求的main.c,lib.h和app.h文件,确认它们都已经存在,然后就会执行创建main.o的命令(第五行)。接下来创建的必须文件是app.o,make工具会找到并检查第七行的规则, 然后执行第八行的命令。然后继续这个过程创建bar.o和lib.o,最后执行第二行的命令创建这个应用程序。如果你在一个”清洁过的“目录(没有编译生成的目标文件)中执行make命令,make工具会执行如下一系列的命令
  1. gcc -c -o main.o src/main.c
  2. gcc -c -o app.o src/app.c
  3. gcc -c -o bar.o src/bar.c 
  4. gcc -c -o lib.o src/lib.c
  5. gcc -c -o appexp main.o app.o bar.o lib.o
对比这一系列命令和清单所示的脚本,你会发现自己已经使用GNUmake重新实现了这个简单的生成脚本。为什么要使用GUNmake工具来取代生成脚本呢? 和使用生成脚本的方式相比,make工具毕竟显得更加复杂。起原因在于GUNmake可以根据Makefile文件设定的依赖关系进行增量版本生成。比如说在生成玩appexp程序之后,app.c源文件中发现了一个错误。为了修正这个错误,需要修改app.c源文件然后重新生成可执行文件。如果采用如下脚本:
  1. #!/bin/sh
  2. # Build the chapter 6 example preject
  3.  
  4. gcc -c -o main.o src/main.c
  5. gcc -c -o app.o src/app.c 
  6. gcc -c -o bar.o src/bar.c
  7. gcc -c -o lib.o src/lib.c
  8. gcc -o appexp main.o app.o bar.o lib.o
那么全部源文件都将重新编译为目标文件,然后这些目标文件被链接为可执行文件。而使用make工具的话,这个重新生成的过程将只执行下面这两条命令:
  1. gcc -c -o app.o src/app.c
  2. gcc -o appexp main.o app.o bar.o lib.o
GNUmake是怎么认定其他的命令不执行呢?因为make工具能够理解可执行文件生成过程的每一步。通过对文件数据的检查,它确认main.o,bar.o和lib.o的依赖树中没有变化,因此这些目标文件不需要重新生成。而在检查app.o时发现它的一个名为app.c的依赖文件发生了变化,因此确认app.o需要重新生成。如果理解了Makefile所表达的依赖树,那么你就明白了make工具之于一个简单的生成脚本的优势。

你可能感兴趣的:(简单的Makefile文件)