彻底把握 Makefile(一)
介绍
makefile 就是一个能够被 make 命令解析的文件,他定义了一系列编译的规定,帮忙咱们更加不便、简洁的去实现编译的过程。在一个大工程当中咱们会有各种各样的文件,咱们可能会分模块去寄存各种文件,可能有些文件还依赖其余的文件,因而咱们在编译的时候须要先将被依赖的文件先编译,其余文件后编译,而咱们应用 makefile 就能够更好的去实现这件事儿。
Makefile 根底
在很多状况下咱们在 C /C++ 的我的项目当中应用 makefile,其实在其余语言的我的项目也能够应用 make 和 makefile,它不局限于语言的,能够是 C 也能够是 Java。当初写一个根本的例子:
demo: demo.c
gcc demo.c -o demo
clean:
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.o
myprint.o: myprint.c
gcc -c myprint.c -o myprint.o
clean:
rm myprint.o demo.o out
执行 make 之后的后果如下图所示:
因为咱们没有指定编译的指标,因而 make 会寻找一个编译指标,也就是 main
,然而在main
当中依赖两个文件 demo.o
和myprint.o
,因而 make 会再去执行 demo.o
和myprint.o
两个编译指标,当这两个编译指标被实现之后能力执行 main
的编译,依据 make 的输入后果咱们能够失去这一个后果的印证。
Makefilex 小技巧
Makefile 当中的变量
在 makefile 当中咱们能够定义一些咱们的变量这样就能够防止重复输出了。比方咱们常常会变编译文件的时候减少一些编译选项,比方像上面这样:
cflags=-c
main: demo.o myprint.o
gcc demo.o myprint.o -o out
demo.o: demo.c
gcc $(cflags) demo.c -o demo.o
myprint.o: myprint.c
gcc $(cflags) myprint.c -o myprint.o
clean:
rm myprint.o demo.o out
在下面的 makefile 当中咱们定义了一个变量 cflags
并且在编译命令当中应用,咱们定义变量的办法其实和 shell 差不多,咱们间接应用 =
能够定义变量,而后应用 $(变量名)
能够应用变量,因为下面的例子当中 cflag=-c
比拟短,比较简单,然而如果当咱们的编译参数很多很长的时候应用变量就十分无效了,而且如果在一个我的项目当中如果有成千上万个文件咱们像对立扭转编译时候的参数的话,咱们一个一个改是很麻烦的,然而如果咱们应用变量就能够做到一改全改。
Makefile 当中的 include 命令
在 makefile 当中咱们也能够应用 include 命令去蕴含其余的 makefile 文件,比方咱们将下面的 makefile 文件分成两个局部 makefile
和submakefile
:
makefile:
include submakefile
demo.o: demo.c
gcc $(cflags) demo.c -o demo.o
myprint.o: myprint.c
gcc $(cflags) myprint.c -o myprint.o
clean:
rm myprint.o demo.o out
submakefile:
cflags=-c
main: demo.o myprint.o
gcc demo.o myprint.o -o out
而后在目录下执行 make 命令,失去的成果和前文当中提到的 makefile 后果是一样的,这就相当于将 submakefile 的内容放到 include 语句的地位。
Makefile 中的 PHONY
在下面谈到的 makefile 当中有一个 clean
的编译指标用于革除咱们编译的后果文件,当初咱们在以后的目录上面减少一个文件 clean
在执行 make 命令,咱们的 makefile 文件如下:
cflags=-c
main: demo.o myprint.o
gcc demo.o myprint.o -o main
demo.o: demo.c
gcc $(cflags) demo.c -o demo.o
myprint.o: myprint.c
gcc $(cflags) myprint.c -o myprint.o
clean:
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=-c
main: demo.o myprint.o
gcc demo.o myprint.o -o main
demo.o: demo.c
gcc $(cflags) demo.c -o demo.o
myprint.o: myprint.c
gcc $(cflags) myprint.c -o myprint.o
clean:
rm myprint.o demo.o main
.PHONY: clean # 减少这一行
执行后果如下图所示:
咱们当初来测试一下当咱们命令生成的文件和编译指标不同名的时候,make 是如何解释执行 makefile 的,makefile 的内容如下:
cflags=-c
main: demo.o myprint.o
gcc demo.o myprint.o -o out # 这里的输入文件是 out 而不是 main 输入文件名字和指标不同名
demo.o: demo.c
gcc $(cflags) demo.c -o demo.o
myprint.o: myprint.c
gcc $(cflags) myprint.c -o myprint.o
clean:
rm myprint.o demo.o main
.PHONY: clean
执行后果:
从下面的后果咱们能够发现,当咱们编译的后果文件 (out) 和编译指标 (main) 不同名的时候 make 每次都回去执行这个指标,因为 make 在进行检测的时候没有发现 main 这个文件,因而每次执行 make 命令的时候都会去执行这个编译指标。
Makefile 的通配符
咱们当初批改后面的 makefile,批改的后果如下(当前目录上面有两个文件 demo.c 和 myprint.c):
cflags=-c
main: 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=-c
main: demo.o myprint.o
gcc demo.o myprint.o -o main
demo.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=-c
main: demo.o myprint.o
gcc demo.o myprint.o -o main
demo.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=./files
main: demo.o myprint.o a.o b.o
gcc demo.o myprint.o a.o b.o -o main
demo.o:demo.c
gcc $(cflags) demo.c
myprint.o:myprint.c
gcc $(cflags) myprint.c
a.o: a.c
gcc $(cflags) a.c
b.o: b.c
gcc $(cflags) b.c
clean:
rm myprint.o demo.o main
.PHONY: clean
执行后果如下图所示:
咱们当初批改咱们的 makefile 文件如下:
cflags=-c
VPATH=./files
main: demo.o myprint.o a.o b.o
gcc demo.o myprint.o a.o b.o -o main
demo.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=-c
VPATH=./files
main: demo.o myprint.o a.o b.o
gcc demo.o myprint.o a.o b.o -o main
demo.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、计算机系统根底、算法与数据结构)常识。