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

相关文章
|
2月前
|
安全 Linux Shell
四、Linux核心工具:Vim, 文件链接与SSH
要想在Linux世界里游刃有余,光会“走路”还不够,还得配上几样“高级装备”。首先是Vim编辑器,它像一把瑞士军刀,让你能在命令行里高效地修改文件。然后要懂“软硬链接”,软链接像个快捷方式,硬链接则是给文件起了个别名。最后,SSH是你的“传送门”,不仅能让你安全地远程登录服务器,还能用scp轻松传输文件,设置好密钥更能实现免-密登录,极大提升效率。
340 3
|
2月前
|
安全 Linux iOS开发
SonarQube Server 2025 Release 5 (macOS, Linux, Windows) - 代码质量、安全与静态分析工具
SonarQube Server 2025 Release 5 (macOS, Linux, Windows) - 代码质量、安全与静态分析工具
163 0
SonarQube Server 2025 Release 5 (macOS, Linux, Windows) - 代码质量、安全与静态分析工具
|
2月前
|
Unix Linux 程序员
Linux文本搜索工具grep命令使用指南
以上就是对Linux环境下强大工具 `grep` 的基础到进阶功能介绍。它不仅能够执行简单文字查询任务还能够处理复杂文字处理任务,并且支持强大而灵活地正则表达规范来增加查询精度与效率。无论您是程序员、数据分析师还是系统管理员,在日常工作中熟练运用该命令都将极大提升您处理和分析数据效率。
226 16
|
4月前
|
缓存 监控 Linux
Linux系统性能调优技巧和相关工具
Linux 作为一种应用应展和系统服务的优选操作系统,在处理性能和端到端点评估上持有出色表现。但是,在处理进程或系统处于低效状态时,性能调优就显得十分重要。本文将探讨一些 Linux 系统性能调优的常用技巧,并介绍相关工具
145 1
Linux系统性能调优技巧和相关工具
|
4月前
|
Linux 数据安全/隐私保护 iOS开发
推荐Linux环境下效能优良的双向文件同步工具
综合上述条件,对于Linux环境下的双向文件同步需求,Unison 和 Syncthing 是两个非常出色的选择。它们都有良好的社区支持和文档资源,适用于不同规模的环境,从个人使用到商业部署。Unison 特别适合那些需要手动干预同步过程、需要处理文件冲突解决的场景。而 Syncthing 更加现代化,适合需要自动、实时的数据同步与备份的环境。对于选择哪一个,这将取决于个人的使用场景和具体需求。
471 16
|
3月前
|
数据采集 编解码 运维
一文讲完说懂 WowKey -- WowKey 是一款 Linux 类设备的命令行(CLT)运维工具
WowKey 是一款面向 Linux 类设备的命令行运维工具,支持自动登录、批量执行及标准化维护,适用于企业、团队或个人管理多台设备,显著提升运维效率与质量。
|
2月前
|
Linux 应用服务中间件 Shell
二、Linux文本处理与文件操作核心命令
熟悉了Linux的基本“行走”后,就该拿起真正的“工具”干活了。用grep这个“放大镜”在文件里搜索内容,用find这个“探测器”在系统中寻找文件,再用tar把东西打包带走。最关键的是要学会使用管道符|,它像一条流水线,能把这些命令串联起来,让简单工具组合出强大的功能,比如 ps -ef | grep 'nginx' 就能快速找出nginx进程。
374 1
二、Linux文本处理与文件操作核心命令
|
2月前
|
Linux
linux命令—stat
`stat` 是 Linux 系统中用于查看文件或文件系统详细状态信息的命令。相比 `ls -l`,它提供更全面的信息,包括文件大小、权限、所有者、时间戳(最后访问、修改、状态变更时间)、inode 号、设备信息等。其常用选项包括 `-f` 查看文件系统状态、`-t` 以简洁格式输出、`-L` 跟踪符号链接,以及 `-c` 或 `--format` 自定义输出格式。通过这些选项,用户可以灵活获取所需信息,适用于系统调试、权限检查、磁盘管理等场景。
279 137
|
2月前
|
安全 Ubuntu Unix
一、初识 Linux 与基本命令
玩转Linux命令行,就像探索一座新城市。首先要熟悉它的“地图”,也就是/根目录下/etc(放配置)、/home(住家)这些核心区域。然后掌握几个“生存口令”:用ls看周围,cd去别处,mkdir建新房,cp/mv搬东西,再用cat或tail看文件内容。最后,别忘了随时按Tab键,它能帮你自动补全命令和路径,是提高效率的第一神器。
627 57