工程管理

在实际的开发过程中,仅仅通过使用gcc命令对程序进行编译是非常低效的。原因主要有以下两点。

1)程序往往是由多个源文件组成的,源文件的个数越多,那么gcc的命令行就会越长。此外,各种编译规则也会加大gcc命令行的复杂度。所以在开发调试程序的过程中,通过输入gcc命令行来编译程序是很麻烦的。

2)在程序的整个开发过程中,调试的工作量占到了整体工作量的70%以上。在调试程序的过程中,每次调试一般只会修改部分源文件。而在使用gcc命令行编译程序时,gcc会把那些没有被修改的源文件一起编译,这样就会影响编译的总体效率。

为了提高编译程序的效率,很多基于Windows平台上的开发工具都提供了工程管理器。用户只需要点击一个“make”按钮就可以启动工程管理器对整个程序进行自动编译。在整个编译的过程中是不需要人工干预的。这种工程管理器形象的称为全自动工程管理器。

GCC提供了半自动化的工程管理器Make。所谓半自动化是指在使用工程管理器前需要人工编写程序的编译规则。所有的编译规则都保存在Makefile文件中。全自动化的工程管理器在编译程序前会自动生成Makefile文件。

Make工程管理器的优越性具体体现在以下两个方面。

1)使用方便

通过命令“make”就可以启动Make工程管理器对程序进行编译,所以不再需要每次都输入gcc命令行。Make启动后会根据Makefile文件中的编译规则命令自动对源文件进行编译和链接,最终生成可执行文件。

2)调试效率高

为了提高编译程序的效率,Make会检查每个源文件的修改时间(时间戳)。只有在上次编译之后被修改的源文件才会在接下来的编译过程中被编译和链接,这样就能避免多余的编译工作量。为了保证源文件具有正确的时间戳,必须保证操作系统时间的正确性(注意VMWare虚拟机的CMOS时间是否正确)。

4.2.1 Makefile

Make工程管理器是完全根据Makefile文件中的编译规则命令进行工作的。Makefile 文件由以下三项基本内容组成。

1)需要生成的目标文件(target file)。

2)生成目标文件所需要的依赖文件(dependency file)。

3)生成目标文件的编译规则命令行(command)。

这三项内容按照如下格式进行组织:

target file :dependency file

command

其中,Makefile规定在书写command命令行前必须加一个键。

Make工程管理器在编译程序时会检查每个依赖文件的时间戳,一旦发现某个依赖文件的时间戳比目标文件要新,就会执行目标文件的规则命令来重新生成目标文件。这个过程称为目标文件的依赖规则检查。依赖规则检查是Make工程管理器的最核心的工作任务之一。下面以编译程序test(由a.c、b.c和b.h组成)为例来描述Make的工作过程。例如:

l a.c:

#include “b.h”

int main()

{

  hello();

  return 0;

}

l b.h:

void hello();

l b.c:

#include “stdio.h”

void hello()

{

  printf(“hello”);

}

l Makefile:

test : a.o b.o

cc -o test a.o b.o

a.o : a.c b.h

cc -c a.c

b.o : b.c

cc -c b.c

Make工程管理器编译test程序的过程如下:

1)Make工程管理器首先会在当前目录下读取Makefile文件。

2)查找Makefile文件中的第一个目标文件(在本例中为test),该文件也是Make工程管理器本次编译任务的最终目标。

3)把目标文件test的依赖文件当作目标文件进行依赖规则检查。这是一个递归的检查过程。在本例中就是依次把a.o和b.o作为目标文件来检查各自的依赖规则。Make会根据以下三种情况进行处理:

1) 如果当前目录下没有或缺少依赖文件,则执行其规则命令生成依赖文件(比如缺少a.

文件,则执行命令“cc -c a.c”生成a.o)。

2) 如果存在依赖文件,则把其作为目标文件来检查依赖规则(假设a.c比a.o新,则执

行命令“cc -c a.c”更新a.o)。

3) 如果目标文件比所有依赖文件新,则不做处理。

4)递归执行第三步后,就会得到目标文件test所有最新的依赖文件了。接着Make会根据以下三种情况进行处理:

1) 如果目标文件test不存在(比如第一次编译),则执行规则命令生成test。

2) 如果目标文件test存在,但存在比test要新的依赖文件,则执行规则命令更新test。

3) 目标文件test存在,且比所有依赖文件新,则不做处理。

下面通过make的运行结果来印证上述流程。

1)第一次编译时,由于没有test、a.o和b.o,Make会先执行命令“cc -c a.c”生成a.o;然后接着执行命令“cc -c b.c”生成b.o;最后执行命令“cc -o test a.o b.o”生成test文件。如下所示:

[root@localhost home]#make

cc –c a.c

cc –c b.c

cc –o test a.o b.o

2)如果修改了a.c文件,Make会先执行命令“cc -c a.c”生成a.o;由于b.o没有修改,所以Make就接着执行命令“cc -o test a.o b.o”生成test文件。如下所示:

[root@localhost home]#make

cc –c a.c

cc –o test a.o b.o

3)如果删除了b.o文件,由于a.o没有修改,所以Make就先执行命令“cc -c b.c”生成b.o;然后接着执行命令“cc -o test a.o b.o”生成test文件。如下所示:

[root@localhost home]#make

cc –c b.c

cc –o test a.o b.o

4)如果再运行一次make时,因为所有的源文件都没有改动,所以Make不会有任何动作。如下所示:

[root@localhost home]#make

make: “test”是最新的。

你可能感兴趣的:(linux)