爱生活,爱阅读
分类: LINUX
2012-09-22 23:17:34
下Makefile教程
1. Make命令
1. 一个简单的
2. 编译若干文件
3. 分离编译(Separate compilation)
4. 分离编译步骤
5. 分离你的C程序
2. 依赖关系(Dependencies)
1. 依赖关系图表
2. 依赖关系是如何工作的
3. Make是如何做的?
3. 文件
1. 解析依赖关系图表
2. 列出依赖关系
3. 使用make解析makefile
4. Make的“快捷方式”
1. Make中的宏
2. 特殊的宏
3. 预定义规则
4. 各种各样的快捷方式
5.
1. 特殊依赖关系
2. 用户化后缀与规则
命令
Make命令允许你管理大型程序或一组程序(groups of programs)。当你开始编写大型程序时,你会发现重编译(re-compiling)大型程序比小型程序花费更多的时间。另外也会注意到,你经常仅仅在一小部分程序上工作(例如你正在调试的一个函数),剩余的大部分程序保持不变。
make程序帮助你开发大型程序。它跟踪自上次编译之后,整个程序中发生变化的部分,并仅仅编译这些程序。
一个简单的编译
编译一个小的C程序至少需要一个.c文件,以及一些合适的.h文件。尽管执行这个任务的命令很简单:cc file.c,但实际上,获得最终的可执行程序包含3个步骤,如下所示:
1. 编译阶段(Compiler stage):所有.c文件中的C语言代码将被转化为一种称作汇编语言(Assembly language)的语言(Lower-level language),生成.s文件。
2. 汇编阶段(Assembler stage): 前一阶段生成的汇编语言代码然后被转化为目标代码(object code),一种计算机可以直接理解的分段代码(fragments of code)。目标代码文件名以.o结尾
3. 链接阶段(Linker stage):编译程序的最后阶段主要将目标代码同包含像printf这样的、特定的“内建”函数(“built-in” functions)的代码库链接起来。这个阶段生成一个默认名为a.out的可执行程序。
编译若干文件
当你的程序变得很大时,一个明智的做法是将源代码划分为若干容易管理的单独的.c文件。上面的图解演示了一个包含两个.c文件和一个common.h文件的程序的编译过程。命令如下:
cc green.c blue.c
上面的命令中,两个.c文件均作为编译器的输入。注意,编译多个文件的前两个阶段与之前编译单个.c文件的处理过程完全相同,但最后一个步骤有一个有趣的连接(twist):在链接阶段,两个.o文件被链接在一起,生成了一个可执行程序a.out。
分离编译
生成可执行程序的步骤可以分为两个用红色标注的编译/汇编阶段,以及一个用黄色标注的最终的连接阶段。可以分别地创建两个.o文件,但在创建可执行程序的最后步骤中,二者均被需要。
你可以使用cc的-c选项来创建.c文件相应的目标文件(.o)。例如,输入命令:cc –c green.c将不会产生a.out,但编译器会在汇编阶段之后停止下来,并生成green.o文件。
分离编译步骤
用于生成可执行程序的这三个不同的任务列举如下:
§ 编译green.o:cc –c green.c
§ 编译 blue.o: cc -c blue.c
§ 将两部分链接在一起: cc green.o blue.o
例如,必须注意到:为了创建文件green.o,需要green.c文件以及头文件common.h。相似的,为了生成可执行程序a.out,需要目标文件green.o以及blue.o。
分离你的C程序
当你将C程序分离为许多文件时,记住以下几点:
§ 确保两个文件中没有相同函数名的函数。否则编译器将会被弄混(get
confused)。
§ 相似的,如果你在程序中使用全局变量(global variables),确保给全局变量不会被两个文件同时定义。
§ 如果使用全局变量,确保在一个文件中定义它们,并将之在.h文件中声明:extern int global var;
§ 为了使用在另一个文件中定义的函数,创建一个.h文件,并在其中加上该函数的原型,然后使用#include将这些.h文件包含在.c文件中。
§ 至少一个文件中必须包含main()函数。
注意:当你定义一个变量时,它看起来像这样:int globalvar;当你声明一个变量时,它看起来像这样:extern int globalvar;主要的区别在于定义变量(a variable definition)创建了该变量,而变量声明则表明该变量已经在其它地方定义过。定义暗含着声明(A definition implies a declaration)。
依赖关系
编译器进行操作的原则将在最后的部分进行说明。而生成可执行程序则是根据文件的依赖关系。例如,我们现在知道,为了创建目标文件program.o,我们至少需要文件program.c(这里还有可能有其它依赖关系,例如.h文件)。
这个部分包含画出 “依赖关系图”(“dependency graphs”),该图与之前部分中的图表非常相似。当使用make非常娴熟的时候,你可能不再需要画出此类图表,但它对于理解将要执行的动作非常重要。
依赖关系图
上述图解(figure)中展示的图表(graph),是一个由5个源文件组成的程序:data.c data.h io.c io.h 以及main.c。最上面部分是最后的结果—--名为project1的程序。那些从一个文件向下延伸到其它文件的线,指明了该文件依赖的其它文件。例如,为了生成main.o,需要以下三个文件:data.h io.h以及main.c。
依赖关系是如何工作的
假设已经完成了编译程序的过程,当测试该程序时,你发现io.c中的一个函数有缺陷。于是,你通过编辑io.c来修复该缺陷。
上述的图解用红色显示io.c。通过向上跟踪这个图标,你会发现io.o需要更新,因为io.c已经发生变化。相似的,因为io.o已经变化了,project1也需要进行更新。
make是如何工作的?
Make程序从文本文件makefile 或者Makefile中获取相应的依赖关系图表(dependency graph),该文件通常同源文件存在于相同的目录下。Make检查文件的更改时间,当一个文件比依赖于该“文件的文件”更新(newer)时,就会相应地运行编译器。
例如,前面一页讲解io.c已经发生变化。如果你编辑io.c,那么它将比io.o更新(newer),这意味着make将必须运行cc –c io.c 来生成新的io.o,然后运行cc data.o main.o
io.o –o project1来重新生成project1。
文章参考:
itisthinktime2012-09-23 15:09:42
itisthinktime2012-09-23 14:31:53