【Linux】项目自动化构建工具make/makefile

news2024/11/27 15:47:25

🏖️作者:@malloc不出对象
⛺专栏:Linux的学习之路
👦个人简介:一名双非本科院校大二在读的科班编程菜鸟,努力编程只为赶上各位大佬的步伐🙈🙈
在这里插入图片描述

目录

    • 前言
      • 一、make/makefile的背景
      • 二、makefile的基本结构
      • 三、项目清理
      • 四、makefile的依赖
      • 五、如何快速编写大型项目中的Makefile文件


前言

本篇文章我们将要讲解的是项目自动化构建工具make与makefile。

一、make/makefile的背景

会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力,一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。makefile带来的好处就是——“自动化编译”,一旦写好只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。

在Linux下,make命令主要用于自动化构建(build)软件项目,它能够根据程序员编写的Makefile文件中的指令,自动编译、链接、打包和安装软件。

具体而言,make完成以下工作:

1.根据Makefile文件中的规则,检查每个源文件的修改时间和依赖关系,确定哪些文件需要重新编译。
2.编译源文件,生成目标文件。
3.链接目标文件,生成可执行文件或库文件。
4.打包可执行文件或库文件,以便于分发和安装。
5.安装可执行文件或库文件到指定的目录中。

通过使用make命令,程序员可以更加方便和高效地管理项目中的代码编译、构建和部署等任务。

Makefile是一个文本文件,其中包含了一系列规则和指令,用于告诉make命令如何构建(build)软件项目。
具体而言,Makefile完成以下工作:

1.定义变量:可以定义一些变量,用于存储常量或者目录路径等信息。
2.定义规则:规则指定了如何生成一个或多个目标文件。每个规则包含了目标文件、依赖文件和生成命令。如果目标文件需要更新,make会根据规则自动执行生成命令。
3.定义伪目标:伪目标是没有实际文件对应的目标,只是一个执行动作的标签。伪目标可以用来执行清理操作、运行测试脚本等操作。
4.定义命令:命令是指make需要执行的操作。可以是编译源代码、链接目标文件、打包可执行文件等操作。
5.定义依赖关系:依赖关系指明哪些文件是构建目标文件所必需的。当依赖文件被修改时,make会根据依赖关系重新构建目标文件。

通过编写Makefile文件,程序员可以实现对项目的自动化构建,提高项目构建的效率和可靠性。

注:make是一个指令,makefile是一个文件!!


二、makefile的基本结构

makefile是一个围绕依赖关系和依赖方法构建的一个自动化编译的工具,我们想完成一件事,必须有正确的依赖关系和正确的依赖方法。

我们先来看看它的基本结构:

在这里插入图片描述

注意:依赖方法必须以tab(tab长度不定,我们一般tab以4字符为一个间隔)开头,这是语法规定!!

如果我们使用空格的话会出现下面的情况:

在这里插入图片描述
在这里插入图片描述

我们可以看到不符合Makefile规则是不能完成构建的。另外细心的读者也可以发现如果使用tab符合Makefile规则的话是会颜色高亮的!!


下面我们来简单使用make演示一下:

在这里插入图片描述

看了上图那么我有一个问题,为什么直接使用make就执行了第一个目标文件的依赖方法??而第二个目标文件clean却要显式的使用make clean?

这是因为编译器会默认从上往下扫描,找到文件中的第一个目标文件,在上面的例子中,他会找到“myfile”这个目标文件,然后根据依赖方法得到可执行程序!!而其他的目标文件则需要指令文件名才能进行编译了。

下面我们来验证一下,这次我将clean目标文件放在最前面:

在这里插入图片描述

我们发现此时make执行的就是第一个目标文件的依赖方法了,而myfile需要make指明构建:

在这里插入图片描述

回到之前的例子,我们来看看下面出现的现象:

在这里插入图片描述

为什么make只能进行一次,然后就提示myfile这个可执行程序已经是最新的了,不需要重新编译了,而make clean却能执行多次??

这是因为clean的依赖文件列表为空,则意味着该目标文件不依赖于任何文件。这意味着在执行该目标时,不需要检查其依赖项是否已经更新,因为没有依赖项需要检查;而myfile可执行程序它其实是需要根据依赖文件来判断是否需要重新编译的!!

Q:那么请问gcc是如何根据依赖文件来判断是否需要重新编译?

实际上gcc是根据依赖文件修改的时间来判断是否需要重新编译的,我们的源文件(依赖文件)对应一个时间戳,我们的目标文件也有一个时间戳,我们知道源文件(依赖文件)的创建时间一定比目标文件要早,这样才能形成目标文件对吧;而在源文件(依赖文件)进行修改之后它的时间戳就进行了更新,此时
源文件(依赖文件)的时间戳比目标文件新,那么Make工具就会重新构建这个目标文件。

如下图我简单的展示一下:

在这里插入图片描述

下面我们来进行实验一下:

在这里插入图片描述

修改依赖文件(源文件)之后:

在这里插入图片描述

Q:那么有没有办法不修改源文件也可以使目标文件能够重新构建呢?

在这里插入图片描述

我们可以使用touch指令将依赖文件(源文件)更新到最新的时间戳,那么此时我们的依赖文件(源文件)比目标文件的时间戳更新,所以我们就能重新进行编译了!!!

总结:如果这个目标文件不存在或者它的时间戳比依赖文件的时间戳更旧,那么 Make 工具会重新构建这个目标文件;否则,它会认为这个目标文件已经是最新的,不需要重新构建。

三、项目清理

工程是需要被清理的,像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过我们可以显式要make执行。即命令——“make clean”,以此来清除所有的目标文件,以便重新编译。但是一般我们这种clean的目标文件,我们将它设置为伪目标,用 .PHONY 修饰,伪目标表示一个“虚拟目标”,它并不对应于任何实际的文件,而是表示一些需要执行的命令。当执行伪目标时,Make 不会检查它是否存在于文件系统中,而是仅仅执行对应的命令。我们记住它最重要的特性——总是执行的。

我们来看看它的使用方式:

在这里插入图片描述

我们来看看现象:

在这里插入图片描述

我们发现在clean被.PHONY修饰之后能执行多次,那之前我们写的clean依赖的文件列表为空不是也能完成这个任务吗?它们之间一样吗?

首先目标文件依赖的文件为空和.PHONY修饰的伪目标是两个不同的概念。

目标文件依赖的文件为空:

  • 当一个目标文件依赖的文件为空时,make 不会检查这个依赖项是否存在或是否被更新。这通常用于创建某个目标文件,但其依赖项是通过其他方式处理或生成的情况。
  • .PHONY修饰的伪目标:.PHONY 是一个特殊的目标,用于声明一些伪目标。伪目标不是实际的文件名,而是 makefile 中定义的一组操作。.PHONY 目标声明的目标通常不需要实际的文件作为依赖项。当你运行 make 时,它将忽略这些目标是否存在或是否被更新,而是直接执行定义的操作。

总的来说,目标文件依赖的文件为空适用于依赖项是通过其他方式生成的情况,而.PHONY适用于声明一组操作而不需要实际文件作为依赖项的情况。

Q:"依赖项通过其他方式生成"是什么意思呢?

“依赖项通过其他方式生成” 的意思是在构建某个目标时,它的某个依赖项并不是一个实际的文件,而是通过其他方式生成的,比如:

1.通过命令行运行一些程序生成的结果。
2.通过其他的 Makefile 规则生成的结果。
3.通过复制或者下载远程文件得到的结果。

在这些情况下,依赖项并不是一个实际的文件,因此无法使用文件的时间戳来判断它是否需要重新构建。相反,你需要手动定义生成依赖项的规则,以确保它们能够在构建目标之前正确地生成。

我们来看看具体的例子:

在这里插入图片描述

在这里插入图片描述

上述例子我们通过clean目标文件创建了一个新的文件touch-file,我们还可以进行一些命令行的其他指令等,依赖项通过其他方式生成还有其他的情况,这里我就不一一进行讲解了,感兴趣的读者下来可以试试。


利用.PHONY修饰目标文件,我们也可以重新编译之前的目标文件了,我们一起来看看:

在这里插入图片描述

在这里插入图片描述

.PHONY修饰目标文件后忽略目标是否存在或是否被更新,直接执行定义的操作,只会更新目标文件的时间戳!!

在这里插入图片描述

虽然我们达到了重新编译的目的,但其实我们是非常不推荐这种行为的,因为重新编译需要消耗大量的时间(大型工程中),而且我们的文件内容并没有进行修改,重新编译也没什么必要!!

四、makefile的依赖

我们来看看下面这个例子,它很好的体现了makefile的依赖关系:

在这里插入图片描述

在这里插入图片描述

我们平时还是不建议这些写哈,因为没什么必要我们只要得到目标文件就可以了。

五、如何快速编写大型项目中的Makefile文件

我们在前面编写的Makefile文件都只涉及一个源文件的编译链接,但是如果在一个大型项目中我们有很多的源文件需要被合并链接,难道我们一个个的手动去添加吗??

在这里插入图片描述

既然有这种问题的出现,那么必然会有对应的解决办法。对于大型工程,我们一般采用模块化的方式组织代码,每个模块对应一个或多个源文件。为了避免将全部源文件写出来进行链接,可以使用通配符或变量来自动化处理源文件!!!

以下是一个示例 Makefile,演示如何使用通配符和变量来构建一个大型工程:

# 编译器
CC = gcc 

# 列出Makefile所在目录中所有的.c文件
SOURCES := $(wildcard *.c)              

# 列出SOURCES中所有.c文件对应的.o文件  
OBJECTS := $(patsubst %.c,%.o,$(SOURCES)) 

# 最后生成的目标文件
TARGET = myfile              

 # 目标文件依赖OBJECTS中的所有.o文件,变量使用时需要在变量名前加上"$"符号,并且最好用小括号或者大括号把变量括起来             
$(TARGET): $(OBJECTS)      
	$(CC) $^ -o $@           # 所有依赖目标集$^利用CC编译器,一个一个编译成目标文件集$@    
%.o: %.c                     # 所有的.o目标文件依赖于对应的.c文件
	$(CC) -c $< -o $@        # 所有的.c文件一个一个的取出来,利用CC编译器形成同名对应的.o文件

# 清理所有的.o文件以及目标文件   
.PHONY:clean
clean:
    rm -rf $(OBJECTS) $(TARGET) 

我们的源文件如下图:

在这里插入图片描述

Makefile中的内容:

在这里插入图片描述

上述图中的CC、SOURCES、OBJECTS以及TARGET都是变量,在Makefile中变量其实也就是C/C++中的宏!!!

在OBJECTS变量中我们使用了一个patsubst模式字符串替换函数。

使用格式:$(patsubst \<pattern>,\<replacement>,\<text>)
返回:函数返回被替换过后的字符串。
功能:查找<text> 中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式 <pattern> ,如果匹配的话,则以 替换。这里, <pattern> 可以包括通配符 % ,表示任意长度的字串。如果<replacement> 中也包含 % ,那么,<replacement> 中的这个 % 将是 <pattern> 中的那个 % 所代表的字串。(可以用 \ 来转义,以 % 来表示真实含义的 % 字符)

注: \$(objects:.o=.c)$(patsubst \%.o,\%.c,\$(objects)) 是一样的,大家下来可以试试。


另外我们上述看到的\$^$@\$< 都被叫做自动化变量!!
所谓自动化变量,就是这种变量会把模式中所定义的一系列的文件自动地挨个取出,直至所有的符合模式的文件都取完了。这种自动化变量只应出现在规则的命令中。

下面是所有的自动化变量及其说明:

$@ : 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么, $@ 就是匹配于目标中模式定义的集合。
$% : 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是 foo.a(bar.o) ,那么, $% 就是 bar.o , $@ 就是 foo.a 。如果目标不是函数库文件(Unix下是 .a ,Windows下是 .lib ),那么,其值为空。
$< : 依赖目标中的第一个目标名字。如果依赖目标是以模式(即 % )定义的,那么 $< 将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
$? : 所有比目标新的依赖目标的集合。以空格分隔。
$^ : 所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那么这个变量会去除重复的依赖目标,只保留一份。
$+ : 这个变量很像 $^ ,也是所有依赖目标的集合。只是它不去除重复的依赖目标。
$* :这个变量表示目标模式中 % 及其之前的部分。如果目标是 dir/a.foo.b ,并且目标的模式是 a.%.b ,那么, $* 的值就是 dir/foo 。这个变量对于构造有关联的文件名是比较有效。如果目标中没有模式的定义,那么 $* 也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么 $* 就是除了后缀的那一部分。例如:如果目标是 foo.c ,因为 .c 是make所能识别的后缀名,所以, $* 的值就是 foo 。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用 $* ,除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么 $* 就是空值。

通过上述使用变量以及通配符我们已经配置好了一个简单的自动化构建的Makefile文件,下面我们来检测一下:

在这里插入图片描述

通过这种方式,我们就实现了自动化处理大量的源文件,避免了手动编译的繁琐。


本篇文章的Makefile就讲到这里了,它是我们开发项目中非常重要的一个工具,它非常的灵活。一个Makefile写的好不好决定了你的工作效率;另外,其实Makefile其实还有很多功能在这篇文章中并未进行展示,因为博主的精力和水平还不够就讲了大致的一部分的,如果有读者感兴趣的话可以拜读一下这位巨佬的博客分享哦orz~🙈🙈

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/513169.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

00后卷起来,真没我们老油条什么事了···

都说00后躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。 这不&#xff0c;前段时间我们公司来了个00后&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪20K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。…

Android 中你碰不到但是很重要的类之ActivityThread

作者&#xff1a;Drummor 通过本文能了解一下内容 1、和系统进程打交道的桥头堡 应用进程起来之后ART(Android Runtime)第一站就是ActivityThread&#xff0c;代码层面上就是ActivityThread的main()方法&#xff0c;是不是很熟悉&#xff0c;爷青回啊&#xff0c;这不就是java…

基于深度学习的动物识别系统的实现

项目介绍 动物识别系统&#xff0c;使用Python作为主要开发语言&#xff0c;基于深度学习TensorFlow框架&#xff0c;搭建卷积神经网络算法。并通过对18种动物数据集进行训练&#xff0c;最后得到一个识别精度较高的模型。并基于Django框架&#xff0c;开发网页端操作平台&…

数据分析师 ---- SQL强化(3)

数据分析师 ---- SQL强化(3) 题目&#xff1a;每个月Top3的周杰伦歌曲 从听歌流水中找到18-25岁用户在2022年每个月播放次数top 3的周杰伦的歌曲 输入例子&#xff1a; drop table if exists play_log; create table play_log (fdate date,user_id int,song_id int ); inser…

前端的培训计划书

文章目录 导文模板一一、前言二、培训目标三、培训内容和计划 模板二模板三 导文 这里是导文 模板一 一、前言 随着互联网的快速发展&#xff0c;前端开发已经成为了现代软件开发中一个不可或缺的重要技能。本次培训旨在帮助学员快速掌握前端开发的核心知识和技能&#xff0c…

ChatGPT实现撰写邮件

撰写邮件 电子邮件是日常工作中很常用的工具&#xff0c;在相对正式的场合&#xff0c;一封格式美观、用语典雅的电子邮件正文会起到很好的作用。ChatGPT 可以较好的完成电子邮件的编写和格式美化工作。 下面让我们以产品销售的角度&#xff0c;写一封推销邮件。假定产品名称…

String类 [中]

目录 一、 string 的深浅拷贝 0x00 构造函数与析构函数的实现 0x01 拷贝构造 0x02 赋值 0x03 整体代码 二、 string的实现 0x01 引入 0x02 c_str 0x03 默认构造函数 三、size()与operator[]的实现 0x01 size()的实现 0x02 operator[]的实现 0x03 遍历实现 四、迭代器…

洛谷B2098 整数去重

整数去重 题目描述 给定含有 n n n 个整数的序列&#xff0c;要求对这个序列进行去重操作。所谓去重&#xff0c;是指对这个序列中每个重复出现的数&#xff0c;只保留该数第一次出现的位置&#xff0c;删除其余位置。 输入格式 输入包含两行&#xff1a; 第一行包含一个…

获取两个日期间时长 (XX天XX时XX分)

使用场景&#xff1a; 发货日期与到货日期 计算运输时长 代码&#xff1a; private String getMinuteTime(String startTime, String endTime) {String minuteTime null;if (StrUtil.isNotBlank(startTime) && StrUtil.isNotBlank(endTime)) {long minute DateUti…

【芯片设计- RTL 数字逻辑设计入门 2 - vcs 及 verdi 使用介绍】

文章目录 1.1 VCS 编译环境1.1.1 Complie Design1.1.2 simv 仿真 1.2 VCS 波形生成及查看1.2.1 verdi 命令介绍1.2.2 verdi 波形查看 1.1 VCS 编译环境 VCS 全称是 Verilog Compiler Simulator&#xff0c;是 Synopsys 公司的&#xff0c;类似于windows环境下的 questasim 或 …

Flink sql

1.创建表的执行环境 第一种 StreamExecutionEnvironment env StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(1); SingleOutputStreamOperator<Event> streamOperator env.addSource(new ClickSource()).assignTimestampsAndWatermarks(W…

深入理解C语言指针

目录 一、指针基础知识 二、野指针 三、指针运算 四、二级指针 五、指针数组与数组指针 六、函数指针与函数指针数组 一、指针基础知识 什么是指针&#xff1f; 指针其实就是个用来存放地址的变量&#xff0c;定义为type *。 指针大小&#xff1f; 32位平台(32个地…

【大数据-调度工具】dolphinscheduler安装和遇到的问题

1.安装 安装步骤按照官网安装即可 官网&#xff1a;DolphinScheduler | 文档中心 (apache.org) 版本&#xff1a;3.1.5 2.踩坑记录 Q1.大文件无法上传 问题描述&#xff1a; 在资源中心中上传文件选择完大文件夹之后&#xff0c;选择确认之后确认按钮转了几圈圈之后就没…

[Element]调整select样式

通过伪元素&#xff0c;实现这个和step长得差不多的样式 <template><el-selectv-model"value"placeholder"请选择提报单位"style"width: 430px"><el-optionv-for"(item, i) in officeList":class"el-option get…

hive的基本操作语句

背景&#xff1a;记录一下hive创建数据库&#xff0c;建表&#xff0c;添加数据&#xff0c;创建分区等的语句吧&#xff0c;省得总百度&#xff0c;&#x1f604; 第一步&#xff1a;hive的建库语句 create database pdata_dynamic;查看是否创建成功了 show databases;显示如…

Jenkins入门教程

一、开始使用 Jenkins 本导读将向您介绍使用 Jenkins、Jenkins 的主要特性和 Jenkins Pipeline 的基本知识。 本导读使用“独立”的 Jenkins 发行版&#xff0c;它可以在您自己本地的机器上运行。 准备工作 第一次使用 Jenkins&#xff0c;您需要&#xff1a; 机器要求&…

从入门到实践:计算机视觉图像分类完全指南

计算机视觉图像分类是计算机视觉领域的一个重要分支&#xff0c;它的目标是让计算机能够识别并分类不同的图像。在本文中&#xff0c;我们将介绍计算机视觉图像分类的基本概念、流程和常用算法。 一、图像分类的基本概念 图像分类是指将输入的图像自动分类到预定义的一组类别中…

量子计算+真实材料模拟!美国埃姆斯国家实验室科学家获得新突破

​ 噪声量子比特ADAPT仿真&#xff08;图片来源&#xff1a;网络&#xff09; 量子计算机的潜在功能远远超出当今的经典计算机&#xff0c;来自美国能源部埃姆斯国家实验室的科学家们展示了一种在材料研究中利用量子计算的新方法&#xff0c;通过模拟材料的自适应算法&#xf…

自参考和对比学习正则化的Few-shot医学图像分割

文章目录 Few-shot Medical Image Segmentation Regularized with Self-reference and Contrastive Learning摘要本文方法Local Prototype-Based SegmentationSelf-reference RegularizationContrastive LearningSuperpixel-Based Self-supervised Learning 实验结果 Few-shot …

分布式搜索引擎2——深入elasticsearch

数据聚合 聚合的分类 聚合(aggregations)可以实现对文档数据的统计、分析、运算。聚合常见的有三类: 桶(Bucket)聚合:用来对文档做分组 TermAggregation:按照文档字段值分组Date Histogram:按照日期阶梯分组&#xff0c;例如一周为一组&#xff0c;或者一月为一组 度量&…