《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的原理、伪目标及特殊符号含义等内容,篇幅较长,也较为复杂,希望大家可以多动手来理解其含义~

相关文章
|
1月前
|
测试技术
自动化测试项目学习笔记(五):Pytest结合allure生成测试报告以及重构项目
本文介绍了如何使用Pytest和Allure生成自动化测试报告。通过安装allure-pytest和配置环境,可以生成包含用例描述、步骤、等级等详细信息的美观报告。文章还提供了代码示例和运行指南,以及重构项目时的注意事项。
187 1
自动化测试项目学习笔记(五):Pytest结合allure生成测试报告以及重构项目
|
28天前
|
关系型数据库 MySQL Linux
基于阿里云服务器Linux系统安装Docker完整图文教程(附部署开源项目)
基于阿里云服务器Linux系统安装Docker完整图文教程(附部署开源项目)
234 3
|
1月前
|
Ubuntu Linux 编译器
Linux/Ubuntu下使用VS Code配置C/C++项目环境调用OpenCV
通过以上步骤,您已经成功在Ubuntu系统下的VS Code中配置了C/C++项目环境,并能够调用OpenCV库进行开发。请确保每一步都按照您的系统实际情况进行适当调整。
289 3
|
1月前
|
测试技术 Python
自动化测试项目学习笔记(四):Pytest介绍和使用
本文是关于自动化测试框架Pytest的介绍和使用。Pytest是一个功能丰富的Python测试工具,支持参数化、多种测试类型,并拥有众多第三方插件。文章讲解了Pytest的编写规则、命令行参数、执行测试、参数化处理以及如何使用fixture实现测试用例间的调用。此外,还提供了pytest.ini配置文件示例。
28 2
|
1月前
|
测试技术 Python
自动化测试项目学习笔记(二):学习各种setup、tearDown、断言方法
本文主要介绍了自动化测试中setup、teardown、断言方法的使用,以及unittest框架中setUp、tearDown、setUpClass和tearDownClass的区别和应用。
63 0
自动化测试项目学习笔记(二):学习各种setup、tearDown、断言方法
|
1月前
|
算法 Java Linux
java制作海报五:java 后端整合 echarts 画出 折线图,项目放在linux上,echarts图上不显示中文,显示方框口口口
这篇文章介绍了如何在Java后端整合ECharts库来绘制折线图,并讨论了在Linux环境下ECharts图表中文显示问题。
39 1
|
1月前
|
前端开发 JavaScript 应用服务中间件
linux安装nginx和前端部署vue项目(实际测试react项目也可以)
本文是一篇详细的教程,介绍了如何在Linux系统上安装和配置nginx,以及如何将打包好的前端项目(如Vue或React)上传和部署到服务器上,包括了常见的错误处理方法。
329 0
linux安装nginx和前端部署vue项目(实际测试react项目也可以)
|
1月前
|
机器学习/深度学习 人工智能 运维
构建高效运维体系:从自动化到智能化的演进
本文探讨了如何通过自动化和智能化手段,提升IT运维效率与质量。首先介绍了自动化在简化操作、减少错误中的作用;然后阐述了智能化技术如AI在预测故障、优化资源中的应用;最后讨论了如何构建一个既自动化又智能的运维体系,以实现高效、稳定和安全的IT环境。
65 4
|
1月前
|
运维 Linux Apache
,自动化运维成为现代IT基础设施的关键部分。Puppet是一款强大的自动化运维工具
【10月更文挑战第7天】随着云计算和容器化技术的发展,自动化运维成为现代IT基础设施的关键部分。Puppet是一款强大的自动化运维工具,通过定义资源状态和关系,确保系统始终处于期望配置状态。本文介绍Puppet的基本概念、安装配置及使用示例,帮助读者快速掌握Puppet,实现高效自动化运维。
54 4
|
9天前
|
机器学习/深度学习 数据采集 人工智能
智能运维:从自动化到AIOps的演进与实践####
本文探讨了智能运维(AIOps)的兴起背景、核心组件及其在现代IT运维中的应用。通过对比传统运维模式,阐述了AIOps如何利用机器学习、大数据分析等技术,实现故障预测、根因分析、自动化修复等功能,从而提升系统稳定性和运维效率。文章还深入分析了实施AIOps面临的挑战与解决方案,并展望了其未来发展趋势。 ####
下一篇
无影云桌面