Linux - make命令 和 makefile
make命令和 makefile
如果之前用过 vim 的话,应该会对 vim 又爱又恨吧,刚开始使用感觉非常的别扭,因为这种编写代码的方式,和在 windows 当中用图形化界面的方式编写代码的方式差别是不是很大。当你把vim 用熟悉的之后,虽然没有刚开始那么别扭了,但是当你在编写 一些大型项目的时候,vim 怎么用都不习惯。
而 makefile,就是一个可以定义一系列规则,来指定那个文件先编译,那个文件后编译,哪些文件需要重新编译,甚至进行更复杂的操作。
makefile 相对与 vim来说,他可以实现--“自动化编译”,一旦把 makefile 当中的各个文件的执行过程写好只好,只需要一个 make 命令,就可以让 由makefile 实现的各个文件的编译顺序 的整个工程 进行 完全自动的编译,这极大的提高了软件开发的效率。
makefile 编写链接关系 和 链接方法
首先要明确的是, make 是一个命名,它会默认在 本目录下寻找 名字叫做 "makefile" 或者 "Makefile" 也就是说,首字母大不大写其实是无所谓的。
一般情况下,makefile是创建在 源文件的 同级目录(当前目录)下创建的,我们用一个小例子来看 makefile和 make 的使用过程:
我们新建立一个文件夹(mk),在这个文件夹当中进行操作:
此时这个文件夹当中什么都没有,是空的,我们创建一个 text.c 文件:
然后 在其中简单些一些代码:
此时,就有了源代码文件,然后,我们建立的 makefile 文件要和 这个 源代码文件(text.c)文件在同级目录下:
然后我们使用 vim 打开编译 makefile 文件,在 makefile 文件当中进行以下书写:
在 " : " 的右边是 与 makefile同级目录之下的某一个 源代码文件;而在 " : " 的左边 是由右边的源代码文件 即将 生成的可执行文件(这个在外部使用make 名字之后才会生成)。
由上述过程,我们就编译好了, text.c 这个源代码文件 由 makefile 生成可执行文件的过程。
此时我们只需要在 makefile 的同级目录下,使用 make 命令就可以在 同级目录之下生成一个 text 的可执行文件:
由上述结果可知,使用 make 之后,还会再屏幕上打印 编译过程。
我们把上述的 用 " : " 把源文件 和 对应可执行文件连接在一起的关系,叫做链接关系。关于链接关系我们下述在具体说明。
但是,上述我们只是写了 一个 链接关系,并没有指定这个源代码要使用什么样的编译方式,我们可以在 " : " 说明之后,在下面把 编译规则写上:
向上述就表示买吧 text.c 源代码文件 以 gcc 的方式编译成 text 可执行文件。我们把这个中方式叫做依赖方法。
注意:此处的依赖方法我们可以看到前面是 空格开头的,这里不能是空格分割,必须使用 tab 键进行分割:
往后,我们先要编译 text.c 这个源代码文件的时候,就可以不用再使用 gcc 命令来编译这个文件了,直接在 上述同级目录之下,使用 make 命令就可以直接生成 text 可执行文件。(具体方式和上述演示是一样的,这里就不演示了)
makefile 编写 删除源文件 规则
当我们不想使用上述的 text 这个可执行文件时,我们当然可以 用 rm 删掉 这个 text 可执行文件,但是,用 rm 删除文件的风险太大,很容易写错文件名,而导致删错文件,要知道,在 linux 当中可是没有 像 windows 当中的 回收站一样的东西,在linux 当中删除某一个文件就是真的删除掉了。
所以,在 makefile 当中不能只提供 编写 源代码文件 链接关系 和 链接方法,还应该提供 删除的方式:
我们可以在 其中写一个 类型 make 的命名,向上述我就写了一个 make clean 的命令(这个命令的名字是自定义的),然后在后边写上 删除 text 这个可执行文件:
在使用 make clean 这个命令之后,在目录当中就没有 text 可执行文件了。
依赖关系 和 依赖方法
如上所示:text:text.c 是一个依赖关系, gcc -o text text.c 是一个依赖方法,那么这里的 依赖关系 和 依赖方法是什么呢?
我们举一个例子:小明 和 他的爸爸,是一种依赖关系,当小明向他的爸爸要零花钱的时候,小明说:“爸,给我打100 块钱”,类似的话语,那么 小明 只有管他的爸爸叫爸,这是一种依赖关系,如果他问 他的室友的 爸爸,给他打钱,小明 和 他室友的爸爸 没有依赖关系,那么 室友的 爸爸为什么要给 小明打钱呢?
只有 小明 和 他父亲,是一种依赖关系,所以 ,小明 的爸爸 才会给他打钱;
但是,确定依赖关系之后,已经确定是要打钱的了,但是,如何打钱,微信还是支付宝,还是 打 银行卡,就要 有人去确定,所以这时候 就有了打钱的方法,也就是依赖方法。
那么,换到 makefile 当中也是一样的,text 和 text.c 是一种依赖关系,只有通过 text.c 才能生成 text 可执行文件,对于如何生成可执行文件,如上述:使用 gcc -o 的方式生成可执行文件。
当前的 text 依赖的是 text.c 这个一个源文件来生成的,如果有 两个,那么后面就跟两个,三个就跟三个,是依靠 这 text.c 一个源文件 生成的 text 可执行文件(目标文件)。所以,就告诉 makefile, 目标文件是 text,这个text 依赖的文件是 text.c。但是光有依赖关系是不够的,makefile还需要知道如何根据 text.c 文件生成 text 目标文件,他也是需要知道的,所以,才有了 依赖方法。
关于make 指令
上述是有 text.c 文件一步到位生成的 text 可执行文件,我们还可以写得更复杂一点,把 c 源代码文件 生成可执行文件的整个过程都是 意义列举出来:
此时 make 命令打印:
而且,发现,此时在文件夹当中不止有 text 可执行文件了,还有 上述生成的所有的中间文件:
其实是因为,在上述当中,我们要想 从 text.o 文件 生成 text 可执行文件,makefile 就要从当前目录之下来寻找 text.o 文件,但是此时目录当中是没有 text.o 文件的,所以他就会接着走链接关系,发现 text.o 是由 text.s 生成的,但是此时目录当中还是有 text.s,他又会继续寻找链接关系,发现是 text.i 生成的 text.s········一次类推。
最后找到 text.c ,生成了text.i ,然后逆向的一直生成到 最终的 目标文件 text 可执行文件。
也就是说,makefile 的最终目的是要生成 目标文件,而 目标文件的依赖关系 的 源文件,他会先去看目录当中有没有,有就直接生成,没有的话,就会去寻找 这个号源文件的依赖关系,然后递归式的 去生成 目标文件的 依赖文件。
这个特征(过程),就特别像一个函数递归的过程。而上述的目标文件就是这个递归的 出口(结束条件),整个的结构,在保存这些依赖关系的时候,是一种栈式的结构。
而且,上述过程,不是需要按照 顺序来 书写 链接关系的,就算是乱序,都是可以 编译出来的:
make 指令:
当然,虽然能 打乱顺序,但是不能缺胳膊少腿,一个工程当中的文件编译顺序,缺一个他肯定是不能自动推导的:
输出:
总结:make 命令 可以自动推导 makefile 的链接关系,就算其中的顺序是打乱的,只要存在,就会自动推导。其中的依赖关系是利用栈式的结构进行存储的。
在上述你也发现了,我们的 clean 指令,写的是删除了4 个文件,当我们在外部输入 make clean 的时候,这个 4 个文件就都会删除,非常的方便:
注意:make 的默认动作是 ,把 makefile 当中的第一个目标文件作为 make 的默认动作;
比如上述,把 clean 放到 text 目标文件的第一行的话,那么我们使用 make 指令是不会执行 text 的,而是执行 clean:
使用 make 指令:
如上述所示,我们直接使用 make 指令,直接执行的是 clean 指令,而不是 生成 text 可执行文件。
而 make + 目标文件名(如上述的 make clean),就是执行 在makefile当中的 目标文件。
在 本目录下 有 make + 目标文件 的 目标文件时,就不能再次 make 了:
发现,当目录当中没有目标文件的时候,可以用 make 编译,但是当有目标文件的时候,就不能再次编译了,但是,当上述情况发生之后,我们又去修改了 text.c 的代码,发现make 有可以进行编译了:
因为,如果在第一次编译之后,源代码没有进行修改,那么是没有必要进行再次编译的(提升效率)。
我们在 ,windows 当中编写代码的时候,有时候报错了,但是我们已经进行修改了,但是修改之后还是报错,当我们重新生成解决方案之后,程序又好了,在期间我们没有修改过代码。其实原因就和上述是一样的,有些编译器会帮我们去 辨别 当前代码有没有被修改过,如果没有,那么也就没有再次编译的必要了,但是有的时候,可能编译器判断不是很灵敏,需要我们手动编译一下。
那,上述的 不给继续编译,和 判断源代码文件是否进行修改是如何做到呢?
我们都知道,可执行文件 是 在源文件的基础之上生成的,先有源文件,才有可执行文件,所以,一般而言,源文件的最近一次修改时间 是要比 可执行文件要早的。
如果,我们修改了源文件,此时在目录当中还是有 可执行文件的,那么源文件的实现一定要比可执行文件的晚。
所以,只需要比较 可执行文件 的最近修改时间 和 源文件的最近修改时间:
- 如果.exe 新于 .c 说明源文件是老的,不需要重新编译。
- 如果.exe 于旧 .c 说明源文件是新的,需要重新编译。
对于 可执行 文件 和 源文件的 修改时间,一般是不会一样的,除非是用一些修改文件时间的命令。
stat命令(验证make 利用 文件时间 差异 实现 新老文件判断)(文件的三个时间)
stat命令 用于访问某一个文件的 时间问题,那么,可以使用 stat命令 来查看 源文件和 可执行文件的一些时间:
- 最近访问时间就是访问了这个文件的最近一次时间,比如 cat ,vim 都算是访问了这个文件。不管是你要 查看 还是 修改这个文件当中的内容,都是要访问这个文件的,所以 最近访问时间,几乎是 你 对这个文件任何操作的 最近时间。这个 时间 更改的频率是非常高的。
- 文件内容修改时间,就是文件当中存储的内容,最近一次的修改时间。
- 文件属性改变时间,就是文件当中存储的文件的属性,最近一次的修改时间。
上述的三个时间可能不是割裂的,比如 修改了文件内容,不仅是 Modify 会改变,可能 Access 也会改变,系统可能判定为当前一次操作就是 一个 文件的访问操作,而且修改文件的内容 可能 会修改到 文件属性,所以,都不是割裂的。
现在,我们修改一下文件的属性:
发现文件的 Change 时间已经被修改了。
此时 Modif 和 Access 都没有被修改,说明在当前系统下,认为 chmod 命令不算做是一次文件的访问。
其实在早期的linux 当中 access 在上述情况是要被修改的,但是也正是 Access 被修改的频率太高了,所以,如果有多个用户在修改访问这个文件,那么 Access 都要被修改,而 Access 被修改是要写进日志的,文件还是在磁盘上保存的,要修改 磁盘当中 Access 被修改的日志的话,就是要频繁的修改磁盘当中的内容 ,这效率就太低了。
所以,现在就减少了Access 修改的次数,来变相修改操作系统的效率。
如果实在想要 修改这个文件的当中的所有的时间,可以使用 touch (要修改的文件)文件名 来实现。touch + 目录当中不存在的文件名,就是创建一个文件,如果 touch + 目录当中已经有的文件,就是更新这个文件的 所以时间。
make 命令 就是把 两个文件 各种各样的时间,转化成时间戳,然后按照时间戳的大小,来比较谁新谁旧。
验证:
首先我们先用 make 编译一次,打印出 源文件时间 和 可执行文件的文件内容时间:
如上所示,我们进行第二次编译是不行的,此时 两个文件的时间如上所示:
现在,我们使用 touch 命令给 源文件更新时间,使得 源文件的时间 比 可执行文件的时间要新:
此时就可以进行 make 编译了。
makefile 当中的 .PHONY: 的使用
make 指令 会根据源文件 和 目标文件 的新旧,判定是否需要重新执行依赖关系,进行编译。编译不总是执行的。
如果想要,在不能编译的情况下一定编译的话,也就是让对应的依赖关系总是被执行,就可以在 makefile当中操作:
我们把 .PHONY 修饰的 目标文件称之为 伪目标。
我们不建议把 你需要的 生产的 目标文件修饰为 伪目标,因为 有些编译器不是很友好,重新编译的时候 可能不是删掉原本的可执行文件,然后重新生成新的可执行文件,可能是 不删除直接新增一个新的可执行文件。可能就会导致老的问题依旧还有。
我们一把 把上述写的 清理操作,比如 clean ,写成 伪目标。
makefile当中的特殊符号
在链接方法当中的:
- "$@" : 代表目标文件。如上述的 text
- "$^" : 代表 : 右侧所属内容。如上述的 text.c
在 链接方法前 加 “@” 符号可以让这个目标文件在生成的时候,不答应生成过程:
相关文章:

Linux - make命令 和 makefile
make命令和 makefile 如果之前用过 vim 的话,应该会对 vim 又爱又恨吧,刚开始使用感觉非常的别扭,因为这种编写代码的方式,和在 windows 当中用图形化界面的方式编写代码的方式差别是不是很大。当你把vim 用熟悉的之后࿰…...

FPGA复习(功耗)
减小功耗 就得减小电流 电流和CF有关( C: 电容(被门数目和布线长度影响) F:时钟频率) 方法大纲 减小功耗:1 时钟控制 2输入控制 3减小供电电压 4双沿触发器 5修改终端 同步数字电路降低动态功耗:动态禁止…...

element ui el-table表格复选框,弹框关闭取消打勾选择
//弹框表格复选框清空 this.$nextTick(()>{this.$refs.table.clearSelection();})<el-table ref"table" v-loading"crud.loading" :header-cell-style"{ color: #FFF, background: #333 }":cell-style"{ color: #FFF, background: #3…...

数据结构——队列
1.队列元素逆置 【问题描述】 已知Q是一个非空队列,S是一个空栈。仅使用少量工作变量以及对队列和栈的基本操作,编写一个算法,将队列Q中的所有元素逆置。 【输入形式】 输入的第一行为队列元素个数,第二行为队列从首至尾的元素…...

【Unity引擎核心-Object,序列化,资产管理,内存管理】
文章目录 整体介绍Native & Managed Objects什么是序列化序列化用来做什么Editor和运行时序列化的区别脚本序列化针对序列化的使用建议 Unity资产管理导入Asset Process为何要做引擎资源文件导入Main-Assets和 Sub-Assets资产的导入管线Hook,AssetPostprocessor…...

Generics/泛型, ViewBuilder/视图构造器 的使用
1. Generics 泛型的定义及使用 1.1 创建使用泛型的实例 GenericsBootcamp.swift import SwiftUIstruct StringModel {let info: String?func removeInfo() -> StringModel{StringModel(info: nil)} }struct BoolModel {let info: Bool?func removeInfo() -> BoolModel…...

数据结构之手撕顺序表(增删查改等)
0.引言 在本章之后,就要求大家对于指针、结构体、动态开辟等相关的知识要熟练的掌握,如果有小伙伴对上面相关的知识还不是很清晰,要先弄明白再过来接着学习哦! 那进入正题,在讲解顺序表之前,我们先来介绍…...

进阶JAVA篇- ZoneId 类与 ZoneDateTime 类、Instant类的常用API(七)
目录 API 1.0 ZoneId 类的说明 1.1 如何创建 ZoneId 类的对象呢? 1.2 ZoneId 类中的 getAvailableZoneIds() 静态方法 2.0 ZoneDateTime 类的说明 2.1 如何创建 ZoneDateTime 类的对象呢? 3.0 Instant 类的说明 3.1 如何创建 Instant 类的对象呢…...

bat脚本字符串替换:路径中\需要替换,解决一些文件写入路径不对的问题
脚本 set dir_tmp%~dp0 set dir%dir_tmp:\\\\\% set dir_tmp%~dp0 新建一个变量dir_tmp,存储获取的脚本当前路径 set dir%dir_tmp:\\\\\% 新建一个变量dir ,存储字符串替换之后的路径 其中黄色的\\实际上代表的是一个\...

python一行命令搭建web服务,实现内网共享文件
python一行命令搭建web服务,实现内网共享文件 有时候我们在本地电脑访问自己的虚拟机的时候,可能因为某些原因无法直接CV文件到虚拟机。但此时我们又想上传文件到虚拟机,如果虚拟机和本地电脑可以互通。那么我们可以直接通过python来启动一个…...

RK3562开发板:升级摄像头ISP,突破视觉体验边界
RK3562开发板作为深圳触觉智能新推出的爆款产品,采用 Rockchip 新一代 64 位处理器 RK3562(Quad-core ARM Cortex-A53,主频最高 2.0GHz),最大支持 8GB 内存;内置独立的 NPU,可用于轻量级人工智能…...

数据结构与算法-队列
队列 🎈1.队列的定义🎈2.队列的抽象数据类型定义🎈3.顺序队列(循环队列)🔭3.1循环队列🔭3.1循环队列类定义🔭3.2创建空队列🔭3.3入队操作🔭3.4出队操作&#…...

腾讯云轻量2核4G5M可容纳多少人访问?
腾讯云2核4G5M服务器支持多少人在线访问?卡不卡?腾讯云轻量2核4G5M带宽服务器支持多少人在线访问?5M带宽下载速度峰值可达640KB/秒,阿腾云以搭建网站为例,假设优化后平均大小为60KB,则5M带宽可支撑10个用户…...

【分布式计算】九、容错性 Fault Tolerance
分布式系统应当有一定的容错性,发生故障时仍能运行 一些概念: 可用性Availability:系统是否准备好立即使用 可靠性Reliability:系统连续运行不发生故障 安全性:衡量安全故障的指标,没有严重事件发生 可维护…...

The SDK location is inside Studio install location 解决
The SDK location is inside Studio install location 解决 安装 Android Studio SDK 时提示:The SDK location is inside Studio install location 解决 问题: 由于 SDK 与 编辑器(Android Studio)的安装在同一目录下所以报错。 解决 你需要在 Andro…...

【蓝桥】数树数
一、题目 1、题目描述 给定一个层数为 n n n 的满二叉树,每个点编号规则如下: 具体来说,二叉树从上往下数第 p p p 层,从左往右编号分别为:1,2,3,4,…, 2p-1。 给你一条从根节点开始的路径࿰…...

2、Windows下安装
目录 一.安装 1、双击下载的程序: 2、加载完成后,会进入如下界面(选第一个Developer Default) 3、然后点击Next 点击Execute 然后Next 4.继续next注意端口为3306 5.继续next,输入账户密码(要有大小写…...

vue中transition的使用
Vue中的<transition>组件用于在元素或组件添加/移除时应用过渡动画。它能够包裹需要进行过渡效果的元素或组件,通过设置相应的CSS样式来实现过渡动画效果。 <transition name"过渡效果名称" before-enter"beforeEnter" enter"…...

性能测试中如何使用RunnerGo还原混合并发场景
我们在进行软件开发时经常需要进行性能测试、压力测试和负载测试。其中有一类测试场景叫做混合并发测试,需要模拟多个接口下不同数量的用户使用场景,检查同时处理多个并发任务的能力,本文将展示如何使用开源的RunnerGo还原混合并发场景。 在…...

KanziStudio described using object-oriented design patterns(持续更新...)
1.绑定-mvc mvc,model数据与view控件分离。...

线程同步的几种方式
目录 互斥锁条件变量读写锁信号量CAS-- 参考 线程同步方式有互斥锁,条件变量,信号量,读写锁,CAS锁等方式 互斥锁 互斥量 pthread_mutex_t在执行操作之前加锁,操作完之后解锁. 使用互斥量,来确保同一时刻只…...

Linux网络编程系列之服务器编程——多路复用模型
一、什么是多路复用模型 服务器的多路复用模型指的是利用操作系统提供的多路复用机制,同时处理多个客户端连接请求的能力。在服务器端,常见的多路复用技术包括select、poll和epoll等。这些技术允许服务器同时监听多个客户端连接请求,当有请求…...

在SQL语句里使用正则表达式,因该怎么使用
在SQL中使用正则表达式通常需要使用特定的函数或运算符,具体的语法可能因不同的数据库系统而有所不同。以下是使用正则表达式的一般方法,但请注意,具体语法可能会因您使用的数据库而有所不同。 一般情况下,您可以使用以下方法在S…...

扫码登录-测试用例设计
扫码登录测试用例...

PyTorch CUDA GPU高占用测试
0x00 问题描述 安装完成PyTorch、CUDA后,验证PyTorch是否能够通过CUDA高占用GPU(占用>95%),特地使用以下代码测试。 0x01 代码设计 这个代码会持续执行神经网络的训练任务,每次循环都进行前向传播、反向传播和参数…...

Java|学习|abstract ,接口 Interface , Object
1.abstract 1.1 abstract abstract 是修饰符,表示抽象的,用来修饰抽象类和抽象方法。 abstract 修饰的类是抽象类,抽象类不能创建对象,主要用于被子类继承。 abstract 修饰的方法是抽象方法,该方法没有方法体&…...

安全的Sui Move是Web3大规模采用之路的基石
没有信任,就没有Web3的大规模采用。还有其他重要障碍阻碍了首个十亿用户的到来,包括令人困惑的用户体验、复杂的身份验证模式以及不确定的监管体系,但所有障碍中,要数大多数人对区块链技术持怀疑和不信任态度最严重。 对于许多人…...

Python中图像相似性度量方法汇总
1. 引言 在当前到处充满着图像的世界里,测量和量化图像之间的相似性已经成为一项关键的任务。无论是图像检索、内容推荐还是视觉搜索,图像相似性方法在现代计算机视觉的应用中都发挥着关键的作用。 幸运的是,Python提供了大量的工具和库&am…...

pycharm中快速对比两个.py文件
在学习一个算法的时候,就想着自己再敲一遍代码,结果最后出现了一个莫名其妙的错误,想跟源文件对比一下到底是在哪除了错,之前我都是大致定位一个一个对比,想起来matlab可以快速查找出两个脚本文件(.m文件)的区别&#…...

C++程序结束
在C程序任意位置结束程序需要return 0,如果只return的话会发生生成错误...