Linux | makefile简单教程 | Makefile的工作原理

news2024/10/6 4:12:27

前言 

在学习完了Linux的基本操作之后,我们知道在linux中编写代码,编译代码都是要手动gcc命令,来执行这串代码的。

 但是我们难道在以后运行代码的时候,难道都要自己敲gcc命令嘛?这是不是有点太烦了?

 在vs中,我们编写好代码之后,直接点击构建项目,就会直接帮我们自动化构建好了,我们在linux中构建的时候,有的时候上百个文件,还是比较麻烦的,所以到底有没有一些简单的做法呢?当然是有的啦~

这个工具呢就是Makefile/make项目自动化构建工具

  • 会不会写Makefile,从一个侧面说明了一个人是否具有完成大型工程的能力;
  • 一个工程的源文件不计其数,其按类型、功能、模板分别放在一个若干个目录中,Makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作;
  • Makefile带来的好处就是——自动化编译。一旦写好,只需要一个make命令,整个工程就完成自动化编译,极大提高了软件开发的效率;
  • make是一个命令工具,是一个解释Makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,visual C++的make,linux下GNU的make。可见,Makefile都成为了一种在工程方面的编译方法。
  • make是一个命令,Makefile是一个文件,两个搭配使用,完成项目自动化构建

实例理解

 在上图中,mytest:text.cgcc -o mytest test.c是匹配的,clean:rm -f mytest是匹配的。这两组中的每个第一句是依赖关系,两组的下面称为依赖方法

Makefile实现原理

以图为例(与上图例子一致,为了方便大家查看,就在下面也放一份):

 Makefile文件写好之后,当我们实际在命令行运行make的时候,对应的make程序,会在当前目录下找这个Makefile,读取Makefile里面的内容:

  • make会根据Makefile里面的内容,完成编译、清理工作。
  • 根据Makefile里面的依赖关系,依赖关系依赖方法形成目标文件

(如上图:编译器就会知道依赖关系test.c文件形成mytest的目标文件,那么怎么形成呢?就是根据对应的gcc -o mytest test.c这样的依赖方法)

依赖关系

那么到底如何理解依赖关系依赖方法呢?

这里就给大家将一个小故事咯~便于大家理解:

小明是某大学里的在校生,这天他没有生活费了。他就打电话给那个人:“爸!我是你儿子!”在小明看来,你是我爸你就应该给我生活费;但是在老爹看来,你小子说这话是啥意思?你想干啥?老爹是一脸懵啊。小明挂了电话之后,就又想了想:“爸!我是你儿子!给我点生活费!”这下他老爹才懂了,原来他儿子没有生活费了,想要生活费。

所以在上面的小故事中,“我是你儿子”就是依赖关系,“给我点生活费”就是依赖方法。在日常生活中,我们也难免得需要拜托谁做一件什么事,在计算机中也是一样的,完成一件事的必然要素少不了依赖关系与依赖方法。

在上图中的依赖关系如下:

其实,我们知道程序翻译运行的过程为:预处理、编译、汇编和链接。

翻译的过程

gcc命令

说明

预处理

(进行宏替换)

gcc -E test.c -o test.i

-E 让gcc在预处理结束后,就停止编译

“从现在开始进行程序的翻译,预处理完成,就停下来”

宏替换

去注释

头文件展开

条件编译

生成.i文件

编译

(生成编译)

gcc -S test.i -o test.s

-S 只进行编译不进行汇编,生成汇编代码

“从现在开始进行程序的编译,汇编完成就停下来”

检查代码规范性

检查语法错误

(确认无误后汇编)

C语言代码翻译成汇编语言

生成.s文件

汇编

gcc -C test.o -o test.o

-C “从开始带现在进行程序的翻译”,汇编完成就停下来

将汇编语言编译成二进制目标文件

生成.o文件

链接

gcc test.o -o my.exe

形成可执行程序

生成.exe文件

所以将上面gcc -o mytest test.c指令写完整就是如下:

诶?很奇怪啊,怎么感觉顺序呢不太对?在这里我们就要说一下Makefile的工作原理了:

  • Makefile在执行过程中,是从上往下进行扫描的;
  • 当它看到的第一个文件时,其实并不是.c文件,第一个是.o文件;
  • 也就是识别的从上往下,第一组依赖关系;
  • 可是识别的依赖关系中的.o文件并不存在,所以它下面的命令,也就是依赖方法不能被执行,就无法形成可执行程序;
  • 所以Makefile就会自动在后续,继续再找下一组依赖关系,根据下一组依赖关系来形成.o文件;
  • 但是在形成.o文件还是需要依赖.s文件,依次类推直到遇到依赖文件存在.c文件;
  • 所以存在.c文件,就会有.i文件、.s文件以及.o文件;
  • 所以就下往上执行了,很类似与递归问题。

在上图中:

  • 上面的文件 mytest ,它依赖 mytest.o
  • mytest.o , 它依赖 mytest.s
  • mytest.s , 它依赖 mytest.i
  • mytest.i , 它依赖 mytest.c
  • make、Makefile会自动根据文件中的依赖关系,进行自动推导,帮助我们执行所有相关的依赖方法。

这里注意一下,因为Makefile就是类似于一个选择器,里面包含了各种指令的选项,但是一般默认智慧运行第一个依赖关系所对应的指令,所以上面的各组依赖关系是可以乱序的,但是必须要将最重要的一条指令放在最前面,如下图:

 上面的依赖关系,是为了让大家能够理解,这里不建议大家写成这样,直接就gcc -o mytest test.c指令就可以。

依赖文件列表

依赖文件列表可以为多个文件,按照空格分割分。

mytest:依赖文件1 依赖文件2 依赖文件3 ...

 

我们刚刚也说了clean:rm -f mytest是匹配的。根据上图我们也不难看出,clean后面是空的,也就是说clean不依赖任何文件,也就是说:

  • 依赖文件列表可以为空。 

依赖方法

依赖方法的指令前必须打一个[Tab]键,按四下空格会报错。

多条依赖方法

  • 依赖方法不限于一条,可以是多条的。 

项目清理

.PHONY含义

在Makefile文件执行的时候,如下图,我们发现在使用make的时候默认执行的是Makefile文件中的第一对的依赖关系和依赖方法:

  • Makefile默认形成第一个可执行文件。

 其次,我们看下边运行过程:

make: `mytest' is up to date. :当前可执行程序是最新的。

  • 所以Makefile会在源代码的内容没有修改,没有变化时,Makefile默认就会拦截再一次的make命令。

那么如果今天我们就想让其一直执行,不要拦截,即使在没有被修改源码的情况下,也要让make指令一直执行,那么怎么做呢?只需要:

  • .PHONY : XXX 
  • XXX对应的方法总是要被执行的。

 当然我们理解了原理之后,小编还是建议大家之后再清理的命令加上总是可执行即可。所以我们目前标准的就是以下5行:

以后我们需要的Makefile文件

通配符认识

符号含义
$^所有依赖文件列表
$@所有目标文件
$<所有依赖文件的第一个文件

 在这里,我们可以将$,理解为取内容

如下图,gcc在编译的过程,Makefile会自动进行符号替换:

  • 把对应的$@就会自动替换为目标文件
  • 把对应的$^就会自动替换为所有依赖文件

变量

Makefile也是支持变量的。但是并不是之前学习的编程语言中的整形、浮点型int/double=1/1.0等等。Makefile是解释性的,所以它的变量直接就是符号,比如说,“‘形成的可执行程序’为‘XX’”:

  • bin是“目标文件可执行程序”的变量 ;
  • src是“依赖文件列表”的变量;
  • $可以理解为去内容;
  • $(bin):取出bin变量中的内容,即目标文件;
  • $(src):取出src变量中的内容,即依赖文件列表;
  • $@:所有的目标文件;
  • $^:所有的依赖文件列表。

在执行make的时候,Makefile就会进行变量替换,可以理解成为宏的替换,也可以执行程序。

将来你可能会有很多地方遇到使用make的项目与文件,那就只需要将变量的内容修改,就可以完成不同文件的make指令进行执行,就会很简单方便。

make指令打印隐藏

在命令行输入make指令之后,系统都会自己打印出我们底层所输入的依赖方法对应的指令,那么如何将make指令进行隐藏也就是让它不再打印呢?

直接在命令前面加一个“@”符号:

推荐使用的Makefile模板

bin=test.exe    
src=test.c      
    @gcc -o $@ $^
    @echo "compiler $(src) to $(bin)..."
.PHONY:clean
clean:
    @rm -f $(bin)
    @echo "clean project..."

  • 其中echo是打印字符串。 

如下图就会有提示信息:

必熟知知识

为什么Makefile对最新的可执行程序,默认不想重新形成?

今天,我们编译的代码只有一个源文件,以后我们编译的源代码是两千个源文件,成百上千个源文件,在我们修改bug的时候,可能只是修改几行代码,做完改动之后,如果我们要将所有的源文件重新编译一遍,那么就是效率很低,又或者说,如果我们没有修改,又make的话,那么编译器又会重新编译,假如一个文件需要用0.1秒,那么成百上千个代码就需要十几分钟甚至几个小时,所以效率是极其低的。

  • 所以在Makefile为了调高效率,就默认对最新的可执行程序,不重新形成。

Makefile怎么知道程序需要被编译的呢?

ACM时间是Linux系统下分别代表AccessModifyChange三个时间。

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

当然我们要知道的是:

  • 源代码可执行程序文件内容最近被修改的时间(修改)一定是不同的。 

因为我们基本先写源代码,再进行编译;所以可执行程序一般是比源代码的时间更新的。如果修改的源代码,那么此时源代码比可执行程序的时间更新,所以就需要重新编译。

Makefile怎么知道程序需要被编译的呢?

  • 对比可执行程序的最近修改时间和源文件最近的修改时间,谁更新。如果可执行程序更新,就不需要重新编译了;反之,源文件更新,就需要重新编译。

所以我们在用vs写代码的时候,出现错误 ,再修改了很多次,仍旧报这个错误,这个时候,可以重新清理一下,重新构建一下,重新生成解决方案,这个问题就会可能解决。

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

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

相关文章

Docker安装多个nginx容器时,要注意端口设置:

Docker安装多个nginx容器时&#xff0c;要注意端口设置&#xff1a; docker run -id --namemynginx4 -p 8089:80 nginx 安装多个nginx容器时&#xff0c;要注意端口设置&#xff1a;宿主机80端口已经被暂用&#xff0c;所以宿主机端口一定不能设置位80&#xff0c;但是容器上80…

Vulnhub靶场DC-7

本机192.168.223.128 目标192.168.223.136 主机发现 nmap -sP 192.168.223.0/24 端口发现 nmap -sV -p- -A 192.168.223.136 开启了22 80端口 80端口apache服务 先进入web界面看一下 提示&#xff1a;DC7加了一些新的东西&#xff0c;让我看看盒子之外的东西&#xff0c;与…

C++ 利用容器适配器,仿函数实现栈,队列,优先级队列(堆),反向迭代器,deque的介绍与底层

C 利用容器适配器,仿函数实现栈,队列,优先级队列【堆】,反向迭代器,deque的介绍与底层 一.容器适配器的介绍二.利用容器适配器实现栈和队列1.stack2.queue 三.仿函数介绍1.什么是仿函数2.仿函数的使用3.函数指针的使用1.函数指针的用处2.利用函数指针完成回调3.利用仿函数完成回…

docker 体验怀旧游戏(魂斗罗等)

docker run --restart always -p 8081:80 --name fc-games -d registry.cn-hangzhou.aliyuncs.com/bystart/fc-games:latest ip:8081访问 jsnes: js制作了一个网页版的NES模拟&#xff0c;可以在网页上玩fc游戏 (gitee.com)

创建第一个 Spring 项目(IDEA社区版)

文章目录 创建 Spring 项目创建一个普通的 Maven 项目添加 Spring 依赖IDEA更换国内源 运行第一个 Spring 项目新建启动类存储 Bean 对象将Bean注册到Spring 获取并使用 Bean 对象 创建 Spring 项目 创建一个普通的 Maven 项目 首先创建一个普通的 Maven 项目 添加 Spring 依…

Tomcat好帮手---JDK

目录 1、Tomcat好帮手---JDK 2、安装JDK 部署Tomcat参考博主博客 部署TOMCAT详解-CSDN博客 1、Tomcat好帮手---JDK JDK是 Java 语言的软件开发工具包&#xff0c;JDK是整个java开发的核心&#xff0c;它包含了JAVA的运行环境&#xff08;JVMJava系统类库&#xff09;和JAVA…

2024年新提出的算法:一种新的基于数学的优化算法——牛顿-拉夫森优化算法|Newton-Raphson-based optimizer,NRBO

1、简介 开发了一种新的元启发式算法——Newton-Raphson-Based优化器&#xff08;NRBO&#xff09;。NRBO受到Newton-Raphson方法的启发&#xff0c;它使用两个规则&#xff1a;Newton-Raphson搜索规则&#xff08;NRSR&#xff09;和Trap Avoidance算子&#xff08;TAO&#…

视频压缩怎么才能保持画质清晰?这样设置参数~

很多时候我们都需要压缩视频&#xff0c;但在减小视频大小的时候我们也会担心画质清晰度问题&#xff0c;那怎么既能压缩视频大小&#xff0c;又能保证视频清晰度呢&#xff1f;下面就来了解下吧~ 视频压缩分为无损压缩和有损压缩&#xff0c;如果想保持画质清晰&#xff0c;可…

[C#]winform部署yolov5实例分割模型onnx

【官方框架地址】 https://github.com/ultralytics/yolov5 【算法介绍】 YOLOv5实例分割是目标检测算法的一个变种&#xff0c;主要用于识别和分割图像中的多个物体。它是在YOLOv5的基础上&#xff0c;通过添加一个实例分割模块来实现的。 在实例分割中&#xff0c;算法不仅…

spring-boot redis stream消息队列demo-及死信简单处理

Redis stream 是 Redis 5 引入的一种新的数据结构&#xff0c;它是一个高性能、高可靠性的消息队列&#xff0c;主要用于异步消息处理和流式数据处理。在此之前&#xff0c;想要使用 Redis 实现消息队列&#xff0c;通常可以使用例如&#xff1a;列表&#xff0c;有序集合、发布…

RedisInsight详细安装教程

简介 RedisInsight 是一个直观高效的 Redis GUI 管理工具&#xff0c;它可以对 Redis 的内存、连接数、命中率以及正常运行时间进行监控&#xff0c;并且可以在界面上使用 CLI 和连接的 Redis 进行交互&#xff08;RedisInsight 内置对 Redis 模块支持&#xff09;。 RedisIn…

用navigator.sendBeacon完成网页埋点异步请求记录用户行为,当网页关闭的时候,依然后完美完成接口请求,不会因为浏览器关闭了被中断请求。

代码用例 <template><div :class"$options.name"><el-button type"primary" click"sendBeacon">navigator.sendBeacon 请求埋点接口 发送json对象数据</el-button></div> </template><script> expor…

JVM系列-7内存调优

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术、JVM原理&#x1f525;如果感觉博主的文…

TensorFlow2实战-系列教程3:猫狗识别1

&#x1f9e1;&#x1f49b;&#x1f49a;TensorFlow2实战-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Jupyter Notebook中进行 本篇文章配套的代码资源已经上传 1、项目介绍 数据预处理&#xff1a;图像数据处理&#xff0c;准备训练和验证数据集卷…

无人值守变电所运维在海南市某住宅区的应用

1 前言 随着国家电网改革政策的逐步推进和落实&#xff0c;AcrelCloud-1000变电所运维云平台运用互联网和大数据技术&#xff0c;为电力运维公司提供变电所运维云平台。该平台作为连接运维单位和用电企业的纽带&#xff0c;监视用户配电系统的运行状态和电量数据&#xff0c;为…

HCIA学习第二天OSI七层协议与网络协议_操纵网络设备

第一天总结&#xff1a; 对等网——网络变大——无限的传输距离 无冲突 单播 为满足以上问题&#xff0c;出现了--网桥--紧接着出现了交换机——介质访问控制层&#xff08;二层设备&#xff09;——识别MAC地址 &#xff08;认识有记录-单播 不认识无记录-泛洪&#xff08;泛…

【java面试】Spring

目录 1. Spring 介绍1.1 Spring 的优点1.2 Spring 的缺点1.3 详细讲解一下核心容器&#xff08;spring context应用上下文) 模块 2. Spring俩大核心概念IOC&#xff0c;Inversion of Control&#xff0c;控制反转AOP(Aspect-OrientedProgramming)&#xff0c;面向切面编程Sprin…

如何实现高效一键群发1000人?

对于职场人来说&#xff0c;微信里的客户越多&#xff0c;成交的概率越大。但客户太多&#xff0c;群发是个难题&#xff0c;因为微信本身的群发是有数量限制的。 我们都知道&#xff0c;群发消息可以帮助我们&#xff1a; 1. 高效传递信息&#xff1a;群发短信的方式能够迅速…

Vue<圆形旋转菜单栏效果>

效果图: 大家不一定非要制成菜单栏,可以看下人家的华丽效果😝,参考地址 https://travelshift.com/ 大佬写的效果可比我的强多了,但是无从下手,所以就自己琢磨怎么写了,只能说效果勉强差不多 可以通过更改data值和注释我标注的css样式处部分,就可以实现全圆的效果😄…

vue3 + antd 封装动态表单组件(二)

传送带&#xff1a; vue3 antd 封装动态表单组件&#xff08;一&#xff09; 前置条件&#xff1a; vue版本 v3.3.11 ant-design-vue版本 v4.1.1 vue3 antd 封装动态表单组件&#xff08;一&#xff09;是基础版本&#xff0c;但是并不好用&#xff0c; 因为需要配置很多表…