《Linux从练气到飞升》No.06 Linux项目自动化构建工具 make/Makefile 【云边有个小卖部】上新啦

简介: 《Linux从练气到飞升》No.06 Linux项目自动化构建工具 make/Makefile 【云边有个小卖部】上新啦

前言

前面第五章我们讲了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吧!

makefile的编写

在讲述之前回想一下,gcc是怎么执行程序的?

每次执行,它需要这样一个命令:

gcc -o test test.c

每次都要打一遍 gcc -o test test.c,确实有点麻烦,有没有办法简化呢?有的!

接下来我们先来见见“🐖”跑,后面再详细讲解。

见见🐖跑

第一步:建立makefile文件

建立makefile或者Makefile文件都可以

第二步:配置
test:test.c
  gcc -o test test.c

注意第二行必须是Tab而不是空格!

ESc键+:wq

保存退出

第三步:执行

怎么使用呢?

命令如下:

make
或者
make test

可以看到生成了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命令是如何工作的,原理是什么?

  1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
  2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“hello”这个文件,并把这个文件作为最终的目标文件。
  3. 如果hello文件不存在,或是hello所依赖的后面的hello.o文件的文件修改时间要比hello这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成hello这个文件。
  4. 如果hello所依赖的hello.o文件不存在,那么make会在当前文件中找目标为hello.o文件的依赖性,如果找到则再根据那一个规则生成hello.o文件。(这有点像一个堆栈的过程)
  5. 当然,你的C文件和H文件是存在的啦,于是make会生成hello.o文件,然后再用hello.o文件声明make的终极任务,也就是执行文件hello了。
  6. 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
  7. 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。
  8. 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的原理、伪目标及特殊符号含义等内容,篇幅较长,也较为复杂,希望大家可以多动手来理解其含义~

相关文章
|
3月前
|
安全 Linux 测试技术
Kali Linux预装的自动化渗透测试工具
Kali Linux预装的自动化渗透测试工具
177 2
|
4月前
|
Ubuntu Linux 持续交付
在Linux中,如何在Linux中使用Ansible进行自动化部署?
在Linux中,如何在Linux中使用Ansible进行自动化部署?
|
4月前
|
运维 Linux 持续交付
在Linux中,如何进行配置管理和自动化部署?
在Linux中,如何进行配置管理和自动化部署?
|
4月前
|
存储 监控 Linux
|
4月前
|
存储 Linux 测试技术
在Linux中,如何使用expect进行自动化交互式应用程序测试?
在Linux中,如何使用expect进行自动化交互式应用程序测试?
|
4月前
|
Linux 程序员 Python
python-office支持Mac和Linux吗?自动化办公用这个就够了
大家好,我是程序员晚枫。今天解答关于`python-office`是否支持Mac和Linux的问题。起初因需调用Office功能,该项目不完全支持这两个系统。现已通过拆分子项目解决了这一限制,例如`poexcel`可在多平台上运行Excel相关功能。只需简单修改导入语句,如使用`import poexcel`替代`import office`,即可在Mac和Linux上顺利执行。学习或使用中有任何疑问,欢迎留言交流!
|
4月前
|
jenkins Linux 持续交付
在Linux中,如何使用Jenkins和Ansible进行虚拟化环境的自动化和持续集成/持续部署(CI/CD)?
在Linux中,如何使用Jenkins和Ansible进行虚拟化环境的自动化和持续集成/持续部署(CI/CD)?
|
4月前
|
存储 运维 监控
|
4月前
|
监控 安全 Linux
在Linux中,如何编写自动化脚本来执行重复性任务?
在Linux中,如何编写自动化脚本来执行重复性任务?
|
4月前
|
Linux Shell PHP
奇怪,自从学会Linux这些“自动化”技巧,头发也不掉了!
奇怪,自从学会Linux这些“自动化”技巧,头发也不掉了!