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

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

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

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

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

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

view plain
/* main.c */   

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

如果程序员只修改了文件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隔开的文件列表
(这些文件用于创建目标文件)。与我们例子相对应的依赖关系如下所示:
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组成。我们可以用下面的语句进行定义:
all:myapp myapp.l

       如果使用make命令时未指定目标all,则make命令将只创建它在makefile文件中找到的第一个目标。

2.  规则

       makefile文件的第二部分内容是规则,它们定义了目标的创建方式。例如,make命令确定需要重建2.o时,它具体应该使用哪条命令呢?这都是规则所定义的。
        实验:简单的makefile文件
       创建我们的第一个makefile文件命名为Makefile1:
myapp: main.o 2.o 3.ogcc -o myapp main.o 2.o 3.o
main.o: main.c a.hgcc -c main.c
2.o: 2.c a.h b.hgcc -c 2.c
3.o: 3.c b.h c.hgcc -c 3.c

       在命令行输入如下命令:
[root@localhost ~]# make -f Makefile1
make: *** 没有规则可以创建“main.o”需要的目标“main.c”。 停止。
       由于我们没有在makefile文件中指定all,make命令假设在makefile文件中的第一个目标myapp就是我们想创建的目标文件。然后它会检查其他的依赖关系,
最终确定需要有一个文件main.c。由于我们并未创建该文件,makefile文件里也未说明如何创建该文件,所以make命令报告错误。下面就来创建这些源文件并重新尝试。
由于我们对程序执行的结果没有任何兴趣,所以这些文件的内容都非常简单。头文件实际上都是空文件,我们可以用touch命令来创建它们:
[root@localhost ~]# touch a.h
[root@localhost ~]# touch b.h
[root@localhost ~]# touch c.h

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

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

/* 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命令:

[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文件能否正确处理这一情况。

[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所需的最少命令,并以正确的顺序执行它们。下面我们来看看删除一个目标文件后的情况:

[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命令再次正确地确定出需要采取的动作。

make命令和makefile文件相关推荐

  1. Make命令与Makefile文件

    Make命令 使用Make命令只会在必要时重新编译所有受改动影响的源文件.而不会因为只改动了一个文件而重新编译整个项目. Make命令不仅仅用于编译程序,无论何时,当需要通过多个输入文件来生成输出文件 ...

  2. makefile文件简要介绍

    到此为止,读者已经了解了如何在Linux下使用编辑器编写代码,如何使用Gcc把代码编译成可执行文件,还学习了如何使用Gdb来调试程序,那么,所有的工作看似已经完成了,为什么还需要Make这个工程管理器 ...

  3. Makefile 文件怎么写

    跟我一起写Makefile:MakeFile介绍 Makefile 使用总结 1. make 命令与 Makefile 文件 在 Linux 平台,执行 make 命令时,会在当前目录下寻找 Make ...

  4. Makefile文件(四)_书写命令

    变量说明: $@       --->      目标文件 $^       --->       所有的依赖文件 $<       --->       第一个依赖文件 一. ...

  5. linux中命令对c文件进行编译,Linux下C语言编译基础及makefile的编写

    这篇文章介绍在LINUX下进行C语言编程所需要的基础知识.在这篇文章当中,我们将会学到以下内容: 源程序编译 Makefile的编写 程序库的链接 程序的调试 头文件和系统求助 1.源程序的编译 在L ...

  6. Linux作业 使用make命令和分析makefile文件

    使用make命令和分析makefile文件   diction是一个经典的Unix小工具,用来检测使用不当的英文短语.   请前往 http://www.gnu.org/software/dictio ...

  7. make命令 makefile文件

    利用make工具,我们可以将大型的开发项目分解成为多个更易于管理的模块,对于一个包括几百个源文件的应用程序,使用make和 makefile工具就可以简洁明快地理顺各个源文件之间纷繁复杂的相互关系.而 ...

  8. Makefile文件详解一(gcc命令参数详解)

    gcc编译源文件共有4个过程,预处理.编译.汇编.链接. 预处理: 命令:gcc -E test.c -o test.i  (-o后面指定生成文件的命名) 过程:展开宏定义(#define),处理编译 ...

  9. 从简入难makefile文件编写,Linux C++编程,简单vi命令

     1.一个最基本的C++程序 2.第二个c++程序 3.第一个入门级别的简单的makefile 4.在makefile中定义变量. 5.编写makefile的依赖 如果start:标识后面的某个. ...

最新文章

  1. Kubernetes中部署Docker registry2.7.1并通过containerd实现拉取镜像到应用Pod的部署
  2. mac os 升级为Mountain Lion后,eclipse找不到JRE的问题
  3. mysql登录报错 ERROR 1045 (28000)
  4. 查询linux信号量命令,linux下的trap命令和SIGHUP信号量详解。
  5. Redis学习笔记~分布式的Pub/Sub模式
  6. Android之非root手机run-as命令获取debug版本apk里面的数据(shared_prefs文件,lib下面的so,数据库文件)
  7. 《TCP/IP Sockets 编程》笔记5
  8. QEBA:基于类边界查询访问的黑盒攻击
  9. Javascript的两种“单引号”
  10. linux学习笔记:我的第一个shell脚本
  11. 计算机管理软件禁止玩游戏,如何禁止电脑指定程序不能运行 怎么屏蔽QQ或游戏运行提高办公效率...
  12. 易游网络验证好不好?如何正确的使用易游验证
  13. 魔兽linux版本,linux下玩warcraft III(魔兽)
  14. 国家发明专利:基于改进型黏菌优化算法的业务资源分配方法
  15. 【实战+源码】RGB-D移动抓取服务机器人(四)——完结篇(ROS机器人、系统设计、运动规划、目标定位)
  16. spring+mybatis 一个事务中两次查询结果不一样的问题
  17. 东信EST-100系列身份证读卡器安卓开发包,Android Studio环境编译
  18. 旧机宝开发笔记之:RN开发编译器的选择
  19. oracle 空闲连接数_oracle数据库空闲连接
  20. 浅析民用建筑电气设计中智能消防应急照明系统的应用

热门文章

  1. 核电站计算机专业是干什么的,什么叫核电站?它是干什么用的呢?
  2. couldnotfindartifactcom.orcale:ojdbc6:pom:11.2.0.1.0innexus-aliyun
  3. Flutter 嵌套深、刷新乱?少年,你怕是连Flutter的门槛都没摸到!
  4. 如何批量输出条形码图片
  5. vue3 setup语法糖下父组件调用子组件的方法
  6. 计算机语言的学习方法
  7. IBM DB2 学习笔记:关系模型、基本概念、数据库实例基本操作、数据库对象
  8. IPv6, DAD 工作原理详解
  9. Javascript Promise用法详解
  10. C语言中 枚举变量与枚举值,枚举类型变量再赋值问题