当前位置: > Linux命令 >

make命令和makefile文件

时间:2016-03-11 17:24来源:linux.it.net.cn 作者:IT

一、多个源文件带来的问题

       在编写小程序时,许多人都会在编辑完源文件后简单地重新编译所有文件以重建应用程序。但对大型程序来说,

使用这种简单到处理方式会带来很明显的问题。编辑-编译-测试这一循环的周期将变长。如果仅改动了一个源文件,

即使是最有耐心到程序员也不想重新编译所有到源文件。

       如果在程序中创建了多个头文件,并在不同到源文件中包含它们,就会带来潜在的、更严重的问题。比如说,

我们有三个头文件:a.h、b.h和c.h,三个C语言源文件main.c、2.c和3.c,具体情况如下所示:

 

 
/* main.c */  
  1. #include "a.h"  
  2. ...  
  3.   
  4. /* 2.c */  
  5. #include "a.h"  
  6. #include "b.h"  
  7. ...  
  8.   
  9. /* 3.c */  
  10. #include "b.h"  
  11. #include "c.h"  
  12. ...  

       如果程序员只修改了文件c.h,则源文件main.c和2.c无需重新编译,因为它们并不依赖于这个头文件,而对源文件3.c来说,

因为它包含了头文件c.h,所以在文件c.h改动后,就必须重新编译它。但如果修改的是文件b.h,而程序员又忘记重新编译源文件2.c,

则最终的程序就可能无法正常工作了。make工具可以解决上述这些问题,它会在必要时重新编译所有受改动影响的源文件。

 

       虽然make命令内置了许多智能机制,但光凭其自身是无法了解应该如何建立应用程序的。用户必须为其提供一个文件,

告诉它应用程序应该如何构造,这个文件成为makefile。

       makefile文件一般都会和项目到其它源文件放在同一个目录下。你的机器上可以同时存在许多不同到makefile文件。事实上,

如果管理的是一个大项目,你可以用多个不同的makefile文件来分别管理项目的不同部分。

       make命令和makefile文件的结合提供了一个在项目管理领域的十分强大的工具。它不仅常被用于控制源代码的编译,还用于使用手册的编译及将应用程序安装到目标目录。

二、makefile文件的语法

       makefile文件由一组依赖关系和规则构成。每个依赖关系由一个目标(即将要创建的文件)和一组该目标所依赖的源文件组成。

而规则描述了如何通过这些依赖文件创建目标。一般来说,目标是一个单独的可执行文件。

       make命令会读取makefile文件的内容,它先确定要创建的目标文件,然后比较该目标所依赖的源文件到日期和时间,

以决定该采用哪条规则来构造目标。通常在创建最终的目标文件之前,需要先创建一些中间目标。make命令会根据makefile

文件来确定目标文件的创建顺序以及正确的规则调用顺序。

1)make命令的选项和参数

       make命令本身有许多选项,其中最常用的三个选项是:

 

  • -k:它的作用是让make命令在发现错误时仍然继续执行,而不是在检测到第一个错误时就停下来。我们利用这个选项在一次操作中发现为编译成功的源文件。
  • -n:它的作用是让make命令输出将要执行的操作步骤,而不真正执行这些操作。
  • -f filename:它的作用是告诉make命令将哪个文件作为makefile文件。如果未使用这个选项,make命令将首先查找当前目录下名为makefile的文件,如果该文件不存在,就会查找名为Makefile的文件。
       为了指示make命令创建一个特定的目标(通常是一个可执行文件),我们可以把该目标的名字作为make命令的一个参数。
否则,make命令将试图创建列在makefile文件中的第一个目标。许多程序员都会在自己的makefile文件中将第一个i额目标定义为all,
然后再列出其他的从属目标。这样就可以明确地告诉make命令,再未指定特定目标时,默认情况下应该创建哪个目标。

1. 依赖关系

       依赖关系定义了最终应用程序里的每个文件与源文件之间的关系。在上例中,我们可以把依赖关系定义为最终应用程序依赖于文件main.o、2.o和3.o。
同样,main.o依赖于main.c和a.h,2.o依赖于2.c、a.h和b.h,3.o依赖于3.c、b.h和c.h。因此,main.o受文件main.c和a.h修改的影响,
如果这两文件之一有所改变,就需要重新编译main.c以重建main.o。
       在makefile文件中,这些规则的写法是:先写目标的名称,然后紧跟着一个冒号,接着是空格或制表符tab,最后是用空格货制表符tab隔开的文件列表
(这些文件用于创建目标文件)。与我们例子相对应的依赖关系如下所示:
 
  1. myapp: main.o 2.o 3.o  
  2. main.o: main.c a.h  
  3. 2.o: 2.c a.h b.h  
  4. 3.o: 3.c b.h c.h  
myapp: main.o 2.o 3.o
main.o: main.c a.h
2.o: 2.c a.h b.h
3.o: 3.c b.h c.h
       它表示目标myapp依赖于main.o、2.o和3.o,而main.o依赖于main.c和a.h,等等。
       这组依赖关系形成一个层次结构,显示了源文件之间的关系。容易看出,如果文件b.h发生改变,就需要重新编译2.o和3.o,而由于2.o和3.o发生了改变,
目标myapp也需要重新创建。
       如果我们想一次制作多个文件,就可以利用名义上的目标all。假设我们的应用程序由二进制文件myapp和使用手册myapp.l组成。我们可以用下面的语句进行定义:
 
  1. all:myapp myapp.l  
all:myapp myapp.l
       如果使用make命令时未指定目标all,则make命令将只创建它在makefile文件中找到的第一个目标。

2.  规则

       makefile文件的第二部分内容是规则,它们定义了目标的创建方式。例如,make命令确定需要重建2.o时,它具体应该使用哪条命令呢?这都是规则所定义的。
       实验:简单的makefile文件
       创建我们的第一个makefile文件命名为Makefile1:
[plain] view plain copy print?
  1. myapp: main.o 2.o 3.o  
  2.     gcc -o myapp main.o 2.o 3.o  
  3. main.o: main.c a.h  
  4.     gcc -c main.c  
  5. 2.o: 2.c a.h b.h  
  6.     gcc -c 2.c  
  7. 3.o: 3.c b.h c.h  
  8.     gcc -c 3.c  
myapp: main.o 2.o 3.o
	gcc -o myapp main.o 2.o 3.o
main.o: main.c a.h
	gcc -c main.c
2.o: 2.c a.h b.h
	gcc -c 2.c
3.o: 3.c b.h c.h
	gcc -c 3.c
       在命令行输入如下命令:
 
  1. [root@localhost ~]# make -f Makefile1  
  2. make: *** 没有规则可以创建“main.o”需要的目标“main.c”。 停止。  
[root@localhost ~]# make -f Makefile1
make: *** 没有规则可以创建“main.o”需要的目标“main.c”。 停止。
       由于我们没有在makefile文件中指定all,make命令假设在makefile文件中的第一个目标myapp就是我们想创建的目标文件。然后它会检查其他的依赖关系,
最终确定需要有一个文件main.c。由于我们并未创建该文件,makefile文件里也未说明如何创建该文件,所以make命令报告错误。下面就来创建这些源文件并重新尝试。
由于我们对程序执行的结果没有任何兴趣,所以这些文件的内容都非常简单。头文件实际上都是空文件,我们可以用touch命令来创建它们:
 
  1. [root@localhost ~]# touch a.h  
  2. [root@localhost ~]# touch b.h  
  3. [root@localhost ~]# touch c.h  
[root@localhost ~]# touch a.h
[root@localhost ~]# touch b.h
[root@localhost ~]# touch c.h

       文件main.c中包含main函数,该函数调用了function_two和function_three函数,而这两个函数分别在另外两个文件中定义。源文件通过#include语句包含合适的头文件,

使它们看上去依赖于这些头文件的内容。下面是其程序的清单:

 

  1. /* main.c */  
  2. #include <stdlib.h>  
  3. #include "a.h"  
  4. extern void function_two();  
  5. extern void function_three();  
  6. int main()  
  7. {  
  8.       function_two();  
  9.       function_three();  
  10.       exit(EXIT_SUCCESS);  
  11. }  
  12. /* 2.c */  
  13. #include <stdlib.h>  
  14. #include "a.h"  
  15. #include "b.h"  
  16. void function_two()  
  17. {  
  18.       printf("function_two\n");  
  19. }  
  20. /* 3.c */  
  21. #include <stdlib.h>  
  22. #include "b.h"  
  23. #include "c.h"  
  24. void function_three()  
  25. {  
  26.       printf("function_three\n");  
  27. }  
/* main.c */
#include <stdlib.h>
#include "a.h"
extern void function_two();
extern void function_three();
int main()
{
      function_two();
      function_three();
      exit(EXIT_SUCCESS);
}
/* 2.c */
#include <stdlib.h>
#include "a.h"
#include "b.h"
void function_two()
{
      printf("function_two\n");
}
/* 3.c */
#include <stdlib.h>
#include "b.h"
#include "c.h"
void function_three()
{
      printf("function_three\n");
}


 

       再次执行make命令:

 

  1. [root@localhost linux]# make -f Makefile1  
  2. gcc -c main.c  
  3. gcc -c 2.c  
  4. gcc -c 3.c  
  5. gcc -o myapp main.o 2.o 3.o  
[root@localhost linux]# make -f Makefile1
gcc -c main.c
gcc -c 2.c
gcc -c 3.c
gcc -o myapp main.o 2.o 3.o

 

       这次成功执行了make。

       实验解析

       make命令处理makefile文件中的依赖关系,确定需要创建的文件以及创建顺序。虽然我们把目标myapp列在最前面,

但make命令能够自行判断出创建文件的正确顺序。它调用在规则部分给出的命令以创建相应的文件,同时会在执行时在

屏幕上将命令显示出来。现在,我们测试在文件b.h改变时,makefile文件能否正确处理这一情况。

 

  1. [root@localhost linux]# touch a.h  
  2. [root@localhost linux]# make -f Makefile1  
  3. gcc -c main.c  
  4. gcc -c 2.c  
  5. gcc -o myapp main.o 2.o 3.o  
[root@localhost linux]# touch a.h
[root@localhost linux]# make -f Makefile1
gcc -c main.c
gcc -c 2.c
gcc -o myapp main.o 2.o 3.o

 

       make命令读取makefile文件,确定重建myapp所需的最少命令,并以正确的顺序执行它们。下面我们来看看删除一个目标文件后的情况:

 

  1. [root@localhost linux]# rm 2.o  
  2. rm:是否删除普通文件 "2.o"?y  
  3. [root@localhost linux]# make -f Makefile1  
  4. gcc -c 2.c  
  5. gcc -o myapp main.o 2.o 3.o  
[root@localhost linux]# rm 2.o
rm:是否删除普通文件 "2.o"?y
[root@localhost linux]# make -f Makefile1
gcc -c 2.c
gcc -o myapp main.o 2.o 3.o

 

 

       make命令再次正确地确定出需要采取的动作。

 

 

 

(责任编辑:IT)
------分隔线----------------------------
栏目列表
推荐内容