文章目录
前言
一、make/Makefile 的使用
1.示例
2.编写 Makefile 文件
2.1生成
2.2清理
二、make 如何知道生成的可执行文件是否最新
前言
在Linux 环境下编写好C语言代码之后,我们需要使用编译工具gcc 将其翻译为可执行文件。可是,如果对代码进行多次修改,每次修改完成,都需要重新使用 gcc 指令,会显得非常麻烦,所以可以使用 Linux 下的自动化构建工具 make/Makefile 。make是一条命令,Makefile是一个文件,两个搭配使用,完成项目自动化构建。
此外,如果是写工程文件,那么一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,Makefile 定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。。
与此同时,平时使用如 Visual Studio 2019 等 IDE,是可以对程序进行调试的。在Linux环境下,也有专门的调试器 —— gdb。可以实现类似于 VS 环境下的调试,单步执行,监视变量、进入函数等等。
一、make/Makefile 的使用
1.示例
上面说过,make是一条命令,Makefile是一个文件。显然,就类似于 vim 的配置文件一样,Makefile里面定义了某些规则,当我们使用make指令的时候,就可以有选择性地调用这些规则,比如编译文件、删除文件等等。
下面通过一个例子来更好地理解这个内容。如下,编写了一个打印 "Hello World" 的测试文件,和简单的Makefile。
根据 Makefile 里面的内容可以大致猜到,起作用就是 编译test.c 文件,生成 test 这个可执行文件。
我们来测试一下,如下,直接使用 make 指令。发现自动执行上文写好的依赖方法,然后查看,确实生成了 test 文件,执行正常。
2.编写 Makefile 文件
2.1生成
如上,Makefile 里面第一行的内容,可以理解为:我要生成一个目标文件 test,其依赖于 test.c 。我们把这种关系称为依赖关系。第二行的内容被称作是依赖方法,其定义了:如何通过 test.c 生成 test 。我们可以再小小总结一下:Makefile 是围绕依赖关系和依赖方法进行自动化编译的工具。
那么,我们可不可以分步来生成 test 文件呢?自然是可以的,由于冒号左边是依赖于冒号右边,所以我们可以如下图这样写,test 依赖于 test.o , test.o 依赖于 test.s ,test.s 依赖于 test.i ; test.i 依赖于 test.c 。
实际上,只使用 make 指令的话,只会执行 Makefile 里面的第一个依赖关系和依赖方法。但是,上图中,第一个依赖关系是 test : test.o , 可是当前目录下并没有 test.o 文件,下面又有 test.o 依赖于 test.s ,所以还要寻找 test.s …… 一直这样下去,直到找到最后一个依赖关系—— test.i 依赖于 test.c ,test.c 是存在的,所以可以执行这个依赖方法,那么生成了test.i ,又可以执行上面的依赖方法……(依次执行上面的依赖方法。)
上面一步步生成 test 文件只是做一个示例,为了初步了解 Makefile ,一般情况下,不推荐这样,还是推荐最开始那样,直接生成。
2.2清理
有时,我们会需要删除文件,也可以使用 Makefile 配置实现自动化删除。如下图,配置Makefile 里面多了一个关于 clean的 依赖关系 和 依赖方法。其中,可以看到,clean:的右边是没有内容的,即 clean 不依赖于任何文件,这种写法是可以的。但是 clean:不可以去掉,否则不知道这个依赖方法是属于哪个依赖关系。至于多出来的 .PHONY:clean 是什么意思,下文会讲解。
但是,在命令行里执行的时候,为什么不是使用 make 而是 make clean 呢?其实上文提到过一些,如果只使用 make 指令,那么就会执行第一组依赖关系和依赖方法。但是,这里clean是第二组依赖关系和依赖方法,所以要使用 make clean ,让它知道我们要使用的是 clean 这一组依赖关系和依赖方法。
可是 Makefile 文件里面为什么会多了一行 .PHONY:clean 呢?.PHONY: 是一个关键字,在这里被用来修饰 clean。.PHONY: 的意思是说:clean 总是被执行的。直接根据字面意思来理解可能会十分困难,可以通过例子来理解。下图中,使用 make 指令,第二次的时候却被告知 test 文件已经是最新的了,所以无法构建。但是,有 .PHONY: 修饰的 clean ,却可以一直执行。
所以可以以此来总结:有 .PHONY: 修饰之后,该组依赖关系和依赖方法不会因为文件的性质(是否存在、是不是最新版的)而决定执不执行,只要命令行输入了,那么就一定会被执行。注意,被 .PHONY: 修饰的,一般被称作伪目标。
我们用 .PHONY: 修饰 test 之后,就可以一直执行了,但是正常不会这样写,因为如果代码很多,那么编译代码是非常耗时间的。
二、make 如何知道生成的可执行文件是否最新
上文有提到,如果不适用 .PHONY: 修饰 test,那么第二次执行 make 指令的时候,会提示 test 文件已经是最新的了,make 是如何判别的呢?
实际上,由于 test 依赖于 test.c ,所以是根据 test.c 最后修改时间 ,和 test 的生成时间来判断。如果 test.c 最后修改时间 比 test 生成的时间早,那么这个 test 就是最新的;反之就不是最新的,可以重新生成。
如下,一开始 test.c 文件最后修改时间是比 test 生成时间早;然后对 test.c 进行修改,明显看到其最后的修改时间变了,比 test 文件的生成时间要晚,此时就可以执行 make 。成功编译,运行。
根据这个特性,我们可以对 make 进行”欺骗“,即 test.c 文件里的内容不修改,但是也可以多次make。这就需要使用 touch 指令的特性 —— 执行 touch test.c ,如果 test.c 文件已经存在,就只会改变它的 最后修改时间 。根据这里就可以达到 "欺骗" ,如下图。
关于 make / makefile 的知识就先讲到这里啦,有问题欢迎指出讨论!!