作者: 主页
我的专栏 C语言从0到1 探秘C++ 数据结构从0到1 探秘Linux 菜鸟刷题集 欢迎关注:点赞收藏✍️留言
码字不易,你的点赞收藏❤️关注对我真的很重要,有问题可在评论区提出,感谢阅读!!!
前面第五章我们讲了gcc/g++
的使用及编译过程,你会发现命令很长,写起来很烦!
有没有简单一点的方式来执行程序呢?
有的!这就是我们今天要讲的Linux项目自动化构建工具 make/Makefile。
有时候会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力。
为什么这么说呢?
因为一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,
如果要我们一下下敲命令不知道要敲到什么时候去,而makefile定义了一系列的规则来指定,
哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,
甚至于进行更复杂的功能操作,这就是makefile带来的好处——“自动化编译”,
一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
make命令,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,
比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。
可见,makefile都成为了一种在工程方面的编译方法。
总结起来就是:make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。
让我们一起来学习make/makefile
吧!
在讲述之前回想一下,gcc是怎么执行程序的?
每次执行,它需要这样一个命令:
gcc -o test test.c
每次都要打一遍 gcc -o test test.c
,确实有点麻烦,有没有办法简化呢?有的!
接下来我们先来见见“”跑,后面再详细讲解。
建立makefile
或者Makefile
文件都可以
test:test.c
gcc -o test test.c
ESc键+:wq
保存退出
怎么使用呢?
命令如下:
make
或者
make test
然后执行看看
./test
接下来讲述一下它的具体含义,我们以编译过程的makefile
文件内容为例:
hello:hello.o
@gcc -o hello hello.o
hello.o:hello.s
@gcc -c hello.s -o hello.o
hello.s:hello.i
@gcc -S hello.i -o hello.s
hello.i:hello.c
@gcc -E hello.c -o hello.i
.PHONY:clean
clean:
@rm -f hello.o hello.s hello.i
这是它的完整代码。
他们之间有着依赖关系
hello
,它依赖 hello.o
hello.o
, 它依赖 hello.s
hello.s
, 它依赖 hello.i
hello.i
, 它依赖 hello.c
有依赖关系还不够,你得告诉它要干什么对吧,这就需要依赖方法
gcc hello.* -option hello.*
,就是与之对应的依赖关系
然后讲一下make
在默认的方式下,也就是我们只输入make
命令是如何工作的,原理是什么?
make
会在当前目录下找名字叫“Makefile”
或“makefile”
的文件。“hello”
这个文件,并把这个文件作为最终的目标文件。hello
文件不存在,或是hello
所依赖的后面的hello.o
文件的文件修改时间要比hello
这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成hello
这个文件。hello
所依赖的hello.o
文件不存在,那么make
会在当前文件中找目标为hello.o
文件的依赖性,如果找到则再根据那一个规则生成hello.o
文件。(这有点像一个堆栈的过程)make
会生成hello.o
文件,然后再用hello.o
文件声明make
的终极任务,也就是执行文件hello
了。make
的依赖性,make
会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。make
就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make
根本不理。make
只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。touch命令可以将存在的文件的时间修改为现在的时间
命令如下touch hello.o
stat可以查看文件最近一次的查看文件、修改文件内容、修改文件属性的时间
命令如下:stat hello.o
最近访问:以前设计的是只要访问了,就修改时间,现在则是访问几次才修改访问时间(不修改文件的情况下)
最近更改和最近改动的关系就是,修改文件内容就会修改“最近更改”的时间,修改文件属性就会修改“最近改动”的时间,一般情况下,修改文件内容后,“最近更改”和“最近改动”都会修改时间,因为修改文件内容不可避免会改变文件的大小,也就改变了文件的属性。
还有个问题就是,我们前面说如果
hello
文件不存在,或是hello
所依赖的后面的hello.o
文件的文件修改时间要比hello这个文件新,那么,他就会执行后面所定义的命令来生成hello这个文件。
我们用下面这个例子来验证一下
- 当生成所有文件后修改
hello.o
文件的“修改时间”为现在,也就是让hello.o
文件比hello
文件新,然后用hello.s
文件作对比make
一下- 我们会发现
hello.o
文件的访问时间被修改了,而hello.s
文件的则是没有改变- 这是因为系统中有这么一套机制,当
hello.o
文件新于hello
文件,那么它就要重新编译,而hello.s
则不用改变,来提高编译的效率。
[venus@localhost Hello]$ make
[venus@localhost Hello]$ ll
总用量 60
-rwxr-xr-x. 1 venus venus 24272 7月 29 17:26 hello
-rw-r--r--. 1 venus venus 154 7月 29 17:00 hello.c
-rw-r--r--. 1 venus venus 16843 7月 29 17:26 hello.i
-rw-r--r--. 1 venus venus 1496 7月 29 17:26 hello.o
-rw-r--r--. 1 venus venus 450 7月 29 17:26 hello.s
-rw-r--r--. 1 venus venus 666 7月 29 17:07 makefile
[venus@localhost Hello]$ stat hello.o
文件:hello.o
大小:1496 块:8 IO 块:4096 普通文件
设备:fd00h/64768d Inode:220642 硬链接:1
权限:(0644/-rw-r--r--) Uid:( 1000/ venus) Gid:( 1000/ venus)
环境:unconfined_u:object_r:user_home_t:s0
最近访问:2023-07-29 17:26:29.634145794 +0800
最近更改:2023-07-29 17:26:29.631145778 +0800
最近改动:2023-07-29 17:26:29.631145778 +0800
创建时间:2023-07-29 17:26:29.630145773 +0800
[venus@localhost Hello]$ stat hello.s
文件:hello.s
大小:450 块:8 IO 块:4096 普通文件
设备:fd00h/64768d Inode:220641 硬链接:1
权限:(0644/-rw-r--r--) Uid:( 1000/ venus) Gid:( 1000/ venus)
环境:unconfined_u:object_r:user_home_t:s0
最近访问:2023-07-29 17:26:29.631145778 +0800
最近更改:2023-07-29 17:26:29.625145748 +0800
最近改动:2023-07-29 17:26:29.625145748 +0800
创建时间:2023-07-29 17:26:29.622145732 +0800
[venus@localhost Hello]$ touch hello.o
[venus@localhost Hello]$ stat hello.o
文件:hello.o
大小:1496 块:8 IO 块:4096 普通文件
设备:fd00h/64768d Inode:220642 硬链接:1
权限:(0644/-rw-r--r--) Uid:( 1000/ venus) Gid:( 1000/ venus)
环境:unconfined_u:object_r:user_home_t:s0
最近访问:2023-07-29 17:27:10.131354671 +0800
最近更改:2023-07-29 17:27:10.131354671 +0800
最近改动:2023-07-29 17:27:10.131354671 +0800
创建时间:2023-07-29 17:26:29.630145773 +0800
[venus@localhost Hello]$ make
[venus@localhost Hello]$ stat hello.o
文件:hello.o
大小:1496 块:8 IO 块:4096 普通文件
设备:fd00h/64768d Inode:220642 硬链接:1
权限:(0644/-rw-r--r--) Uid:( 1000/ venus) Gid:( 1000/ venus)
环境:unconfined_u:object_r:user_home_t:s0
最近访问:2023-07-29 17:27:39.926508348 +0800
最近更改:2023-07-29 17:27:10.131354671 +0800
最近改动:2023-07-29 17:27:10.131354671 +0800
创建时间:2023-07-29 17:26:29.630145773 +0800
[venus@localhost Hello]$ stat hello.s
文件:hello.s
大小:450 块:8 IO 块:4096 普通文件
设备:fd00h/64768d Inode:220641 硬链接:1
权限:(0644/-rw-r--r--) Uid:( 1000/ venus) Gid:( 1000/ venus)
环境:unconfined_u:object_r:user_home_t:s0
最近访问:2023-07-29 17:26:29.631145778 +0800
最近更改:2023-07-29 17:26:29.625145748 +0800
最近改动:2023-07-29 17:26:29.625145748 +0800
创建时间:2023-07-29 17:26:29.622145732 +0800
.PHONY
伪目标从前面我们可以看到
make
会根据源文件和目标文件的新旧来判断是否需要重新执行依赖关系进行编译,依赖关系不一定总是被执行,那如果我想要对应的依赖关系总是被执行呢?这就需要.PHONY
伪目标来实现。
拿最后的clean
为例,去掉.PHONY:clean
假设有这么一种情况,同目录下,有个和
clean
同名的文件,此时执行make clean
操作会发生什么?
可以看到的是make clean
命令无法删除对应的文件了
而声明了.PHONY之后,即使当前目录下存在与伪目标同名的文件或目录,make 仍然会执行伪目标规则。
我们把.PHONY:clean
加上去
结果表明通过使用.PHONY
声明伪目标,我们可以确保当执行相应的操作时,不受同名文件或目录的干扰。
$@ $^
:可以把指令中的文件名,冒号左边的改为$@
,冒号右边的改为$^
,效果和直接写出文件名是一样的。
本篇讲述了make/makefile
的使用,makefile
中的依赖关系、依赖方法、make
的原理、伪目标及特殊符号含义等内容,篇幅较长,也较为复杂,希望大家可以多动手来理解其含义~