Categories
程式開發

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 calculator
main.o : main.c cal.h
gcc -c main.c
mul.o : mmul.c cal.h
gcc -c mul.c
div.o : div.c cal.h
gcc -c div.c
add.o : add.c cal.h
gcc -c add.c
sub.o : sub.c cal.h
gcc -c sub.c
clean : 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中有许多变量预先就定义好了,可供我们直接调用。常用的预定义变量:

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

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

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

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

[email protected] 目标的完整名称。

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

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

2、使用通配符

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

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

calcultor : main.o mul.o div.o add.o sub.o
gcc *.o -o [email protected]
%.o : %.c
gcc -c $< -o [email protected] clean: rm -f *.o calcultor

五、总结

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

参考资料

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

- Makefile"-wikipedia