目录
一、概念理解
1、基本概念
2、举例说明
二、编写 Makefile
1、依赖关系和依赖方法
2、文件清理
3、扩展内容
一、概念理解
1、基本概念
在我们学习 Linux 的过程中,我们可以直接使用 gcc 指令对程序的文本文件逐个进行编译处理,这是因为我们写的程序很少,可能只有两三个。
但是如果在日后的工程项目之中,我们要编译处理的程序文本文件会非常的多,难道我们还是需要使用 gcc 指令对每一个文件都进行一次编译吗?这样效率也太低了。所以我们需要引出一个概念:Makefile 和 make。
会不会写Makefile,从一个侧面说明了一个人是否具备完成大型工程的能力。Makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个 make 命令,整个工程完全自动编译,极大的提高了软件开发的效率。
make 是一个命令工具,是一个解释 Makefile 中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。
make是一条命令,Makefile是一个文件,两个搭配使用,完成项目自动化构建。Makefile 文件一般放在当前的源代码路径下。
2、举例说明
接下来我们快速写一个 demo ,来见一见 Makefile 文件是如何使用的。我们先在当前源代码路径下创建一个 Makefile 文件:
touch Makefile
当前源代码 myfile.c 所在的路径下就有了一个 Makefile 文件。我们打开 Makefile 文件,进行编写,操作细节大家可能还不理解,不过没关系,下面的内容会详细的介绍,现在只是让大家了解 Makefile 的用法:
保存退出。从此之后,我们想再去编译源代码文件 myfile.c 就不再需要在命令行里敲 gcc 指令了。可以直接使用 make 指令,它会自动的在当前目录下查找 Makefile 文件,然后执行我们刚刚内置好的指令:
make
此时我们的可执行程序已经生成了,那么我们如果想把所有的可执行程序都删除怎么办呢,数量较少可以直接使用 rm 指令,如果数量较多,还是需要使用 Makefile 文件来处理。
继续打开 Makefile 文件进行编辑:
保存退出。这样我们清理的时候直接使用指令:
make clean
可运行文件已经被删除了。
从此之后,我们对于该路径下的任何源文件进行修改之后,都直接使用 make 命令直接重新进行编译,不想要了就直接使用 make clean 命令进行清除,非常简单快捷。这就是 Makefile 。
二、编写 Makefile
相信同学们看了以上操作,对于 Makefile 文件的编写一定有很多疑问,接下来我就来说明它是如何编写的。
1、依赖关系和依赖方法
打开 Makefile 文件
来看第一行代码,我们形成 myfile 这个目标文件所依赖的文件是 myfile.c 。意思是要用 myfile.c 形成 myfile 。我们把他们之间的关系称为 依赖关系 。
根据 myfile.c 要怎么形成 myfile 呢?我们来看第二行代码,这一行代码被称为 依赖方法 。依赖方法必须要以 Tab 键开头,这是依赖方法的固定语法格式。
所以 Makefile 文件是一个围绕 依赖关系 和 依赖方法 构建的一个自动化编译的工具。Makefile 文件生效必须要有 正确的依赖关系 + 正确的依赖方法。
在 Makefile 文件被识别时,是 从上往下识别 的。我故意把前两行代码写的复杂一些,方便大家理解:
开始时,myfile 文件依赖于 myfile.o 文件。但是程序中没有 myfile.o 文件,于是向下找到 myfile.o 文件依赖于 myfile.s 文件。但是程序中没有 myfile.s 文件,于是向下找到 myfile.s 文件依赖于 myfile.i 文件。这样依次向下寻找,终于找到了程序中存在的 myfile.c 文件,根据依赖关系与依赖方法生成 myfile.i 文件,依次向上返回,这个识别方式是借助 栈 来实现的。最终得到了 myfile 文件。
这种写法是我故意复杂化的写法,只是让大家理解 Makefile 文件的识别方式,实际上没有人这么写代码。
2、文件清理
打开 Makefile 文件,我先对 Makefile 做一下准备做的修改:
依赖关系中,目标文件对应的依赖文件列表可以是空。 保存退出。
我们再来执行 make clean 指令:
可以看到指定字符确实打印出来了。那么我们在执行清除指令时,为什么要在 make 后面加一个 clean 呢?
这是因为 myfile 是我们从上到下扫描所遇到的第一组依赖关系与方法。而 clean 是第二组依赖关系与方法。Makefile 在从上到下扫描时,默认只会执行第一组依赖关系与方法。所以我们执行编译操作只需要 make 指令就可以了,而执行 clean ,就需要 make clean 。
而我们之前所写的这个第10行代码是什么意思呢? .PHONY 是 Makefile 语法当中的一个关键字,用来修饰目标文件。
.PHONY 含义:总是被执行的。
这句话可能不是很好懂,我来说明一下:如果我们重复多次使用 make 指令来编译 myfile 文件,会出现这样的提示:
显示我们当前的可执行程序是源代码匹配的最新的可执行程序,不需要再被更新了。这种现象就叫做 不是总是被执行的。
我们给 myfile 也加上 .PHONY :
再来试一试多次输入 make 指令:
可以看到已经变为 总是被执行的了。
我们称被 .PHONY 修饰的符号为 伪目标,伪目标的典型特点,就是总是被执行的。
为了防止时间的浪费,我们一般把程序的编译设为 不是总被执行的,程序的清理设为 总是被执行的:
同学们注意,问题又来了!make 指令怎么能知道当前的可执行程序是源代码匹配的最新的可执行程序了呢?
是通过对比时间来判断的,正常来说,一定是先有的源代码,然后才有的编译之后的可执行程序,也就是说可执行程序的最近修改时间一定在原代码最近修改的时间之后,如果不符合这个条件,就重新编译。
3、扩展内容
根据以上原理,我们可以通过修改时间来欺骗 make 指令。具体操作如下:
显示文件时间指令:
stat [文件名]
再使用 touch 指令操作:
touch [文件名]:如果文件不存在,则创建该文件。如果该文件存在,更新该文件的时间。
touch myfile.c
此时,源文件的时间就比可执行文件的时间更新了,我们就可以再次使用 make 指令来编译文件了。
关于 Makefile 的基本知识就讲到这里,希望同学们多多支持,如果有不对的地方,欢迎大佬指正,谢谢!