1.1 make概述
Linux环境下的程序员如果不会使用GNU make来构建和管理自己的工程,应该不能算是一个合格的专业程序员,至少不能称得上是Unix程序员。在Linux(unix)环境下使用GNU 的make工具能够比较容易的构建一个属于你自己的工程,整个工程的编译只需要一个命令就可以完成编译、连接以至于最后的执行。不过这需要我们投入一些时间去完成一个或者多个称之为Makefile文件的编写。此文件正是make正常工作的基础。
所要完成的Makefile文件描述了整个工程的编译、连接等规则。其中包括:工程中的哪些源文件需要编译以及如何编译、需要创建那些库文件以及如何创建这些库文件、如何最后产生我们想要得可执行文件。尽管看起来可能是很复杂的事情,但是为工程编写Makefile的好处是能够使用一行命令来完成“自动化编译”,一旦提供一个(通常对于一个工程来说会是多个)正确的Makefile。编译整个工程你所要做的唯一的一件事就是在shell 提示符下输入make命令。整个工程完全自动编译,极大提高了效率。
make是一个命令工具,它解释Makefile中的指令(应该说是规则)。在Makefile文件中描述了整个工程所有文件的编译顺序、编译规则。Makefile有自己的书写格式、关键字、函数。像C语言有自己的格式、关键字和函数一样。而且在Makefile中可以使用系统shell所提供的任何命令来完成想要的工作。Makefile(在其它的系统上可能是另外的文件名)在绝大多数的IDE开发环境中都在使用,已经成为一种工程的编译方法。
目前,系统完整的介绍make工具和如何编写Makefile的中文文档比较少。我整理这个文档就是希望能使众多的Linux环境下的程序员能够比较容易的掌握和学会使用GNU make。本文所要介绍的是GNU的make,采用Red Hat FC3(包括最新发布的GNU Linux系统)所集成的GUN make工具。
本文中所有示例均采用C语言的源程序,因为它是目前最普遍使用的一种语言。当然make工具不仅仅是用来管理C语言工程的,那些编译器只要能够在shell下运行的语言所构建的工程都可以使用make工具来管理。Make工作不仅仅可以用来编译源代码,它也可以完成一些其它的功能。例如,有这样的需求:当我们修改了某个或者某些文件后,需要能够根据修改的文件来自动对相关文件进行重建或者更新。那么应该考虑使用GNU make工具。GNU make工具为我们实现这个目的提供了非常有利的支持。工程中根据源文件的修改情况来进行代码的编译正是使用了make的这个特征。make执行时,根据Makefile的规则检查文件的修改情况,决定是否执行定义的动作(那些修改过的文件将会被重新编译)。这是GNU make的执行依据。
1.2 准备知识
在开始我们关于make的讨论之前,首先需要明确一些基本概念:
编译:把高级语言书写的代码转换为机器可识别的机器指令。编译高级语言后生成的指令虽然可被机器识别,但是还不能被执行。编译时,编译器检查高级语言的语法、函数与变量的声明是否正确。只有所有的语法正确、相关变量定义正确编译器就可以编译出中间目标文件。通常,一个高级语言的源文件都可对应一个目标文件。目标文件在Linux中默认后缀为“.o”(如“foo.c”的目标文件为“foo.o”)。
为了和规则的目标文件相区别。本文将编译高级语言后生成的目标文件成为.o文件。
链接:将多.o文件,或者.o文件和库文件链接成为可被操作系统执行的可执行程序(Linux环境下,可执行文件的格式为“ELF”格式)。链接器不检查函数所在的源文件,只检查所有.o文件中的定义的符号。将.o文件中使用的函数和其它.o或者库文件中的相关符号进行合并,对所有文件中的符号进行重新安排(重定位),并链接系统相关文件(程序启动文件等)最终生成可执行程序。链接过程使用GNU 的“ld”工具。
静态库:又称为文档文件(Archive File)。它是多个.o文件的集合。Linux中静态库文件的后缀为“.a”。静态库中的各个成员(.o文件)没有特殊的存在格式,仅仅是一个.o文件的集合。使用“ar”工具维护和管理静态库。
共享库:也是多个.o文件的集合,但是这些.o文件时有编译器按照一种特殊的方式生成(Linux中,共享库文件格式通常为“ELF”格式。共享库已经具备了可执行条件)。模块中各个成员的地址(变量引用和函数调用)都是相对地址。使用此共享库的程序在运行时,共享库被动态加载到内存并和主程序在内存中进行连接。多个可执行程序可共享库文件的代码段(多个程序可以共享的使用库中的某一个模块,共享代码,不共享数据)。另外共享库的成员对象可被执行(由libdl.so提供支持)。
参考 info ld了解更加详细的关于ld的说明和用法。