目录
make/makefile
make/makefile是什么?
你们如果是到了那些大型工程中,编写具有上千、上万条代码,一次编译完成之后又修改源代码,接着再想进行编译,此时便需要重新敲入指令,工作就会变得繁琐。在VS中,我们可以无限地修改自己的代码,然后随时编译运行,不需要考虑这些复杂的原理。
在Linux中也有这样的一站式操作-----【make/Makefile】
makefile官方解释:
一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。可见,makefifile都成为了一种在工程方面的编译方法。
简单的理解就是,Makefile就是一个比较特别的文件,在这个文件里边定义了一些规则,来帮助我们同时编译多个源文件。
make又是什么?为什么要使用make?
make是一个命令工具,是一个解释Makefile文件中指令的命令工具。比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作。
总之:make是一条命令,Makefile是一个文件,两者搭配使用,可完成项目自动化构建,接下来就来仔细讲讲它们是怎么搭配使用的。
makefile语法:
target(目标文件):文件(依赖文件) //依赖关系
<Tab>gcc -o 欲建立的执行文件 目标文件 //依赖方法
...
...
依赖关系/依赖方法:
- 依赖关系只有 依赖文件存在才能有target(目标文件),两者之间存在关系。
- 依赖方法是指target(目标文件)通过下面的命令转化成依赖文件。
- 依赖方法以gcc为例,也可以是其他的,但必须是tab键开头。
MakefileDemo:
-
先创建一个test.c文件
touch test.c
再对test.c文件编写
vim test.c
【vim相关不会的可以参考一下liunx中yum,rzsz及开发工具(vim)这篇文章。】 随便编写一些,如:
#include<stdio.h> int main() { printf("hello liunx\n"); printf("hello liunx\n"); printf("hello liunx\n"); printf("hello liunx\n"); printf("hello liunx\n"); printf("hello liunx\n"); return 0; }
-
创建makefile
vim makefile //可以直接vim //进入后编写 mytest:test.c gcc -o mytest test.c clean: rm -f mytest
- 直接【make】就可以实现target(目标文件),如想删除可以【make clean】,示例:
- 上面的
mytest:test.c gcc -o mytest test.c
是简洁的,复杂的应该是:
mytest:test.o 2 gcc -o mytest test.o 3 test.s:test.i 4 gcc -c test.i -o test.s 5 test.o:test.s 6 gcc -S test.s -o test.o 7 test.i:test.c 8 gcc -E test.c -o test.i 9 clean: 10 rm -f mytest.i mytest.s mytest.o mytest
- 结果:
- 1) 对于test.o来说,它依赖于test.s这个经过编译之后文件,可是【test.s】不存在,所以跳转到下一条依赖关系 。2)对于test.s来说,它依赖于test.i这个经过预编译之后的文件,可是【test.i】不存在,所以跳转到下一条依赖关系 。3) 对于test.i来说,它依赖于test.c这个源文件,查找后发现源文件存在,于是开始执行gcc命令
- 如果这个依赖的顺序改变后,当我再去
make
的时候,却发现这个执行的顺序还是按照没打乱之前的位置,这说明了一点:【make】会自动推导Makefile中的依赖关系 - 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。
make机制:
我们执行完一次make获取【mytest】
这个目标文件后,第二次再去执行【make】指令就不会其效果了,这是为何呢?
- 出现这种情况是为了提高编译效率。
- 上面也说过未被修改的文件进行编译是没有意义的。
源文件生成的时间一定比可执行文件的要早!!
stat指令:
【stat】这个指令来查看源文件和可执行文件的所有属性,不过要观察的还是其中一个叫做ACM时间。
- 【Access】: 最后一次访问该文件的时间。
- 【Change】:最后一次改变该文件属性或状态的时间。
- 【Modify】:最近一次修改文件内容的时间【比较的是这个时间】。
如:我对源文件的内容修改后,change,modify时间变化了。
【touch】指令,它除了创建文件之外还有其他功能。
- 若是要创建的这个文件不存在,那就将其创建出来。
- 若是要创建的这个文件存在,那就修改它的ACM时间为最新的时间。
经过对比发现都修改了。
.PHONY伪目标的原理:
像【clean】这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行, 不过,我们可以显示要【make】执行。即命令——【make clean】,以此来清除所有的目标文件,以便重编译。但是一般我们这种clean的目标文件,我们将它设置为伪目标,用 【.PHONY】 修饰,伪目标的特性是,总是被执行的。
下图,先是没有添加.PHONY,一直显示
加入后:
make/makefile小技巧:
注释:
makefile中可以使用 #
在行首表示行注释
特殊符号:
我们可以在Makefile中添加一些特殊符号,就可以起到一些特殊的功能即这个【$@
】
和【$^
】
,前者表示:
左侧被编译的所有内容,即【目标文件】,后者表示:
之后所有内容,即【依赖文件】。
编写了这么多【make】,你是否感觉每次都会出现回显很麻烦呢?能不能像我们在敲普通指令的时候一样,直接给出结果呢?
在【gcc】和【rm】的开头加【@】
结果:
这样就不用再看到执行的结果。
以上就是对make/makefile的理解,希望能帮助到各位。