[Linux]Linux项目自动化构建工具-make/Makefile

news2025/1/12 1:07:28

🥁作者华丞臧.
📕​​​​专栏:【LINUX】
各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞+收藏+关注)。如果有错误的地方,欢迎在评论区指出。
推荐一款刷题网站 👉 LeetCode刷题网站


文章目录

  • 前言
  • make && makefile
    • 一个简单的makefile文件
    • make执行步骤
    • 依赖关系
    • 依赖方法
    • 项目清理
    • 原理
  • 伪目标.PHONY
    • 生成多个目标文件
  • 自动变量
  • Linux第一个小程序 - 进度条
    • 行缓冲区
    • 进度条代码


前言

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

make && makefile

make一条命令makefile一个文件,两个搭配使用,完成项目自动化构建。(makefile也可以写成Makefile)

一个简单的makefile文件

在Linux中编写一个makefile文件和一个code.c文件,文件内容如下:

//makefile文件中的内容
code:code.c
	gcc code.c -o code
.PHONY:clean
clean:
	rm -f code
	
//code.c中的代码
#include <stdio.h>

int main()
{
	printf("hello world!\n");
	return 0;
}

在这里插入图片描述

make执行步骤

当我们在Linux中输入并执行make命令时,make会干些什么呢?

  1. 首先第一步, make会在当前目录下寻找Makefile文件,找到了就会用这个文件作为后续操作的依据;如果没找到Makefile,就会寻找名为makefile的文件;两个都没找到就报错;
  2. 找到Makefile或者makefile之后,默认文件中第一行作为第一个目标;make命令会分析第一个目标的依赖关系,并且执行该目标的依赖方法;

注意

  1. make默认从上到下扫描makefile文件,并且第一个被扫描的目标可以被省略名称。
  2. makefile默认情况下只执行生成一个目标文件(一般可执行目标文件),后续的依赖关系和依赖方法不会执行。

依赖关系

//makefile文件中的内容
code:code.c //依赖关系
	gcc code.c -o code //依赖方法

上面的makefile文件中code就是目标,简单来说就是期望生成的内容;比如你编译一个文件,并且期望生成一个名为code的可执行目标文件。
而你要编译生成code这个目标文件,需要依赖code.c这个文件,这就是依赖关系。

依赖方法

有了依赖关系,我们就知道了生成目标可执行文件code需要code.c这个文件,那么我们该怎么利用code.c文件生成code文件呢,我们是不是还缺少一个方法,而这个使用目标的依赖关系文件生成期望的目标文件的方法就是依赖方法。
如上述代码中的:

gcc code.c -o code  //依赖方法

在这里插入图片描述
注意:依赖文件列表可以为空。

项目清理

  • 工程是需要被清理的。
  • 像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令——“makeclean”,以此来清除所有的目标文件,以便重编译。
  • 但是一般我们这种clean的目标文件,我们将它设置为伪目标,用.PHONY修饰,伪目标的特性是,总是被执行的。
  • 可以将我们的code目标文件声明成伪目标,测试一下。
//makefile文件中的清理
.PHONY:clean
clean:
	rm -f code

在这里插入图片描述

原理

make是如何工作的,在默认的方式下,也就是我们只输入make命令。那么,

  1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
  2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“code”这个文件, 并把这个文件作为最终的目标文件。
  3. 如果code文件不存在,或是hello所依赖的后面的code.o文件的文件修改时间要比code这个文件新(可 以用 touch 测试),那么,他就会执行后面所定义的命令来生成code这个文件。
  4. 如果code所依赖的code.o文件不存在,那么make会在当前文件中找目标为code.o文件的依赖性,如果 找到则再根据那一个规则生成code.o文件。(这有点像一个堆栈的过程)
  5. 当然,你的C文件和H文件是存在的啦,于是make会生成 code.o 文件,然后再用 code.o 文件声明 make的终极任务,也就是执行文件code了。
  6. 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文 件。
  7. 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错, 而对于所定义的命令的错误,或是编译不成功,make根本不理。
  8. make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。
//makefile
code:code.o    
    gcc code.o -o code    
code.o:code.s    
    gcc -c code.s -o code.o    
code.s:code.i    
    gcc -S code.i -o code.s    
code.i:code.c    
    gcc -E code.c -o code.i    
.PHONY:clean    
clean:    
    rm -f code code.i code.s code.o code   

在这里插入图片描述

伪目标.PHONY

在makefile或者Makefile文件中,被.PHONY修饰的对象就是一个伪目标。
伪目标总是被执行,那么这是什么意思呢?请看下图:
在这里插入图片描述
当第一次使用make命令时,目标被执行生成了一个可执行文件,而当我们再使用make命令时发现make虽然不会报错但是会告诉我们生成的文件的已经是最新的了,目标并没有被执行。

//修改makefile文件
.PHONY:code
code:code.c
	gcc code.c -o code

修改makefile文件,再使用make命令,可以看到伪目标一直可以被执行,如下图:
在这里插入图片描述
在这里还有一个问题,make怎么知道生成的目标文件是最新的呢?
根据文件的三个时间,如下图:
目标依赖关系文件的Modify时间一定比执行目标依赖方法生成文件的Modify时间更新。
在这里插入图片描述

Access :文件最近一次被读取的时间;
Modify :文件内容最近一次被修改的时间;
Change :文件的属性最近一次被修改的时间。

注意:修改文件内容,文件属性也会修改,因为文件大小也是文件属性。由于文件的访问非常频繁,如果每次都更改就要进行更多次的IO(文件在磁盘上),所以系统对文件的Access时间的更改机制进行了修正。
在这里插入图片描述

伪目标后面可以跟依赖关系,也可以不跟依赖关系,并且依赖关系也可以是伪目标。

生成多个目标文件

我们知道make默认执行第一行的目标及其依赖关系并且生成一个目标文件;那么当我们需要生成多个可执行目标文件时,默认make就不能满足我们的要求了,而伪目标很好的解决这个问题。

#伪目标可以有多个依赖关系
.PHONY:ALL
ALL:test code

test:test.c
	gcc test.c -o test #这里必须是TAB键开头

code:code.c
	gcc test.c -o code
	
.PHONY:clean
clean:
	rm -f code test

//test.c
#include <stdio.h>

int main()
{
	printf("这是一个伪目标的测试!\n");
	return 0;
}

在这里插入图片描述

注意
在这里插入图片描述
空格键是不行的,make会报错,如下图:
在这里插入图片描述

自动变量

这里只介绍makefile当中的一部分自动变量,自动变量是makefile中定义的一些自动化变量。

  • $@:表示目标文件的名称,包括扩展名(后缀);
  • $^:表示所以的依赖文件,以空格隔开,不能重复;
  • $<:表示第一个依赖文件的名称;
//前面的makefile文件可以改成下面这样
.PHONY:ALL
ALL:test code

test:test.c
	gcc $^ -o $@ 

code:code.c
	gcc $^ -o $@ 

可以看到目标的依赖关系被执行时,自动变量被替换成对应的字符。
在这里插入图片描述

Linux第一个小程序 - 进度条

\r && \n 的区别

  • (\r)回车概念:回到当前行的开头。
  • (\n)换行概念:换到下一行。

行缓冲区

首先来看下面这一段代码:

#include <stdio.h>
#include <unistd.h>

int main()
{
 	printf("hello world!\n"); //行缓冲
 	//fflush刷新缓冲区,加上这个可以看到先打印再休眠
 	//fflush(stdout);  
 	//stdout -> 标准输出流 -> 显示器
 	sleep(2);  //让程序休眠指定秒
 	return 0;
}

需要知道的是我们的代码是顺序结构,也就是一定是printf()先执行打印,然后程序再休眠2秒,在Linux上运行该代码确实如此,那么删掉printf中的\n呢?同样的代码,我们发现没有\n时,程序会先休眠2秒再打印。
在这里插入图片描述
上述样例告诉我们,printf一定先跑完,只不过该数据并没有立即显示出来。(结论)那么在休眠时,对应数据就存储在缓冲区中。
\n默认往显示器上输出时,默认是行缓冲,当有一行输入时就会把包含这一行全部的内容打印出去。

进度条代码

//process.c
#include "process.h"    
    
const char* str[STR_MAX] = {"/", "-","\\", "|"};    
    
void Process()    
{    
  int count = 0;    
  char bar[NUM];    
  memset(bar, '\0', NUM);    
  while(count <= 100)    
  {    
    //配置底色颜色选项\033[42;34m  \033[0m
    //printf("\033[42;34m[%-100s][%3d%%][%s]\033[0m\r", bar, count, str[count % STR_MAX]);    
    printf("[%-100s][%3d%%][%s]\r", bar, count, str[count % STR_MAX]);                                                                      
    fflush(stdout);                                       
    bar[count++] = STYLE;                        
    usleep(10000);     
   // sleep(1);                                      
  }                                      
  printf("\n");               
} 

//process.h
#pragma once 
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define NUM 101
#define STYLE '#'
void Process();

//main.c
#include "process.h"

int main()
{
  Process();
  return 0;
}

//makefile文件
ProcessOn:main.c process.c                                                                                                              
	gcc $^ -o $@
 
.PHONY:clean
clean:
	rm -f ProcessOn

配置底色为绿色,运行结果如下:
在这里插入图片描述

注意:使用·xshell运行程序时,一定要把窗口最大化,不然xshell上打印出的现象是换行的。
在这里插入图片描述

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

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

相关文章

FX5U-相对定位指令DRVI(DDRVI )两种写法

该指令通过增量方式(采用相对地址的位置指定)&#xff0c;进行1速定位。 以当前停止的位置作为起点&#xff0c;指定移动方向和移动量(相对地址)进行定位动作。如果驱动触点置为ON,则输出脉冲&#xff0c;并开始从偏置速度进行加速动作。到达指令速度后&#xff0c;以指令速度进…

【LeetCode】N皇后-回溯

N皇后-回溯N皇后题目示例分析代码N皇后II题目示例分析代码总结N皇后 题目 LeetCode 51.N皇后 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间…

第二篇 - Vue 的初始化流程

一&#xff0c;前言 上篇&#xff0c;使用 rollup 完成了 Vue2 源码环境的搭建 本篇&#xff0c;介绍 Vue 的初始化流程 二&#xff0c;Vue 简介 以两个概念性问题做简单介绍 1&#xff0c;问题&#xff1a;Vue 是 MVVM 框架吗&#xff1f; 在 Vue 官网上是这样说的&#…

LeetCode450之删除二叉搜索树中的节点(相关话题:二叉搜索树,删除)

题目描述 给定一个二叉搜索树的根节点 root 和一个值 key&#xff0c;删除二叉搜索树中的 key 对应的节点&#xff0c;并保证二叉搜索树的性质不变。返回二叉搜索树&#xff08;有可能被更新&#xff09;的根节点的引用。 一般来说&#xff0c;删除节点可分为两个步骤&#x…

VS封装C++项目以及属性配置

目录 1. 封装单个项目 1.1 封装 新建C空项目&#xff0c;将需要封装的源文件(eval.cpp)和 头文件(eval.h)复制过来&#xff0c;并对它们做如下修改&#xff1a; 注&#xff1a;如果有多个头文件和源文件&#xff0c;只对可供外部调用的文件及其内部的函数作下面的处理 分别…

1.c++环境配置及第一个环境运行

开发IDE与环境 最好是使用ubuntu系统进行开发&#xff0c;如果没有的话&#xff0c;基于windows使用vs code 进行ssh连接到远程的ubuntu主机进行开发也可以。开发的过程跟本地差不多。 vs code IDE 插件的安装 1.变成中文菜单与提示,安装MS-CEINTL.vscode-language-pack-zh-…

《MySQL系列-InnoDB引擎13》文件-参数文件

文件 MySQL数据和InnoDB存储引擎表中的各种类型的文件&#xff0c;这些文件如下&#xff1a; 参数文件&#xff1a;MySQL启动时的数据库文件&#xff0c;指定初始化参数&#xff0c;介绍各种参数类型&#xff0c;以及定义某种内存结构的大小等日志文件&#xff1a;用来记录My…

C语言日常练习

这里写目录标题循环结构输入两个正整数m和n&#xff0c;求其最大公约数和最小公倍数求Snaaaaaaaaaa……的值&#xff0c;其中a是一个数字&#xff0c;n表示a的个数&#xff0c;n和a都由键盘输入一维数组从键盘输入十个数&#xff0c;并将正着输出反着输出从键盘输入十个数&…

虹科案例 | 解决ASRS系统的痛点问题居然这么简单?(上)

摘要 ASRS(自动存储和检索系统)在内部物流领域变得越来越常见。内部物流包括优化、整合、自动化和管理履行或配送中心内的货物物流流动。 ASRS穿梭机经常用在具有多个存储级别的配送中心的仓库或库存集装箱中处理散装产品的托盘。 自动化存储和检索系统的定义是专门为物料的存…

springcloud-02-微服务间通信及熔断组件

第二章 微服务间通信及熔断组件 1. 微服务间通信组件 1.1 基于RestTemplate的服务调用 Spring框架提供的RestTemplate类可用于在应用中调用rest服务&#xff0c;它简化了与http服务的通信方式&#xff0c;统一了RESTful的标准&#xff0c;封装了http链接&#xff0c; 我们只…

初识 Node.js

1、回顾与思考 1.1、浏览器中的 JavaScript 的组成部分 1.2、思考&#xff1a;为什么 JavaScript 可以在浏览器中被执行 1.3、思考&#xff1a;为什么 JavaScript 可以操作 DOM 和 BOM 1.4、浏览器中的 JavaScript 运行环境 2、Node.js 简介 2.1、什么是 Node.js Node.js…

RSA加密算法完整加密流程

RSA完整加密流程总结1.1-RSA加密介绍RSA公钥加密算法是1977年由罗纳德李维斯特&#xff08;Ron Rivest&#xff09;、阿迪萨莫尔&#xff08;Adi Shamir&#xff09;和伦纳德阿德曼&#xff08;Leonard Adleman&#xff09;一起提出的。1987年7月首次在美国公布&#xff0c;当时…

实习------Spring 框架学习

Spring 是什么&#xff08;了解&#xff09; 在不同的语境中&#xff0c;Spring 所代表的含义是不同的。下面我们就分别从“广义”和“狭义”两个角度&#xff0c;对 Spring 进行介绍。 广义上的 Spring 泛指以 Spring Framework 为核心的 Spring 技术栈。 经过十多年的发展&…

【原创】升级需谨慎,开发两行泪!——记一次MySQL驱动包升级引发的事故

一、背景最近项目组在版本迭代时&#xff0c;组件也要进行升级&#xff0c;此时涉及到MySQL驱动包jdbc的版本升级。即从5.1.X升级到8.0.X。然鹅在上线之后就出现了一部分兼容性问题&#xff0c;造成了一次“事故”&#xff1a;调用接口出现“系统错误”。查看日志&#xff1a;j…

C. Least Prefix Sum(可以后悔的拿取+multiset)

Problem - C - Codeforces 波罗的海&#xff0c;一个著名的棋手&#xff0c;同时也是一个数学家&#xff0c;他有一个数组a1,a2,...,an&#xff0c;他可以进行以下几次&#xff08;可能是0次&#xff09;操作。 选择某个索引i&#xff08;1≤i≤n&#xff09;。 将ai与-1相乘&…

Ffmpeg中AVFrame数据保存成YUV--讨论AVFrame的linesize

目录 YUV播放器 AVFrame中保存成YUV实现 linesize的意义 实测(PC机-64bits-win10) 总结&#xff1a; YUV播放器 首先要有一个YUVplayer用来播放测试的YUV数据&#xff0c;雷神改良过的YUV播放器&#xff1a; 修改了一个YUV/RGB播放器_雷霄骅的博客-CSDN博客 播放器播放界…

查询是: LOCK TABLE test.xx_test IN ACCESS SHARE MODE问题解决办法

如题所示&#xff0c;这个问题是我在postgresql中使用pg_dump备份多个schema的表时遇到的问题。bin\pg_dump --dbnamepostgresql://dbuser:123456localhost:5432/test --table public.xx_user --table test.xx_test -f d:\tools\pgsql\dump.sql pg_dump: 错误: 查询失败: 閿欒…

react生命周期(类组件/函数组件)

1.react代码模式分为两种 类组件和函数组件&#xff08;生命周期也有所不同&#xff09; 2.类组件&#xff08;写法如下&#xff09; import React from react export default class App1 extends React.Component{state {username:,password:}setUser (event) > {this.s…

《MySQL系列-InnoDB引擎12》启动、关闭与恢复

启动、关闭与恢复 InnoDB是MySQL数据库得存储引擎之一&#xff0c;因此InnoDB存储引擎得启动和关闭&#xff0c;可以说是MySQL实例得启动过程中对InnoDB存储引擎的处理过程。 在关闭时&#xff0c;参数innodb_fast_shutdown影响着表的存储引擎为InnoDB的行为。该参数可取值为0、…

BIOS(控制权交接第一棒)

计算机系统的控制权&#xff08;CPU的使用权&#xff09;交接的第一棒是BIOS&#xff01;接下来&#xff0c;我们简单学习一下BIOS相关知识。 在计算机接电后按下开机键&#xff0c;首先运行的软件是基本输入输出系统&#xff08;Basic Input Output System&#xff0c;BIOS&a…