一文看完Makefile

一文看完Makefile

1 Makefile 是什么?

回顾一下我们前面几个章节,我们使用到的示例代码只有一个 hello.c 文件,非常简单,所以直接执行下面的指令进行编译也非常方便。
编译一个文件
gcc hello.c -o hello
但是随着我们课程逐渐深入,以后在我们的项目工程中,势必会有越来越多的 C 文件和 H 头文件。当一个工程中有很多 C 源文件和 H 头文件时,再直接使用编译器指令就非常麻烦了,光输入文件名都会影响心情,比如下面的例子:
编译多个文件
gcc hello.c aaa.c bbb.c -o hello
而且哪怕你只是修改一个文件,也需要重新编译所有的文件,白白浪费了很多开发时间。要解决这个问题,最好的方式就是把工程的编译规则写下来,让编译器自动加载该规则进行编译。解决方法就是使用 make 和 Makefile,这两个工具是搭配使用的,下面给大家介绍一下:
• make 工具:它可以帮助我们找出项目里面修改变更过的文件,并根据依赖关系,找出受修改影响的其他相关文件,然后对这些文件按照规则进行单独的编译,这样一来,就能避免重新编译项目的所有的文件。
• Makefile 文件:上面提到的规则、依赖关系主要是定义在这个 Makefile 文件中的,我们在其中合理地定义好文件的依赖关系之后, make 工具就能精准地进行编译工作。
它们的关系如下图所示:
在这里插入图片描述

从我们上面的介绍,大家可以知道,我们管理一个项目工程,实质上就是管理项目文件间的依赖关系。所以我们在学习和使用 Makefile 的时候,一定要牢牢抓住它这种面向依赖的思想,心里一定要谨记, Makefile 中所有的复杂、晦涩的语法都是更好地为解决依赖问题而存在的。理解了它的本质目的之后,我们以后在学习它的过程中就不用死记硬背各种语法了, 只要想想这个本质目的,你会觉得一切都是那么地顺理成章。
是否真正驾驭 Makefile 的标志,就在于脑海中是否清晰地知道目标和依赖的关系。当你的大脑能够像 make 工具一样,准确无误地解释执行 Makefile 的时候,就是一个 Makefile 高手了。我们就是要奔着这个目标去的。
这里再多介绍一下,当工程复杂度再上一个台阶的时候,会觉得手写 Makefile 也很麻烦,那个时候可以用 CMake、 autotools 等工具来帮忙生成 Makefile。实际上 Windows 系统下很多 IDE 工具内部也是使用类似 Makefile 的方式组织工程文件的,只不过被封装成图形界面,对用户不可见而已。

2 Makefile 概览

Makefile 对于一个嵌入式 Linux 开发人员来说,怎么强调它的重要性都不为过。虽然它跟程序员的编程能力并不直接挂钩,但是它所体现出来的是一种工程能力, 这种工程能力恰恰就是专业程序员和业余程序员的一个分水岭。因为在 Linux 开发环境下,我们并没有太多的集成开发环境IDE,也就是说程序的所有控制权全部在于开发者,你势必要对底层软件的编译、链接、装载有着相当深入的了解,才能说真正地驾驭了你的程序。虽然前面的章节已经简单介绍了部分底层相关内容,但是还不够深入,我们以后的课程还要更加深入。
而且我们以后的课程也会陆续讲解 Uboot 移植开发、 Linux 内核移植开发和其他的一些开源项目,倘若我们 Makefile 基础不够扎实,那么很可能怎么着手分析项目都不知道。程序的架构组织都梳理不清楚,就更别谈修改程序功能了。如下图:
在这里插入图片描述

接下来我们先整体了解 Makefile 的相关语法,这里要提前跟大家说清楚一点, Makefile 经过多年发展,虽然功能非常强大,但是也留下了沉重的历史包袱,我们没有办法详细介绍 make 的每一个详细知识点 (也没有必要),借鉴于以往的开发经验,我们总结了 80% 常用的 Makefile 知识点,定位在为以后研究 Uboot、 Linux kernel 和其他开源项目打下结实的基础。虽然这个目标难度还是比较大的,要学习的知识点也不少,不过我们会通过一系列难度逐步迭代的小实验,来帮助大家无痛学会 Makefile。
我一直提倡:学习知识之前,要现在脑海中初步建立知识点的整体框架,以此来指导进一步的学习。
接下来我们先整体看一下要学习 Makefile 的知识点,如下图所示:
在这里插入图片描述

图里面的知识点不少,我们在这里先不深入学习具体语法,而是告诉大家每一个语法的出现是为了解决什么问题,我们从左上角开始讲起:
1、基础语法–描述目标和依赖的特定格式, Makefile 的核心。
2、变量–记录特定的信息,避免重复输入原始信息。尤其是手动输入原始信息很长时,特别好用。
3、分支判断–灵活控制多个不同的编译过程,方便兼容不同属性。
4、头文件依赖–监控头文件的变化,头文件也是程序的关键内容。
5、隐含规则–利用 Makefile 的一些默认规则,可以减少编写 Makefile 的工作量。
6、自动化变量–利用 Makefile 的默认的自动化变量,可以减少编写 Makefile 的工作量。
7、模式规则–灵活使用正则表达式,可以减少编写 Makefile 的工作量。
8、函数–使用 Makefile 的各种函数,可以更方便地实现 Makefile 的功能。
了解完 Makefile 的知识点,从上面的分析可以知道, Makefile 的核心在于基础语法,用来描述目标和依赖的关系。其他语法的目的,是为了减少我们编写 Makefile 工作量,让我们能够以更加优雅、更加简洁、更好维护的方式来实现 Makefile 的功能。这跟我们程序开发是很相似的,不止要实现功能,还要兼顾程序的可读性、拓展性、可维护性等等.

使用 Makefile 控制编译:
关于 Makefile 的详细使用可参考《跟我一起写 Makefile》一书或 GNU 官方的 make 说明文档:https://www.gnu.org/software/make/manual,本章仅以示例对 Makefile 的基础语法进行讲解.

3.Makefile 小实验:

第一个见面实验

为了直观地演示 Makefile 的作用,我们使用一个示例进行讲解,首先使用编辑器创建一个名为“Makefile”的文件,输入如下代码并保存,其中使用“#”开头的行是注释,自己做实验时可以不输入,另外要注意在“ls -lh”、” touch test.txt”等命令前要使用 Tab 键,不能使用空格代替。
#Makefile 格式
#目标: 依赖的文件或其它目标
#Tab 命令 1
#Tab 命令 2
#第一个目标,是最终目标及 make 的默认目标
#目标 a,依赖于目标 targetc 和 targetb
#目标要执行的 shell 命令 ls -lh,列出目录下的内容
targeta: targetc targetb
ls -lh
#目标 b,无依赖
#目标要执行的 shell 命令,使用 touch 创建 test.txt 文件
targetb:
touch test.txt
#目标 c,无依赖
#目标要执行的 shell 命令, pwd 显示当前路径
targetc:
pwd
#目标 d,无依赖
#由于 abc 目标都不依赖于目标 d,所以直接 make 时目标 d 不会被执行
#可以使用 make targetd 命令执行
targetd:
rm -f test.txt
在这里插入图片描述
这个 Makefile 文件主要是定义了四个目标操作,先大致了解它们的关系:
• targeta:这是 Makefile 中的第一个目标代号,在符号“:”后面的内容表示它依赖于 targetc 和targetb 目标,它自身的命令为“ls -lh”,列出当前目录下的内容。
• targetb:这个目标没有依赖其它内容,它要执行的命令为“touch test.txt”,即创建一个 test.txt文件。
• targetc:这个目标同样也没有依赖其它内容,它要执行的命令为“pwd”,就是简单地显示当前的路径。
• targetd:这个目标无依赖其它内容,它要执行的命令为“rm -f test.txt”,删除目录下的 test.txt文件。与 targetb、 c 不同的是,没有任何其它目标依赖于 targetd,而且它不是默认目标。
下面使用这个 Makefile 执行各种 make 命令,对比不同 make 命令的输出,可以清楚地了解 Makefile的机制。在主机 Makefile 所在的目录执行如下命令:
在主机上 Makefile 所在的目录执行如下命令
#查看当前目录的内容
ls
#执行 make 命令, make 会在当前目录下搜索“Makefile”或“makefile”,并执行
make
在这里插入图片描述

#可看到 make 命令后的输出,它执行了 Makefile 中编写的命令
#查看执行 make 命令后的目录内容,多了 test.txt 文件
ls
在这里插入图片描述

#执行 Makefile 的 targetd 目标,并查看,少了 tes

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值