彻底把握Makefile(一)

介绍

makefile就是一个能够被make命令解析的文件,他定义了一系列编译的规定,帮忙咱们更加不便、简洁的去实现编译的过程。在一个大工程当中咱们会有各种各样的文件,咱们可能会分模块去寄存各种文件,可能有些文件还依赖其余的文件,因而咱们在编译的时候须要先将被依赖的文件先编译,其余文件后编译,而咱们应用makefile就能够更好的去实现这件事儿。

Makefile根底

在很多状况下咱们在C/C++的我的项目当中应用makefile,其实在其余语言的我的项目也能够应用make和makefile,它不局限于语言的,能够是C也能够是Java。当初写一个根本的例子:

demo: demo.c    gcc demo.c -o democlean:    rm demo

下面是一个makefile的例子,咱们简要阐明一下makefile的书写规定:

编译指标:依赖文件    编译命令

而后咱们应用make命令去解释执行makefile,咱们能够应用make 编译指标去执行特定的语句,比方在下面的例子当中咱们执行make demo的话就会执行gcc demo.c -o demo命令。

其实下面咱们也能够间接应用make命令,不须要指定编译指标,因为make会本人寻找第一个指标作为被执行的指标。

在下面的代码当中咱们当咱们执行make的时候寻找到makefile文件的第一个指标demo,然而因为我没有改变demo.c这个文件,而且这个文件曾经编译过了,因而咱们没有必要再去编译这个文件,这也是make给咱们提供的一个十分好的个性,咱们不须要从新编译曾经编译好的文件,这在一个大型项目当中是十分有用的,当咱们的我的项目当中有成千上万的文件的时候,如果咱们从新编译每一个文件的话,那么编译的工夫耗费是十分大的。因而咱们在执行make执行执行clean编译指标先删除demo这个编译后果,而后在执行make这次它再找到demo指标,而此时demo曾经被删除了,因而会从新编译。

Make命令的工作流程

当咱们在命令行当中输出make的时候他的执行流程如下:

  • make命令首先会在当前目录上面寻找makefile或者Makefile文件。
  • 寻找到makefile文件之后,他会在文件当中寻找到一个编译指标,比方在下面的makefile文件当中他会找到demo这个编译指标,而不是clean这个指标,因为clean是第二个编译指标。
  • 而后make会解析编译指标的依赖,如果这个依赖是其余的编译指标A的话,那么make会先实现它依赖的编译指标A的命令,如果它依赖的编译指标A也存在依赖B的话,make就会去执行依赖的B的编译命令,如此的递归上来,晓得有所得依赖指标都存在了,才会实现第一个编译指标的编译,这个也很好了解,只有依赖文件都存在了咱们才可能实现正确的编译过程。

make编译的过程为寻找编译指标,依赖关系,如果依赖的文件还存在依赖那么make会一层一层的寻找上来,只有所有的依赖都被胜利解析了,才会最终执行第一个编译指标的编译命令。然而在makefile当中不被第一个编译指标的指标的编译命令是不会被执行的,比方下面咱们执行make的时候会执行demo编译指标,然而不会执行clean编译指标。

上面咱们写一个例子理解make解析依赖的过程:

main: demo.o myprint.o    gcc demo.o myprint.o -o out    echo make 解析编译实现demo.o: demo.c     gcc -c demo.c -o demo.omyprint.o: myprint.c     gcc -c myprint.c -o myprint.oclean:    rm myprint.o demo.o out

执行make之后的后果如下图所示:

因为咱们没有指定编译的指标,因而make会寻找一个编译指标,也就是main,然而在main当中依赖两个文件demo.omyprint.o,因而make会再去执行demo.omyprint.o两个编译指标,当这两个编译指标被实现之后能力执行main的编译,依据make的输入后果咱们能够失去这一个后果的印证。

Makefilex小技巧

Makefile当中的变量

在makefile当中咱们能够定义一些咱们的变量这样就能够防止重复输出了。比方咱们常常会变编译文件的时候减少一些编译选项,比方像上面这样:

cflags=-cmain: demo.o myprint.o    gcc demo.o myprint.o -o outdemo.o: demo.c     gcc $(cflags) demo.c -o demo.omyprint.o: myprint.c     gcc $(cflags) myprint.c -o myprint.oclean:    rm myprint.o demo.o out

在下面的makefile当中咱们定义了一个变量cflags并且在编译命令当中应用,咱们定义变量的办法其实和shell差不多,咱们间接应用=能够定义变量,而后应用$(变量名)能够应用变量,因为下面的例子当中cflag=-c比拟短,比较简单,然而如果当咱们的编译参数很多很长的时候应用变量就十分无效了,而且如果在一个我的项目当中如果有成千上万个文件咱们像对立扭转编译时候的参数的话,咱们一个一个改是很麻烦的,然而如果咱们应用变量就能够做到一改全改。

Makefile当中的include命令

在makefile当中咱们也能够应用include命令去蕴含其余的makefile文件,比方咱们将下面的makefile文件分成两个局部makefilesubmakefile:

makefile:

include submakefiledemo.o: demo.c     gcc $(cflags) demo.c -o demo.omyprint.o: myprint.c     gcc $(cflags) myprint.c -o myprint.oclean:    rm myprint.o demo.o out

submakefile:

cflags=-cmain: demo.o myprint.o    gcc demo.o myprint.o -o out

而后在目录下执行make命令, 失去的成果和前文当中提到的makefile后果是一样的,这就相当于将submakefile的内容放到include语句的地位。

Makefile中的PHONY

在下面谈到的makefile当中有一个clean的编译指标用于革除咱们编译的后果文件,当初咱们在以后的目录上面减少一个文件clean在执行make命令,咱们的makefile文件如下:

cflags=-cmain: demo.o myprint.o    gcc demo.o myprint.o -o maindemo.o: demo.c     gcc $(cflags) demo.c -o demo.omyprint.o: myprint.c     gcc $(cflags) myprint.c -o myprint.oclean:    rm myprint.o demo.o main

而后执行上面的命令(touch命令是新增一个文件,touch clean 就是往目录中减少一个名字为clean的文件):

咱们能够看到当目录上面减少一个clean文件之后,咱们应用make clean命令的时候,make给咱们的提示信息为clean文件是最新的了。这是因为当执行make 编译指标,make首先会查看当前目录上面是否存在clean文件如果不存在则执行编译指标clean的命令。如果存在clean文件的话,make会查看编译指标clean的依赖文件是否产生更改,如果产生更改了那么就会执行clean对应的命令,然而在下面的makefile当中clean没有依赖文件,因而相当于他的依赖文件没有产生扭转,make不会执行编译指标clean对应的命令了。

然而咱们的需要是依然心愿执行clean之后的命令,这个时候咱们就能够应用PHONY了,他能够保障即便存在clean文件的时候,make命令仍然会执行编译指标clean对应的命令。

cflags=-cmain: demo.o myprint.o    gcc demo.o myprint.o -o maindemo.o: demo.c     gcc $(cflags) demo.c -o demo.omyprint.o: myprint.c     gcc $(cflags) myprint.c -o myprint.oclean:    rm myprint.o demo.o main.PHONY: clean # 减少这一行

执行后果如下图所示:


咱们当初来测试一下当咱们命令生成的文件和编译指标不同名的时候,make是如何解释执行makefile的,makefile的内容如下:

cflags=-cmain: demo.o myprint.o    gcc demo.o myprint.o -o out # 这里的输入文件是 out 而不是 main 输入文件名字和指标不同名demo.o: demo.c     gcc $(cflags) demo.c -o demo.omyprint.o: myprint.c      gcc $(cflags) myprint.c -o myprint.oclean:    rm myprint.o demo.o main.PHONY: clean

执行后果:

从下面的后果咱们能够发现,当咱们编译的后果文件(out)和编译指标(main)不同名的时候make每次都回去执行这个指标,因为make在进行检测的时候没有发现main这个文件,因而每次执行make命令的时候都会去执行这个编译指标。

Makefile的通配符

咱们当初批改后面的makefile,批改的后果如下(当前目录上面有两个文件demo.c和myprint.c):

cflags=-cmain: demo.o myprint.o    gcc demo.o myprint.o -o main%.o: %.c     gcc $(cflags) $<clean:    rm myprint.o demo.o main.PHONY: clean

下面的makefile当中有一个通配符%,其中%.c示意当前目录上面的所有的以.c结尾的文件。下面的makefile与上面的makefile的成果是一样的:

cflags=-cmain: demo.o myprint.o    gcc demo.o myprint.o -o maindemo.o:demo.c    gcc $(cflags) demo.c myprint.o:myprint.c    gcc $(cflags) myprint.c clean:    rm myprint.o demo.o main.PHONY: clean

在下面的makefile当中 $< 示意第一个依赖文件,下面的%就是一个通配符,%.c能够匹配任何以.c结尾的文件,你可能会有疑难%.c匹配不是所有的.c文件吗?那么等价的后果不应该是:

cflags=-cmain: demo.o myprint.o    gcc demo.o myprint.o -o maindemo.o myprint.o:demo.c myprint.c    gcc $(cflags) demo.c clean:    rm myprint.o demo.o main.PHONY: clean

事实上你能够认为make会将通配符匹配的文件一一开展,有几个文件就将产生对应数目的编译指标,而不是将他们都放在一起。

Makefile文件主动搜寻

在一个工程项目当中咱们能够会有许多的目录以及源文件,而make命令只会主动搜寻当前目录下的文件,如果当前目录下没有那么make命令就会产生谬误。因而make也给咱们提供了一种文件搜寻的性能。

在makefile当中,咱们能够应用VAPTH指定make去搜寻文件的门路。咱们先来测试一下当咱们没有应用VPATH的时候指定其余目录下的文件会呈现什么状况,咱们的文件目录构造如下图所示:

咱们的makefile内容如下(先把VPATH的那一行正文掉):

cflags=-c# VPATH=./filesmain: demo.o myprint.o a.o b.o    gcc demo.o myprint.o a.o b.o -o maindemo.o:demo.c    gcc $(cflags) demo.c myprint.o:myprint.c    gcc $(cflags) myprint.c a.o: a.c    gcc $(cflags) a.cb.o: b.c     gcc $(cflags) b.cclean:    rm myprint.o demo.o main.PHONY: clean

执行后果如下图所示:

咱们当初批改咱们的makefile文件如下:

cflags=-cVPATH=./filesmain: demo.o myprint.o a.o b.o    gcc demo.o myprint.o a.o b.o -o maindemo.o:demo.c    gcc $(cflags) demo.c myprint.o:myprint.c    gcc $(cflags) myprint.c a.o: a.c    gcc $(cflags) $<b.o: b.c     gcc $(cflags) $<clean:    rm myprint.o demo.o main.PHONY: clean

再次执行make命令,执行后果如下图所示:

咱们能够看到依然出错了,这是因为尽管make能够找到a.c和b.c的地位,然而在执行编译命令的时候咱们执行的仍然是gcc -c a.c,这条命令是会在当前目录上来寻找a.c的,而不会在其余门路上来寻找a.c,在这里咱们能够应用$<去解决这个问题,$<示意第一个依赖的文件。

批改之后的makefile文件如下所示:

cflags=-cVPATH=./filesmain: demo.o myprint.o a.o b.o    gcc demo.o myprint.o a.o b.o -o maindemo.o:demo.c    gcc $(cflags) demo.c myprint.o:myprint.c    gcc $(cflags) myprint.c a.o: a.c    gcc $(cflags) $<b.o: b.c     gcc $(cflags) $<clean:    rm myprint.o demo.o main a.o b.o.PHONY: clean

执行后果如下图所示:

如果你想设置多条目录的话,设置的规定为VPATH=dir1:dir2:dir3:dir4

总结

在本篇文章当中次要介绍了一些makefile的根底用法,和一些罕用的小技巧,尽管不是很难,然而把握了能够很大的跳高咱们的工作效率,也能够不便咱们浏览他人写的makefile文件。


以上就是本篇文章的所有内容了,我是LeHung,咱们下期再见!!!更多精彩内容合集可拜访我的项目:https://github.com/Chang-LeHu...

关注公众号:一无是处的钻研僧,理解更多计算机(Java、Python、计算机系统根底、算法与数据结构)常识。