写点什么

学习笔记丨 Makefile 的基本编写与优化

用户头像
Liuchengz.
关注
发布于: 2020 年 08 月 20 日
学习笔记丨Makefile的基本编写与优化

提示:本文中使用的操作系统环境为 Ubuntu18.0/64 位。

文章第一次发布于 CSDN:https://blog.csdn.net/liuchengz_/article/details/108062139


前言

在 Linux 系统下编译文件通常需要我们使用命令进行编译,而不像时在 window 系统下许多编译器可以一键将我们编写的代码编译完成,而当我们的源文件数量很多的时候,使用 Makefile 进行编译会很大程度上的提高我们的效率。

一、Makefile 是什么?


Makefile 其实就是一个文件,它默认命名为 Makefile(或者 makefile),它包含了一组自动化构建工具,用来生成目标文件的指令。我的理解就是将我们平时编译需要用到的一步步指令写到了一个脚本文件里,使用时只需要执行 make 指令就能执行 Makefile 中的指令。

二、Makefile 的基本格式

target ... : prerequisites ...	command	...	...
复制代码


这是一个文件的依赖关系,也就是说,target 这一个或多个的目标文件依赖于 prerequisites 中的文件,其生成规则定义在 command 中。值得注意的是,在 Makefile 中的命令,必须要以[Tab]键开始。


target代表目标文件,即你要生成的文件,可以是中间文件也可以是最终的可执行文件。target 还可以是一个 Label,即一些我们自义定的操作,例如常用的 clean,用于删除指定的文件。

prerequisites代表用于前面的target所需要的文件。

command代表 make 需要执行的命令,可以是任意的 Shell 指令。

举个例:我制作了一个计算器,其有加减乘除四个功能,我将其分为了多个文件编写,分别为 cal.h、main.c、mul.c、div.c、add.c、sub.c(举例而已,真是的情况应该不会有人这么做)。

代码如下(示例):


calculator : main.o mul.o div.o add.o sub.o 	gcc main.o mul.o div.o add.o sub.o -o calculatormain.o : main.c cal.h	gcc -c main.cmul.o : mmul.c cal.h	gcc -c mul.cdiv.o : div.c cal.h	gcc -c div.cadd.o : add.c cal.h	gcc -c add.csub.o : sub.c cal.h	gcc -c sub.cclean : rm calculator  main.o mul.o div.o add.o sub.o 
复制代码

我们将这个内容保存为 Makefile(或者 makefile)然后再该目录下输入命令make就可以生成执行文件 calculator 了。如果要删除执行文件和所有的中间目标文件,只需要输入make clean就可以了,

三、基本结构构成

1.变量定义

对于在 Makefile 中重复出现的文件,如刚才举例中的:main.c、main.o 等文件名,我看可以将其自定义成我们所命名的变量。在 Makefile 中变量一般都是字符串,有点像 C 语言中的宏,当 Makefile 被执行时,其中的变量都会被扩展到相应的引用位置上。

2.显示规则

make 会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在命令行前,那么,这个命令将不被 make 显示出来。如果 make 执行时,带入 make 参数“-n”或“--just-print”,那么其只是显示命令,但不会执行命令,这个功能可以用来调试我们的 Makefile,观察我们编写的 Makefile 的执行顺序。而 make 参数“-s”或“--slient”则是全面禁止命令的显示。

3.隐晦规则

在我们使用 Makefile 时,有一些我们会经常使用,而且使用频率非常高的东西,make 其实是预先规定好的,其具有自动推导的功能,它可以自动推导文件以及文件依赖关系后面的命令,如.o 文件由.c 文件编译而来,它们之间的依赖关系是预先就设定好的,于是我们就没必要去在每一个.o 文件后都写上类似的命令

4.文件指示

用于 Makefile 的嵌套执行,在大型的工程项目中,我们将不同的模块放在不同的文件夹,然后为每个文件夹编写相应的 Makefile,有利于我们 Makefile 的维护。

5.注释

Makefile 中只有行注释,和 UNIX 的 Shell 脚本一样,其注释是用“#”字符,类似于 C/C++中的“//”一样。

四、提高编写效率

在刚才的例子中我们对 6 个文件进行编译就 Makefile 就已经写了 13 行,似乎并不比我们手动将.c 文件编译成.o 目标文件然后再链接它们高效多少。其实再实际的工程项目中源文件远不止此,那么面对大量的源文件,我们可以灵活运用其基本结构中来提高我们的 Makefile 编写效率。

1、预定义变量

在 Makefile 中有许多变量预先就定义好了,可供我们直接调用。常用的预定义变量:

$* 不包含扩展名的目标文件名称。

$+ 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。

$< 第一个依赖文件的名称。

$? 所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚。

$@ 目标的完整名称。

$^ 所有的依赖文件,以空格分开,不包含重复的依赖文件。

$% 如果目标是归档成员,则该变量表示目标的归档成员名称。

2、使用通配符

在 Makefile 的编写中我们也可以用到我们平时使用到的通配符。如:星号代表任意个任意字符,*.o 代表文件夹中所有.o 文件。


上述示例中的 Make file 修改后代码示例(如下):


calcultor : main.o mul.o div.o add.o sub.o 	gcc *.o -o $@%.o : %.c	gcc -c $< -o $@clean:	rm -f *.o calcultor
复制代码


五、总结

Makefile 其实在我们日常的生活中编写代码使用到的并不多,只有在对量很大的代码进行编译时才能体现它的优势,但是,学会去写 Make file 对我对于语言的编译原理上的理解会有一些帮助,所以总结了些基本的知识,希望可以帮助到大家。

参考资料


- 《跟我一起写 Makefile》 作者:陈皓

- Makefile-wikipedia


发布于: 2020 年 08 月 20 日阅读数: 526
用户头像

Liuchengz.

关注

选择和时间做朋友 2018.09.18 加入

写作新人,请多批评和指正

评论

发布
暂无评论
学习笔记丨Makefile的基本编写与优化