Chinaunix首页 | 论坛 | 博客
  • 博客访问: 103639561
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类: C/C++

2008-04-17 17:15:03

1 Make 概述
Make 可自动决定一个大程序中哪些文件需要重新编译,并发布重新编译它们的命令。本版本GNU Make使用手册由Richard M. Stallman and Roland McGrath编著,是从Paul D. Smith撰写的V3.76版本发展过来的。

GNU Make符合IEEE Standard 1003.2-1992 (POSIX.2) 6.2章节的规定。

因为C语言程序更具有代表性,所以我们的例子基于C语言程序,但Make并不是仅仅能够处理C语言程序,它可以处理那些编译器能够在Shell命令下运行的的各种语言的程序。事实上,GNU Make不仅仅限于程序,它可以适用于任何如果一些文件变化导致另外一些文件必须更新的任务。

如果要使用Make,必须先写一个称为Makefile的文件,该文件描述程序中各个文件之间的相互关系,并且提供每一个文件的更新命令。在一个程序中,可执行程序文件的更新依靠OBJ文件,而OBJ文件是由源文件编译得来的。

一旦合适的Makefile文件存在,每次更改一些源文件,在shell命令下简单的键入:

make

就能执行所有的必要的重新编译任务。Make程序根据Makefile文件中的数据和每个文件更改的时间戳决定哪些文件需要更新。对于这些需要更新的文件,Make基于Makefile文件发布命令进行更新,进行更新的方式由提供的命令行参数控制。具体操作请看运行Make章节。

1.1怎样阅读本手册
如果您现在对Make一无所知或者您仅需要了解对make 的普通性介绍,请查阅前几章内容,略过后面的章节。前几章节是普通介绍性内容,后面的章节是具体的专业、技术内容。

如果您对其它Make程序十分熟悉,请参阅GNU Make的特点和不兼容性和失去的特点部分,GNU Make的特点这一章列出了GNU Make对make程序的扩展,不兼容和失去的特点一章解释了其它Make程序有的特征而GNU Make缺乏的原因。

对于快速浏览者,请参阅选项概要、快速参考和内建的特殊目标名部分。

1.2问题和BUG
如果您有关于GNU Make的问题或者您认为您发现了一个BUG,请向开发者报告;我们不能许诺我们能干什么,但我们会尽力修正它。在报告BUG之前,请确定您是否真正发现了BUG,仔细研究文档后确认它是否真的按您的指令运行。如果文档不能清楚的告诉您怎么做,也要报告它,这是文档的一个BUG。

在您报告或者自己亲自修正BUG之前,请把它分离出来,即在使问题暴露的前提下尽可能的缩小Makefile文件。然后把这个Makefile文件和Make给出的精确结果发给我们。同时请说明您希望得到什么,这可以帮助我们确定问题是否出在文档上。

一旦您找到一个精确的问题,请给我们发E-mail,我们的E-mail地址是:

bug-make@gnu.org

在邮件中请包含您使用的GNU Make的版本号。您可以利用命令‘make--version’得到版本号。同时希望您提供您的机器型号和操作系统类型,如有可能的话,希望同时提供config.h文件(该文件有配置过程产生)。

2 Makefile文件介绍
Make程序需要一个所谓的Makefile文件来告诉它干什么。在大多数情况下,Makefile文件告诉Make怎样编译和连接成一个程序。

本章我们将讨论一个简单的Makefile文件,该文件描述怎样将8个C源程序文件和3个头文件编译和连接成为一个文本编辑器。Makefile文件可以同时告诉Make怎样运行所需要的杂乱无章的命令(例如,清除操作时删除特定的文件)。如果要看更详细、复杂的Makefile文件例子,请参阅复杂的Makefile文件例子一章。

当Make重新编译这个编辑器时,所有改动的C语言源文件必须重新编译。如果一个头文件改变,每一个包含该头文件的C语言源文件必须重新编译,这样才能保证生成的编辑器是所有源文件更新后的编辑器。每一个C语言源文件编译后产生一个对应的OBJ文件,如果一个源文件重新编译,所有的OBJ文件无论是刚刚编译得到的或原来编译得到的必须从新连接,形成一个新的可执行文件。

2.1 规则的格式
一个简单的Makefile文件包含一系列的“规则”,其样式如下:

目标(target)…: 依赖(prerequiries)…

命令(command)

        …

        …

目标(target)通常是要产生的文件的名称,目标的例子是可执行文件或OBJ文件。目标也可是一个执行的动作名称,诸如‘clean’(详细内容请参阅假想目标一节)。

依赖是用来输入从而产生目标的文件,一个目标经常有几个依赖。

命令是Make执行的动作,一个规则可以含有几个命令,每个命令占一行。注意:每个命令行前面必须是一个Tab字符,即命令行第一个字符是Tab。这是不小心容易出错的地方。

通常,如果一个依赖发生变化,则需要规则调用命令对相应依赖和服务进行处理从而更新或创建目标。但是,指定命令更新目标的规则并不都需要依赖,例如,包含和目标‘clern’相联系的删除命令的规则就没有依赖。

规则一般是用于解释怎样和何时重建特定文件的,这些特定文件是这个详尽规则的目标。Make需首先调用命令对依赖进行处理,进而才能创建或更新目标。当然,一个规则也可以是用于解释怎样和何时执行一个动作,详见编写规则一章。

一个Makefile文件可以包含规则以外的其它文本,但一个简单的Makefile文件仅仅需要包含规则。虽然真正的规则比这里展示的例子复杂,但格式却是完全一样。

2.2一个简单的Makefile文件
一个简单的Makefile文件,该文件描述了一个称为文本编辑器(edit)的可执行文件生成方法,该文件依靠8个OBJ文件(.o文件),它们又依靠8个C源程序文件和3个头文件。

在这个例子中,所有的C语言源文件都包含‘defs.h’ 头文件,但仅仅定义编辑命令的源文件包含‘command.h’头文件,仅仅改变编辑器缓冲区的低层文件包含‘buffer.h’头文件。

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文件创建可执行的称为‘edit’的文件,键入:make

使用Makefile文件从目录中删除可执行文件和目标,键入:make clean

在这个Makefile文件例子中,目标包括可执行文件‘edit’和OBJ文件‘main.o’及‘kdb.o’。依赖是C语言源文件和C语言头文件如‘main.c’和‘def.h’等。事实上,每一个OBJ文件即是目标也是依赖。所以命令行包括‘cc -c main.c’和‘cc -c kbd.c’。

当目标是一个文件时,如果它的任一个依赖发生变化,目标必须重新编译和连接。任何命令行的第一个字符必须是‘Tab’字符,这样可以把Makefile文件中的命令行与其它行分别开来。(一定要牢记:Make并不知道命令是如何工作的,它仅仅能向您提供保证目标的合适更新的命令。Make的全部工作是当目标需要更新时,按照您制定的具体规则执行命令。)

目标‘clean’不是一个文件,仅仅是一个动作的名称。正常情况下,在规则中‘clean’这个动作并不执行,目标‘clean’也不需要任何依赖。一般情况下,除非特意告诉make执行‘clean’命令,否则‘clean’命令永远不会执行。注意这样的规则不需要任何依赖,它们存在的目的仅仅是执行一些特殊的命令。象这些不需要依赖仅仅表达动作的目标称为假想目标。详细内容参见假想目标;参阅命令错误可以了解rm或其它命令是怎样导致make忽略错误的。

2.3 make处理makefile文件的过程
缺省情况下,make开始于第一个目标(假想目标的名称前带‘.’)。这个目标称为缺省最终目标(即make最终更新的目标,具体内容请看指定最终目标的参数一节)。

在上节的简单例子中,缺省最终目标是更新可执行文件‘edit’,所以我们将该规则设为第一规则。这样,一旦您给出命令:

make

make就会读当前目录下的makefile文件,并开始处理第一条规则。在本例中,第一条规则是连接生成‘edit’,但在make全部完成本规则工作之前,必须先处理‘edit’所依靠的OBJ文件。这些OBJ文件按照各自的规则被处理更新,每个OBJ文件的更新规则是编译其源文件。重新编译根据其依靠的源文件或头文件是否比现存的OBJ文件更‘新’,或者OBJ文件是否存在来判断。

    其它规则的处理根据它们的目标是否和缺省最终目标的依赖相关联来判断。如果一些规则和缺省最终目标无任何关联则这些规则不会被执行,除非告诉Make强制执行(如输入执行make clean命令)。

    在OBJ文件重新编译之前,Make首先检查它的依赖C语言源文件和C语言头文件是否需要更新。如果这些C语言源文件和C语言头文件不是任何规则的目标,make将不会对它们做任何事情。Make也可以自动产生C语言源程序,这需要特定的规则,如可以根据Bison或Yacc产生C语言源程序。

    在OBJ文件重新编译(如果需要的话)之后,make决定是否重新连接生成edit可执行文件。如果edit可执行文件不存在或任何一个OBJ文件比存在的edit可执行文件‘新’,则make重新连接生成edit可执行文件。

    这样,如果我们修改了‘insert.c’文件,然后运行make,make将会编译‘insert.c’文件更新‘insert.o’文件,然后重新连接生成edit可执行文件。如果我们修改了‘command.h’文件,然后运行make,make将会重新编译‘kbd.o’和‘command.o’文件,然后重新连接生成edit可执行文件。

2.4使用变量简化makefile文件
在我们的例子中,我们在‘edit’的生成规则中把所有的OBJ文件列举了两次,这里再重复一遍:

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
这样的两次列举有出错的可能,例如在系统中加入一个新的OBJ文件,我们很有可能在一个需要列举的地方加入了,而在另外一个地方却忘记了。我们使用变量可以简化makefile文件并且排除这种出错的可能。变量是定义一个字符串一次,而能在多处替代该字符串使用(具体内容请阅读使用变量一节)。

在makefile文件中使用名为objects, OBJECTS, objs, OBJS, obj, 或 OBJ的变量代表所有OBJ文件已是约定成俗。在这个makefile文件我们定义了名为objects的变量,其定义格式如下:

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o
然后,在每一个需要列举OBJ文件的地方,我们使用写为`$(objects)'形式的变量代替(具体内容请阅读使用变量一节)。下面是使用变量后的完整的makefile文件:

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o
 
edit : $(objects)
        cc -o edit $(objects)
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 $(objects)

2.5 让make推断命令
编译单独的C语言源程序并不需要写出命令,因为make可以把它推断出来:make有一个使用‘CC –c’命令的把C语言源程序编译更新为相同文件名的OBJ文件的隐含规则。例如make可以自动使用‘cc -c main.c -o main.o’命令把‘main.c’编译 ‘main.o’。因此,我们可以省略OBJ文件的更新规则。详细内容请看使用隐含规则一节。

如果C语言源程序能够这样自动编译,则它同样能够自动加入到依赖中。所以我们可在依赖中省略C语言源程序,进而可以省略命令。下面是使用隐含规则和变量objects的完整makefile文件的例子:

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o
 
edit : $(objects)
        cc -o edit $(objects)
 
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
 
.PHONY : clean
clean :
        -rm edit $(objects)
这是我们实际编写makefile文件的例子。(和目标‘clean’联系的复杂情况在别处阐述。具体参见假想目标及命令错误两节内容。)因为隐含规则十分方便,所以它们非常重要,在makefile文件中经常使用它们。

2.6 另一种风格的makefile文件
当时在makefile文件中使用隐含规则创建OBJ文件时,采用另一种风格的makefile文件也是可行的。在这种风格的makefile文件中,可以依据依赖分组代替依据目标分组。下面是采用这种风格的makefile文件:

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o
 
edit : $(objects)
        cc -o edit $(objects)
 
$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h
这里的defs.h是所有OBJ文件的共同的一个依赖;command.h和bufffer.h是具体列出的OBJ文件的共同依赖。

虽然采用这种风格编写makefile文件更具风味:makefile文件更加短小,但一部分人以为把每一个目标的信息放到一起更清晰易懂而不喜欢这种风格。

2.7 在目录中删除文件的规则
编译程序并不是编写make规则的唯一事情。Makefile文件可以告诉make去完成编译程序以外的其它任务,例如,怎样删除OBJ文件和可执行文件以保持目录的‘干净’等。下面是删除利用make规则编辑器的例子:

clean:
        rm edit $(objects)

在实际应用中,应该编写较为复杂的规则以防不能预料的情况发生。更接近实用的规则样式如下:

.PHONY : clean
clean :
        -rm edit $(objects)

这样可以防止make因为存在名为’clean’的文件而发生混乱,并且导致它在执行rm命令时发生错误(具体参见假想目标及命令错误两节内容)。

诸如这样的规则不能放在makefile文件的开始,因为我们不希望它变为缺省最终目标。应该象我们的makefile文件例子一样,把关于edit的规则放在前面,从而把编译更新edit可执行程序定为缺省最终目标。
阅读(205) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~