【Linux】make指令的使用和makefile文件的编写

导语

当我们编译一两个源文件时,我们可以直接使用gcc进行预处理、编译、汇编、链接等等。
例如:

$ gcc file1.c file2.c -o target

在这种情形下,我们通常不会感到不便。

但是如果文件数量增加到37个呢?

$ gcc file1.c file2.c file3.c ... file36.c file37.c  -o target

这时候,假如源文件file37.c的内容进行了更新,执行这条指令意味着重新编译所有的源文件,这显然是效率极低的。

我们可以试着改进:

$ gcc file1.o file2.o file3.o ... file36.o file37.c  -o target

将file37.c源文件和其他源文件生成的中间文件共同进行编译,效率稍有提升。

但是如果我们要做的是一个项目呢?
我们不妨思考以下几种情形:

  1. 不同源文件之间可能会互相调用,也会调用库文件,具有复杂的依赖关系,需要在输入指令时指定。但是每次编译时都要厘清如此繁复的依赖关系,是否具有可行性呢?
  2. 项目内包含很多源文件,这些文件都需要进行编译,假如有37个源文件,我们是不是需要至少输入37遍指令,去分别指定它们的目标文件名和依赖关系呢?
  3. 当某个源文件进行了更新,我们难道要手动更新依赖该文件的其他源文件吗?还是重新开始全部的编译步骤呢?
  4. 如果是与项目无关的人需要使用,采取这样的编译方式是否具有可操作性呢?难道必须所有使用者都知道其中的编译细节?

很显然,要记住库文件、源文件及其生成的中间文件之间所有的依赖关系、更新情况、编译顺序等等,是不现实的。这时候,引入工程管理器make是很必要的。

make指令的使用

什么是make指令

它是一条计算机指令,用来构建和管理自己的工程。GNU的make能使整个工程的编译、链接只需要一个命令就可以完成。
该指令是通过读入一个名为makefile文件(描述了整个工程的编译、链接等规则),然后执行这个文件中指定的指令来实现整个工程的“傻瓜式”编译的。

make指令有哪些功能

  1. Make使最终用户可以在不知道构建细节的情况下构建和安装你的软件,因为这些细节都记录在了你提供的Makefile中。
  2. 基于源文件的改动,Make可以自动知道那些文件需要更新;它也会自动决定文件更新的适当顺序,以避免要更新的文件依赖于另一个同样需要更新的文件。因此,在你修改了程序的源代码并且执行Make后,你不必重新完全编译你的所有文件,只需要重新编译那些直接或间接受到影响的文件就好了。
  3. Make不限于任何一种特定的语言。对于程序中任何一种非源文件,makefile文件中可以指定shell指令去生成它。shell指令可以执行编译器生成目标文件,执行链接器生成可执行文件,执行ar更 新库文件,执行IeX(一个文本排版软件)或Makeinfo去格式化文档。
  4. Make不限于用来生成软件包。你可以用make来控制安装和卸载软件包,或者用来生成标签表,以及其他的任何你想要做的,当然前提是你想好怎么做。

makefile文件的编写

读到这里,我们显然已经明白,makefile文件的内容包含一个工程进行编译、链接等规则。其中包括:工程中的哪些源文件需要编译以及如何编译、需要创建哪些库文件以及如何创建这些库文件、如何最后产生我们想要的可执行文件

尽管编写Makefile文件看起来可能是很复杂的事情,但是为工程编写Makefile的好处是能够使用一行命令来完成“自动化编译”,一旦提供一个(通常对于一个工程来说会是多个)正确的Makefile,编译整个工程你所要做的唯一的一件事就是在shell提示符下输入make命令。整个工程完全自动编译,极大提高了效率。

要想编写makefile文件,我们先来了解一下makefile的语法规则:
在这里插入图片描述
其实语法规则非常简单,最抽象来说,只有两行:

//target是要生成的目标文件,既可以是Object File,也可以是执行文件。
//prerequisites是要生成target所需要的文件或者目标。
1 target ... : prerequisites ...
//command也就是满足更新条件(prerequisites中有任一文件的最新修改日期要比target新)时,make所需要执行的命令(不局限于编译,可以是任意的shell命令)
//注意,command之前有一个Tab,而target之前没有空格。
2	command
	...
	...

这实际上是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。

示例代码

下面我们用一个例子来进行详细说明:
在这里插入图片描述

我们首先要声明要生成的最终目标文件edit,它的依赖文件有:main.o、kbd.o、command.o、display.o、insert.o、search.o、files.o、utils.o。
如果后面的prerequisites中有一个的最新修改日期要比target新,那么就执行后面的command(这里是重新生成target)。
如果目标文件target已经是最新的,就不执行后面的command。

就这样自顶向下地,找到需要执行的指令,根据规定好的依赖关系只更新 必要的中间文件。

也有一种比较特殊,比如示例中的clean指令。这条指令在直接执行命令$make时不会被执行,但是在执行命令$make clean时,会直接执行对应的该条指令。
在这里插入图片描述

使用变量

在示例代码中,我们留意到
在这里插入图片描述
这些依赖文件共同出现的次数很多,我们可以定义一个变量来代替这个组合:
在这里插入图片描述

自动推导

我们还可以进一步对代码进行优化。由于一个object file一定依赖于同名的.c文件,所以依赖文件中不用显式声明同名.c文件:
在这里插入图片描述

至此,我们了解了makefile最简单最基本的语法规则,根据这些规则,足够我们编写简单的符合生产需要的makefile文件。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值