今天我们来了解一下一个强大的工具Makefile。它可以自动帮我们自动处理代码:
Makefile
我们来看看度娘的解释:
一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,也可以执行操作系统的命令。
说白了,就是在Linux下编译代码时我们不用再gcc -o…巴拉巴拉再写一大堆了,我们只需make一下就行了。
具体我们来实际操作一下:
创建Makefile
首先我们像以前创建普通文件一样创建Makefile文件:
但是请注意这里的文件名就只能是Makefile,不能修改成其他的名字哦~
之后我们用vim写一段简单的代码:
之后用vim打开我们之前建的Makefile文件:
我先把代码写出来:
之后我们退出来,执行make命令:
此时我们看到在make之后执行了编译的命令,产生了一个mybin文件,我们应该是可以在目录下找到的:
我们可以来执行一下:
成功了,现在我们想清理掉这个mybin文件,我们可以直接make clean。
现在,这个mybin文件就被删除掉了。
分析
我们来分析一下我们刚才写的Makefile文件:
依赖关系
我们一行一行的来分析:
首先我们来看第一行,我们之前看到过执行make之后会形成一个mybin文件,跟我们第一行的第一个单词是一样的。Test.c更在mybin的后面。
其实我们大概可以猜出来:Test.c经过了编译生成了名为mybin的可执行文件。
我们称像这样通过已有的文件编译形成的文件的这种关系,我们叫做依赖关系,(mybin依赖Test.c形成)
我们可以试着改一改形成文件的名字:
这时候我们来看看形成的文件还是不是mybin:
我们看到文件名还是mybin,没有变化,那是因为我们** -o 后面的文件名没变**。
这里我们可知,最终形成的文件名跟依赖关系中的要形成的文件名没有任何关系,其实MMybin更像一个标签代表了一个方法(生成可执行文件的方法)。
依赖方法
第二行我们用Tab键空了一格(这个非常重要!),然后写下了gcc Test.c -o mybin 这样的代码,大家其实都可以猜的出来,这段代码就是我们的依赖方法,依赖关系要靠依赖关系来实现。
就比如我要mybin:Test.c的依赖关系实现(通过Test.c生成mybin文件),那么必须有一个指令或者一个方法来帮我实现这样的关系,我们想来想去,最终用gcc编译这个办可以帮我们实现,于是在依赖关系的下一行,用Tab空一格,写下gcc编译的代码
这段有关gcc的代码就是我们的依赖方法。
当依赖关系和依赖方法都完整时,我们执行make,就会从上往下逐步执行命令,我们可以验证:
现在我们执行一下make:
现在优先执行的是clean,因为在clean在前面,现在我们执行一下创建文件的命令:
这里大家发现我是用的MMybin,因为我之前将mybin改成了MMybin,代表关系的标签被改了,所以我们要用被改过的新的标签来执行我们的命令。
自动识别是否更新
我们把代码改回来:
然后make一下:
如果此时我们再make一下:
此时它告诉我此时已经是最新的mybin了,不用再make了。
如果之后我们再尝试,都是一样的结果:
但是如果此时我用vim打开了Test.c,然后不做任何事情退出:
此时我们再make一下:
发现此时可以make了,这可真奇怪,这是为什么呢?
这时我们要引入一个指令stat,来查看一个文件的属性:
stat
我们用这个指令来查看Test.c的文件属性:
在这里我们着重注意这几个点:
Access:最后访问该文件的时间
Modify:最后修改文件内容的时间
Change:最后修改文件属性的时间
这里我们可以用stat来查看Test.c的文件属性。
我们来查看依靠Test.c生成的mybin文件的属性:
我们知道mybin依赖Test.c生成,那么就意味着mybin的Access,Modify,Change都应该比Test.c晚,如果mybin的这几个时间都比Test.c的要晚,那么说明这时的mybin是最新的,就不用再次编译。
我们从上图可以看出mybin的时间是要比Test.c的要晚,这时make是没用的:
如果我这时用vim打开了Test.c:
我们此时再来查看Test.c的文件属性(和mybin的时间来作对比):
这时mybin的时间比Test.c的时间要早了,表明这时的mybin不是最新的了,这时make可以重新编译生成最新的mybin文件。
想想我们之前做了什么操作:用vim打开了Test.c,那就表明Test.c的Access时间会被修改(我这的不知道为什么Modify和Change的时间都被改了,按道理应该不会被改的,可能是不同服务器的关系。)
我们尝试修改一下Test.c的Modify:
我们用vim打开Test.c然后增添一小点代码:
此时我们再来看看Test.c的属性:
我们发现Access,Modify,Change,三个时间都被改了,Access很正常,因为我们用vim访问了文件,Modify也正常,因为我们修改了代码的内容,但是Change是修改文件属性,怎么会被改呢?
那是因为我们在修改代码的过程中,代码的大小会发生变化,大小也是文件属性的一部分,所以Change时间也会被改变。
我们来看一个只会修改Change时间的操作:我想去掉Test.c所属组的读权限:
此时我们发现只有Test.c的Change被修改,因为修改权限只属于文件属性的问题,不会影响其他的属性。这是再make也是可以的:
总之一句话:如果形成的可执行文件比原来的文件时间要早,此时make才会更新。
.PHONY伪目标
之前我们写clean的时候,前面写了一个**.PHONY**,这个伪目标的意思是:总会被执行的操作:
比如我可以一直执行make clean的操作:
其实如果我们需要的话我们可以把生成mybin文件的操作也定义成.PHONY:
这时生成mybin文件就不用关注文件时间了,可以一直被执行:
其他操作
其实我们在makefile里面可以定义变量:
此时可以make一下:
这里注意一下,使用变量时用美元符号($)开头:
我们还可以使用一些特殊组合:
$^:表示冒号后的内容。
$@:表示冒号前的内容。