make命令和makefile文件(二)

make命令和makefile文件

1. makefile的语法
2. make命令的选项和参数
3. makefile文件中的注释
4. makefile文件中的宏
5. 多个目标
6. 内置规则
7. 后缀和模式规则
8. 用make管理库函数
9. 高级主题:makefile文件和子目录
10.GUN make和gcc

2.make命令的选项和参数
make命令本身有许多选项,其中最常用的选项如下所示。

  • -k:它的作用是让make命令在发现错误时仍然继续执行,而不是在检测到第一个错误时就停下来。你可以利用这个选项在一次操作中发现所有未编译成功的源文件。
  • -n:它的作用是让make命令输出将要执行的操作步骤,而不真正执行这些操作。
  • -f :它的作用是告诉make命令将哪个文件作为makefile文件。如果未使用这个选项,标准版本的make命令将首先在当前目录下查找名为makefile的文件,如果该文件不存在,它就会查找名为Makefile的文件。但如果你是在Linux系统中,你使用的可能是GNU Make,这个版本的make命令将在搜索makefile文件和Makefile文件之前,首先查找名为GNUmakefile的文件。按惯例,许多Linux程序员使用文件名Makefile,因为如果一个目录下都是以小写字母为名称的文件,则Makefile文件将在目录的文件列表中第一个出现。我们建议不要使用文件名GNUmakefile,因为它是特定于make命令的GNU实现的。
    为了知识make命令创建一个特定的目标(通常是一个可执行文件),你可以把该目标的名字作为make命令的一个参数。如果不这么做,make命令将试图创建列在makefile文件中的第一个目标。许多程序员都会在自己的makefile文件中将第一个目标定义为all,然后再列出其他从属目标。这个约定可以明确地告诉make命令,在未指定特定目标时,默认情况下应该创建哪个目标。我们建议读者都坚持使用这一约定。
    1.依赖关系
    依赖关系定义了最终应用程序里的每个文件与源文件之间的关系。
/*main.c*/
#include<a.h>
...
/*2.c*/
#include<a.h>
#include<b.h>
...
/*3.c*/
#include<b.h>
#include<c.h>
...

在上述例程中,你可以把依赖关系定义为最终应用程序依赖于文件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.1组成。你可以用下面这行语句进行定义:

all: myapp myapp.1

这里再次强调,如果未指定一个all目标,则make命令将只创建它在文件makefile中找到的第一个目标。
2.规则

makefile文件的第二部分内容是规则,它们定义了目标的创建方式。在上节的例子中,当make命令确定需要重建2.o时,它具体应该使用哪条命令呢?看上去只需使用命令gcc -c 2.c就够了(在后面你将看到,make命令内置了很多默认规则),但如果需要指定头文件目录,或者为了今后的调试需要设置符号信息选项又该怎么做呢?这就需要在makefile文件中明确定义一些规则。

此时,我们必须提及makefile文件中一个非常奇怪而又令人遗憾的语法现象:空格和制表符tab是有区别的。规则所在的行必须以制表符tab开头,用空格是不行的。由于连续几个空格和一个制表符tab看上去很相似,而且几乎在Linux编程的所有领域中,空格和制表符tab之间几乎没有差别,所以这样的语法规定会带来问题。此外,如果makefile文件中的某行以空格结尾,它也可能会导致make命令执行失败。但这些都是历史遗留问题,而且因为已有太多的makefile文件存在,企图将其全部改正是不现实的,所以请小心编写makefile文件。幸运的是,如果缺少了制表符tab, make命令就不会正常工作,所以发现这个错误很容易。
实验:一个简单的makefile文件
大多数规则都包含一个简单的命令,给命令也可以在命令行上执行。就前面的例子来说,你把创建的第一个makefile文件命名为Makefile1:

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

你在调用make命令时加上-f选项,这是因为makefile文件并未使用常见的默认文件名makefile或Makefile。如果在一个没有任何源文件的目录下执行这个命令,你就会得到如下的输出结果:
没有源代码执行的结果
make命令假设在makefile文件中的第一个目标myapp是想创建的目标文件。然后它会检查其他的依赖关系,并确定需要有一个名为main.c的文件。由于并未创建该文件,makefile文件里也未说明如何创建该文件,所以make命令报告一个错误。下面就来创建这些源文件并重新进行尝试。由于对程序执行的结果没有兴趣,所以这些文件的内容都非常简单。头文件实际上都是空文件,你可以用touch命令来创建它们:

touch a.h
touch b.h
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"a.h"
#include"b.h"

void function_two()
{

}

/*3.c*/

#include"b.h"
#include"c.h"

void function_three()
{

}

再次执行make命令:
再次执行make命令的结果
这次成功执行了make命令。
实验解析
make命令处理makefile文件中定义的依赖关系,确定需要创建的文件以及创建顺序。虽然把如何创建目标myapp列在最前面,但make命令能够自行判断出创建文件的正确顺序。它调用你在规则部分给出的命令来创建相应的文件,同时会在执行时在屏幕上将命令显示出来。现在,你可以测试在文件b.h改变时,makefile文件能否正确处理这一情况:
在这里插入图片描述
make命令读取makefile文件,确定重建myapp所需的最少命令,并以正确的顺序执行它们。下面我们来看,如果删除一个目标文件会发生什么情况:
在这里插入图片描述
make命令再次正确的确定出需要采取的动作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

helmer_hanssen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值