在之前学习C程序的编译过程中,我们知道编译一个程序还比较简单, 如果要编译多个文件,或者不同文件夹中的文件,需要生成不同的库文件,以及确定这些文件的编译先后顺序,往往所需要的命令行特别多,而且比较复杂,甚至对于以后项目的维护也比较麻烦。
再想想如果我们需要编译Linux内核这样好几W个文件的项目难道需要我们一个命令的输入吗 ? 那估计是一场噩梦。
那么这个时候如果我们能够把所有的编译规则全部规范到文件中,然后通过解析该文件去执行对应的编译指令,这样就大大简化指令的复杂度,同时降低了编译程序过程中所带来的错误。
根据上面的需求就产生了Makefile,我们的编译和处理规则就放在Makefile文件中,通过Makefile工具解析Makefile文件中的命令来指导整个工程的编译过程。
当然Makefile文件中的命令书写是有一定的规范的,这也是今天我们所要讲到的重点,一旦该文件编写好以后在Linux命令行中执行一条make命令即可自动编译整个工程,不但提高了开发效率也便于后期维护。
当然不同厂家的make稍有不同,并且语法上也有点区别,不过基本思想都差不多,主要还是落在目标依赖上来,这里以最广泛的GNU Make跟大家讲解。
上面的为大家展示了Makefile的核心规则,有点类似于一位厨师做菜,目标就是做一碗好菜,那么所谓的依赖就是各种食材、各种厨具等等,然而有了这些依赖还不够需要厨师有着非常好的厨艺才能做出一道好菜。
同时这些依赖也有可能此时此刻并不存在,需要现场制作,或者由其他厨师做好,那么这个依赖就成为了其他规则的目标,该目标也会有它自己的依赖和命令,这样形成一层一层递归依赖组成了Makefile文件。
Makefile并不会关心命令是如何执行的,仅仅只是会去执行所定义的命令,和我们平时直接输入命令行是一样的效果。
Makefile就相当于一个依赖关系文件,在执行该文件的以后会递归的查找依赖文件并执行对应的命令,最终生成第一个目标。
下面小哥就之前代码简单演示一下Makefile:
上图是makefile文件,最终为了生成Test文件,需要三个*.o文件,然后三个.o文件又分别依赖于对应的*.c文件,这样加上对应的gcc命令就构成了一个基本的makefile。
注意 : 2/4/6/8行均为tab键。
下面使用make命令来编译生成test目标文件
这样我们执行make命令一键就搞定了所有的编译任务,如果下次需要改变编译过程就直接修改对应的makefile文件中的规则即可。
有类比过windows里面IDE环境中编译器使用的小伙伴都知道在windows里面的开发工具编译通过一键即可搞定所有编译内容。难道windows中的开发工具更强大一些?
其实不然,我们在windows中构建对应的工程文件的过程中通过图形界面就悄悄的把类似的makefile文件给生成了,那么当点击编译的时候就相当于在命令行里面执行make,其实都是类似的,仅仅只是我们平时使用windows更多一点,更加容易接受。
当然这里仅仅只是演示了一些最简单的操作,比较麻烦的还是命令部分可以使用到通配符和shell指令等等,这样会增加一定的复杂度,其实makefile本身并没有太大的难度。
在命令行中使用make命令,makefile会在默认路径中查找对应的makefile文件来进行工程管理,我们一般把对应的makefile文件名命名为Makefile和makefile等,如果名字不匹配可能无法找到对应的makefile文件,比如:
makefile文件名
Makefile文件名
MAKEFILE文件名-编译失败
从上面的实验现象可以了解到make会查找默认的makefile文件名,如果没有找到规定的文件名就会报相应的故障。
不过如果平时在一个目录下存在多个makefile文件的时候,一般我们都会通过不同命名来进行区分,那么该如何指定对应的makefile文件进行识别解析呢 ?可以使用make -f选项,如下图所示:
在开发过程中存在需要注释掉makefile文件相应文本行可以在文件的行首使用#号:
上面是正常进行makefile编译目标文件的完整实例,并且输出了正确的结果,然后小哥使用#号屏蔽掉前面两行,如下图所示:
继续执行make命令,根据makefile的规则,会默认编译Test1.o目标文件:
这样我们获得了正确的结果,说明注释生效。
在makefile使用过程中默认是会把相应的处理过程信息进行回显,这样能够让开发者更好的了解makefile的处理过程.
不过,如果回显信息非常的庞大也是不利于开发者分析的,所以编辑人员可以使用@来取消相关信息的回显,如下图所示:
在命令tab键后加入@
这里我们可以看到使用make以后没有像之前使用的makefile文件那样存在回显的命令行信息了。
在makefile文件中有时候执行一些不需要有依赖命令的目标,称为伪目标。
上面是对应的makefile文件,其中里面加入伪目标clean,通过使用.PHONY修饰,这样可以直接使用make clean 来执行对应的命令。
上面执行make clean确实执行了清除操作。
在makefile中为了减少代码的重复,使用变量的概念来简化编写,如下面的makefile进行如下改造:
使用$(obj)来代替所有的目标文件,以后需要添加其他目标文件的话就只需要在变量obj处进行相应修改即可。
同时系统还存在其他默认的自动化变量,这样可以大大简化makefile文件,便于设计和后期维护,如:
$^ 表示所有的依赖文件
$@ 表示生成的目标文件
$< 代表第一个依赖文件
等等
makefile基础知识点小哥暂时就讲到这里,其实大部分工程项目自己完完全全编写的并不是很多,更多的修修改改,所以遇到了具体的疑问在进行查找或许效率更高。
从0学ARM专辑汇总
0. 到底什么是Cortex、ARMv8、arm架构、ARM指令集、soc?一文帮你梳理基础概念【科普】
1. 从0开始学ARM-安装Keil MDK uVision集成开发环境
2. 从0开始学ARM-CPU原理,基于ARM的SOC讲解
3. 从0开始学ARM-ARM模式、寄存器、流水线
推荐阅读
进群,请加一口君微信,带你嵌入式入门进阶。
点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。