深入了解Linux —— make和makefile自动化构建工具

devtools/2024/12/29 8:21:11/

什么是make/makefile

在之前写代码的过程中,我们都是对一个文件进行编译链接(gcc编译),但是如果一个项目中,源代码文件非常的多,我们总不能一个一个的进行编译链接,这也太麻烦了;所以现在就来学习make/makefile实现自动化构建

make是一个命令工具,是一个解释makefile在指令的命令工具,大多数的IDE都存在这个命令。

makefile成为一种在工程方面的编译方法。

  • makefile是一个文件,make是一个命令;二者搭配使用来完成项目的自动化构建
  • makefile的好处就是自动化构建,写好makefile文件以后就只需make命令就可以完成项目工程的自动化构建,提点高了软件开发的效率

其实会不会写makefile文件,也侧面反映了其是否具备完成大型工程的能力

在一个工程中源代码文件,按照类型、功能、模块分别放到了若干个目录下,makefile定义一系列的规则来指定哪些文件需要先编译、哪些文件需要后编译,哪些文件需要重新编译,甚至进行更加复杂的操作。

makefile基本使用

首先先来看一个makefile如何使用(以及如何编写)

现在有一个已经写好的makefile文件(这里简单使用一下)

在这里插入图片描述

我们在当前目录下编写一个code.c文件,并且写上一个简单的代码

在这里插入图片描述

现在就来使用make命令

在这里插入图片描述

我们看到,使用make命令就自动编译code.c文件形成code可执行程序了。

现在就来看一下makefile中内容有和含义呢?

code:code.cgcc code.c -o code
.PHONY:clean
clean:rm -f code

1. 依赖关系和依赖方法

在这里插入图片描述

对于上述makefile文件,在介绍之前我们先来想要个问题,就是依赖关系依赖方法

  • 依赖关系:所谓依赖关系就表明两个事物(这里指目标文件和依赖文件)之间关系(就像现实生活中两个人之间的关系一样)
  • 依赖方法:有了依赖关系,那应该做什么呢?,依赖方法就表明(这里表示要如何生成目标文件);(就像现实生活中,你需要向朋友寻求帮助,有了你们是朋友这一关系,接下来就要表明你要做什么了)

了解了依赖关系依赖方法,接下来看目标文件和依赖文件

​ 目标文件和依赖文件,其实就是两个有着依赖关系的文件(依赖文件可以有多个),如上图就表示code依赖code.c文件

而依赖关系就表明了,如何由依赖文件列表生成目标文件

这样我们就很好理解了,上述makefile文件中,code作为目标文件、code.c作为依赖文件、通过依赖关系gcc code.c -o code来生成目标文件。

2. 伪目标.PHONY

对于上述makefile,我们连续执行多次make就会发现,不能执行

但是我们如果连续指向多次make clean,它并没有出错

在这里插入图片描述

到这里一个可能想到是.PHONY的作用了;那这到底是什么呢?又为什么被称为伪目标呢?

伪目标:.PHONY修饰,伪目标它总是被执行的;像clean这种的目标文件,一般都设置成伪目标

在这里插入图片描述

对于这个,可以解释为:

伪目标clean不依赖于任何文件,执行make clean生成clean目标文件就会执行依赖方法rm -f code;但是依赖方法并没有生成clean文件,而是删除code文件,做清理工作。

这里虽然说clean是一个目标文件,但在依赖方法中并没有生成clean文件。(也许是因为没有生成clean目标文件,才称为伪目标

3. ACM时间

对于上述的伪目标能够一直被执行,而其他就不能够连续执行;又是如何实现的呢?

先来了解什么是ACM时间

  • Access :文件最近访问的时间
  • Modify :文件内容最近修改的时间
  • Change :文件属性最近修改的时间

那我们如果想要看一个文件的ACM时间,就要用到新的指令stat了。

在这里插入图片描述

那又是根据哪个时间来判断是否执行呢? 答案是Modify文件内容修改时间

在这里插入图片描述

我们可以看到上图中目标文件Modify时间是晚于依赖文件的,所以就不能为执行。

仔细观察可以看出来,上图Change时间目标文件也晚于依赖文件啊,这样不能说明比较的是Modify时间啊

现在接着来看

在这里插入图片描述

我们可以看到,依赖文件code.cChange时间晚于目标文件codeChange时间还是不能够执行;

那现在我们就能够得出结论,只有当依赖文件的Modify时间晚于目标文件的Modify时间时(在编译过后,依赖文件内容有所修改),才能够被执行。

这里.PHONY之所以能一直被执行,其原因就是它忽视了对比时间。

4. 推导过程

在上述过程中,我们写的依赖文件都是在当前目录中直接存在的;拿如果依赖文件并没有直接存在,而是依赖于其他依赖文件呢?

这里就根据编译过程,生成编译过程中所有的临时文件,来写makefile

code:code.ogcc code.o -o code
code.o:code.sgcc -c code.s -o code.o
code.s:code.igcc -S code.i -o code.s
code.i:code.cgcc -E code.c -o code.i
.PHONY:clean
clean:rm -f *.i *.s *.o code

如上述,code依赖于code.ocode.o依赖于code.scode.s依赖于code.icode.i依赖于code.c。这些除了code.c以为的文件都没有直接存在于当前目录下。

在这里插入图片描述

这样依然可以运行,并且都生成了这些文件;这足以说明应该存在可以推导的过程。
在这里插入图片描述

扩展语法——更加通用的makefile

上面所描述的makefile它只能适用于应该文件,拿对于多个文件也是无济于事(还要重新写makefile

这就一点太麻烦了,现在就来学习更多makefile语法,让makefile变得更加适用。

1. 创建变量

makefile中也可以创建变量,和C语言中指针那样

BIN=code
SRC=cpde.c
OBJ=code.o
CC=gcc
RM=rm -f$(BIN):$(OBJ)gcc $(OBJ) -o $(BIN)
$(OBJ):$(SRC)gcc -c $(SRC) -o $(OBJ)
.PHONY:clean
clean:$(RM) $(OBJ) $(BIN)

上述中例如BIN=codeSRC=code.c这样的就是创建了变量,(它们就像`C语言中的指针);

在需要访问这些变量的值的时候需要用到$()(就比如$(BIN)访问BIN变量的值)
在这里插入图片描述

这里还有优化一下,使用$^$@

$^:在依赖方法中使用,指依赖关系中的依赖文件

$@:在依赖方法中使用,指依赖关系在的目标文件

所有就可以这样来写:

BIN=code
SRC=cpde.c
OBJ=code.o
CC=gcc
RM=rm -f$(BIN):$(OBJ)gcc $^ -o $@
$(OBJ):$(SRC)gcc -c $^ -o $@
.PHONY:clean
clean:$(RM) $(OBJ) $(BIN)

2. 编译当前目录下的多个文件

说了这么多,这还是只能编译一个文件,现在就来看如何编译当前目录下的多个文件

要想编译多个文件,那我们就不能给变量这样赋值了

SRC=code.c

这样就写死了,只能操作一个文件;现在我们需要的是,识别到当前目录下所以的.c文件

获取当前目录下所有的.c文件

方法有两种:

  • $(shell ls *.c) :在makefile中执行shell命令,获取当前目录所有的.c文件。
  • $(wildcard *.c) :使用wildcard函数,获取所有的.c文件。

将所有的.c修改成.o

这里,我们并不是直接生成code可执行程序的,而是生成对应的.o文件,再链接形成的可执行程序;所以我们就需要获取所有.c文件对于的.o文件名;(使用makefile语法即可)

OBJ=$(SRC:.c=.o)

这个语法就是将SRC(获取当前目录下所有.c文件)中的.c替换成.o;这样我们就获取到了所有.c文件对应的.o文件名。

通配符%和逐个执行$<

上面经常写到*.c,它就表示所有以.c结尾的文件;那在写makefile的使用,有时候也要用到通配符,但是我们没有使用*,而是使用%

我们想要让它编译多个文件就不能像之前那样写了,(因为这里我们要生成所有.c文件对应的.o文件。

这样我们在写由.c生成.o文件时,就能直接使用通配符%,这样就可以自动匹配

在匹配结束之后,目标文件依赖多个文件;就不能使用$^直接取依赖文件列表了,而是使用%<将依赖文件列表中多个文件一个一个执行。

到这里,我们就能基本完成编译当前目录下所有.c文件的makefile了。

BIN=code
SRC=cpde.c
OBJ=code.o
CC=gcc
RM=rm -f$(BIN):$(OBJ)gcc $^ -o $@
$(OBJ):$(SRC)gcc -c $^ -o $@
.PHONY:clean
clean:$(RM) $(OBJ) $(BIN)

3. 测试和优化makefile

对于上述的makefile,我们进行一些优化

BIN=code
SRC=cpde.c
OBJ=code.o
CC=gcc
RM=rm -f
COM=-c
LINK=-o$(BIN):$(OBJ)$(CC) $^ $(LINK) $@
%.o:%.c$(CC) $(COM) $<
.PHONY:clean
clean:$(RM) $(OBJ) $(BIN)

这里优化,新增了COM编译选项、LINK链接选项;并且都使用变量,更将通用。

在进行测试之前,我们要先让当前目录有多个文件,这里介绍应该语法

touch code{1..20}.c

该指令作用是创建code1.c、code2.c、code3.c......code20.c(20个文件)。

现在该目录下新建了20个文件,运行一试

在这里插入图片描述

可以看到,它会显示出来编译的过程,如果不想要看到可以在语句前加上@

BIN=code
SRC=cpde.c
OBJ=code.o
CC=gcc
RM=rm -f
COM=-c
LINK=-o$(BIN):$(OBJ)@$(CC) $^ $(LINK) $@
%.o:%.c@$(CC) $(COM) $<
.PHONY:clean
clean:$(RM) $(OBJ) $(BIN)

在这里插入图片描述

这样我们不知道它编译的进度了,我们就可以手动回显一些内容(在依赖关系中直接添加输出到显示器文件的语句即可)

BIN=code
SRC=cpde.c
OBJ=code.o
CC=gcc
RM=rm -f
COM=-c
LINK=-o$(BIN):$(OBJ)@$(CC) $^ $(LINK) $@@echo "link...$^"
%.o:%.c@$(CC) $(COM) $<@echo "compile...$^"
.PHONY:clean
clean:$(RM) $(OBJ) $(BIN)

注意:这里在echo语句前也要加@不让它回显,否则就会回显出来echo语句的内容。

执行结果展示

在这里插入图片描述

到这里makefile部分就结束了,后续有要深入了解的语法知识,在后续过程在继续学习。

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws


http://www.ppmy.cn/devtools/145966.html

相关文章

前端真实面试题自用

一、写在前面 笔者&#xff0c;经过计算机学硕考研的失败后&#xff0c;想谋求一份前端工作实在是太难了。一方面&#xff0c;确实曾经学习过的东西很久没有拾起&#xff0c;另一方面&#xff0c;对于前端面经还是记忆不深刻&#xff0c;特地写此贴记录笔者在真实前端面试中遇…

Elasticsearch相关知识@1

目录标题 Lucene1. **什么是 Lucene?**2. **Lucene 在 Elasticsearch 中的作用**3. **Lucene 的核心功能**(1) **倒排索引**(2) **分词**(3) **查询解析**(4) **相关性评分** 4. **为什么 Elasticsearch 使用 Lucene?**5. **Lucene 和 Elasticsearch 的区别**6. **总结** 分片…

云数智融合体系建设实践——以工行软件开发中心为例

随着“云计算第三次浪潮”的涌动&#xff0c;业界正见证着一场围绕“算力”的结构性变革。云计算、大数据、人工智能三大核心技术的深度融合&#xff0c;正推动着算力基础设施的快速发展&#xff0c;实现算力的高效调度与利用&#xff0c;也标志着业界对云计算体系布局的全新理…

Redis篇--应用篇1--会话存储(session共享)

1、概述 实现Session共享是构建分布式Web应用时的一个重要需求&#xff0c;尤其是在水平扩展和高可用性要求较高的场景下。 在分布式服务或集群服务中往往会出现这样一个问题&#xff1a;用户登录A服务后可以正常访问A服务中的接口。但是我们知道&#xff0c;分布式服务通常都…

SpringAI人工智能开发框架005---SpringAI文本转语音_语音转文本_音频翻译程序接口编写_英文音频翻译_中文音频翻译_指定模型

可以看到SpringAi中关于音频的API帮助文档可以去看一下. 可以看到帮助文档. 这部分功能就是把声音变成文本,以及把文本变成声音. 去创建一个项目 然后修改一下,仓库,引入 sring ai的仓库 然后指定一下版本,这里要用java 17的版本. 然后这里用的api-key 这个key, 这里配置到…

自动化测试框架playwright 常见问题和解决方案

自动化课程已经讲完了playwright框架&#xff0c;很多同学跃跃欲试&#xff0c;所谓实践出真知&#xff0c;这不在实践中就要到了一些问题&#xff0c;小编也给大家整理出来了&#xff0c;送个有需要的同学&#xff0c;记得点赞收藏哦~~ 01安装问题 问题描述&#xff1a; 在安…

自动屏蔽频繁访问IP,提升服务器安全:实战脚本解析

在当前的数字化时代&#xff0c;服务器安全成为了一个不可忽视的重要环节。无论是企业网站还是个人博客&#xff0c;都面临着来自各方的潜在威胁。其中&#xff0c;恶意访问和频繁登录尝试更是让管理员头疼不已。本文将为大家介绍如何通过脚本自动屏蔽频繁访问和尝试SSH登录的I…

QT调用Sqlite数据库

QT设计UI界面&#xff0c;后台访问数据库&#xff0c;实现数据库数据的增删改查。 零售商店系统 数据库表&#xff1a; 分别是顾客表&#xff0c;订单详情表&#xff0c;订单表&#xff0c;商品表 表内字段详情如下&#xff1a; 在QT的Pro文件中添加sql&#xff0c;然后添加头…