当前位置: 首页 > news >正文

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

什么是 make

make 是一个命令,他会在源文件的当前目录下寻找 makefile 或者 Makefile 文件执行这个文件中的代码。

makefile 文件的编写

我们先来见见猪跑,看看 make 怎么用的:
下面是 makefile 文件的内容:
在这里插入图片描述
这是 test.c 中的内容:

#include<stdio.h>
int main()
{printf("hello make\n");return 0;
}

之前我们想要使用 gcc 编译 test.c 生成 test 可执行文件,你是不是要这样写命令:

gcc -o test test.c

在我们写了上面的 makefile 文件之后,我们就能使用 make 命令来代替啦!
在这里插入图片描述
我们可以看到使用 make 命令之后顺利编译出来了可执行文件,并且能够顺利执行!

makefile 文件的编写

我们再来看 makefile 文件中的代码:
在这里插入图片描述
其中这个冒号前面的部分叫做依赖关系(绿色框框的那个),后面的部分叫做依赖方法(红色框框的那个)。听上去十分高大尚,翻译成白话文就是:依赖关系的形成需要依赖方法中的所有文件。

make 命令会自动扫描 makefile 文件,查看当前目录下是否存在依赖方法中的所有文件,如果已经存在,那么就会执行下一行 Tab 缩进的代码(只能是 Tab 缩进)。那么如果不存在怎么办呢?

我们在讲 C 语言编译链接的时候知道:从 C 语言的源文件到生成功可执行文件是分成很多步骤的:

gcc -E
gcc -S
gcc -C

根据这个原理我们就在 makefile 文件中将那一行编译生成可执行文件的代码分成一步一步来执行。

test:test.ogcc test.o -o test
test.o:test.sgcc -c test.s -o test.o
test.s:test.igcc -S test.i -o test.s
test.i:test.cgcc -E test.c -o test.i

make 命令扫描 makefile 文件时:

  • 发现依赖关系 test 的依赖文件 test.o 在源文件的当前目录不存在,继续向下扫描。
  • 发现依赖关系 test.o 的依赖文件 test.s 在源文件的当前目录不存在,继续向下扫描。
  • 发现依赖关系 test.s 的依赖文件 test.i 在源文件的当前目录不存在,继续向下扫描。
  • 发现依赖关系 test.i 的依赖文件 test.c 在源文件的当前目录已经存在,就会执行 Tab 缩进的代码:gcc -E test.c -o test.i 生成 test.i
  • test.i 依赖文件已经存在啦,就会执行:gcc -S test.i -o test.s 生成 test.s 文件。
  • test.s 依赖文件已经存在啦,就会执行:gcc -c test.s -o test.o 生成 test.o 文件。
  • test.o 依赖文件已经存在啦,就会执行:gcc test.o -o test 生成 test 可执行文件。

在上述过程执行完成之后(使用 make 命令之后),源文件的当前目录下就会生成:test.i test.s test.o test 文件。
在这里插入图片描述
我们可以看到显示出来命令的执行顺序与我们推导的顺序是一样的哈!
综上所述:扫描 makefile 文件的时候,如果源文件的当前目录不存在依赖文件,就会递归似的向下执行,这种行为叫做 make 的自动化推导

清理可执行文件

我们在更改了源文件的代码之后,需要清除可执行文件后重新编译。那么清除可执行文件能否使用 make 命令呢?那肯定是可以的撒!

clean:rm -f test

其中,clean 是依赖关系,冒号右侧为空说明表明没有依赖的文件。那么我们应该如何使用这个依赖关系呢?
执行命令:make clean 即可。

make clean

在这里插入图片描述
我们看到顺利运行了呢!

clean 放在 makefile 文件的最开头

如果我们像这样写 makefile 文件会发生什么呢?

clean:rm -f test
test:test.cgcc -o test test.c

在这里插入图片描述
可以看到我们想要编译文件就需要使用命令:make test,而 make 命令变成了执行:rm -f test
由此可见:make 命令会从上到下扫描 makefile 文件,将扫描到的第一个依赖关系作为 make 命令的默认行为


不推荐将依赖关系 clean 放在 makefile 文件的开头


make 命令编译多个文件

多个源文件生成一个可执行程序

我们写一个代码:在 function.h 中声明一个 Add 函数,在 function.c 中实现 Add 函数,然后在 test.c 中调用 Add 函数。
function.h

#pragma once
int Add(int a, int b);

function.c

int Add(int a, int b)
{return a + b;
}

test.c

#include<stdio.h>
#include "function.h"
int main()
{int a, b;scanf("%d %d", &a, &b);printf("a + b 的结果:%d\n", Add(a, b));return 0;
}

我们想要编译 function.h function.c test.c 应该怎么做呢?其实很简单哈!

test:function.c test.cgcc -o test test.c function.c
clean:rm -f test

如果是多个源文件生成一个可执行程序,只需要在依赖文件中以空格隔开多个源文件即可。如果 .h 文件在源文件的当前目录,依赖文件中是不需要写 .h 文件的!

多个源文件生成多个可执行程序

如果在 makefile 文件的目录下有多个源文件,并且想要将这些个源文件分别编译成可执行文件应该怎么做呢?你可以先想一想🤔,你应该是有能力写出来的。
我们来写这样两个源文件:test1.ctest2.c
test1.c

#include<stdio.h>int main()
{printf("i am test1.c\n");return 0;
}

test2.c

#include<stdio.h>int main()
{printf("i am test2.c\n");return 0;
}

我们要使用 make 命令讲他们分别编译成:test1test2 两个可执行文件。makefile 文件可以这样写:

All:test1 test2
test1:test1.cgcc -o test1 test1.c
test2:test2.cgcc -o test2 test2.c
clean:rm -f test1 test2

依赖关系:All 依赖于 test1 和 test2make 命令扫描 makefile 文件,发现源文件当前目录不存在 test1 和 test2 那么就会继续向下扫描。当扫描到 test1 和 test2 这两个依赖关系,他们的依赖文件都在源文件的当前目录。可以直接执行他们 Tab 缩进的代码,生成 test1 和 test2,最后完成两个源文件的编译生成两个可执行文件。
在这里插入图片描述

我们可以看到执行 make 命令之后也是顺利生成了 test1test2 两个可执行文件了呢!

make 可以重复编译吗?为什么?

我们还是回到最开始的那个代码:
test.c

#include<stdio.h>
int main()
{printf("hello make!\n");return 0;
}

makefile

test:test.cgcc -o test test.c
clean:rm -f test

我们发现在不修改代码的情况下,是不允许二次编译的:
在这里插入图片描述
这是为什么呢?
显然是因为没有这个必要哈,既然你的源文件没有被修改为什么要为你重新编译呢?
那这个是怎么做到的呢?

  • 一般来说,我们都是先有源文件,再有可执行程序。这就意味着源文件的最近修改时间比可执行程序的最近修改时间要早。
  • 因此,我们只需要比较可执行程序的最近修改时间和源文件的最近修改时间,就可以判断源文件是否需要重新被编译啦!

🤔思考:源文件的最近修改时间会和可执行程序的最近修改时间想等吗?这个一般是不会的!😊


那么,这个用来比较的时间哪里来呢?
我们先来学习一个命令吧:这个命令可以查看一个文件的时间状态。

stat 文件

在这里插入图片描述
Access Modify Change 这三个时间称为文件的 ACM 时间。

  • Access:最近访问时间,几乎文件的任何操作之后,这个时间都会发生改变。
  • Modify:当对文件的内容做出修改之后,这个时间就会更新。
  • Change:当对文件的属性做出修改之后,这个时间就会更新。

这就意味着,一旦对文件的内容做出修改,Access Modify Change 时间都会被更新。

因为 Access 时间要被频繁被修改,在实际的实现中 Access 时间的更新是有一定的更新策略(例如:当 Modiify 或者 Change 时间到达一定的次数之后再更新 Access 时间),而不是根据 Access 时间的定义那样,操作一次文件都要更新这个时间。
原因:文件是被存放在磁盘中的,将数据刷新到磁盘的速度是比较慢的,频繁地修改 Access 时间势必会影响操作系统的效率的。

在判断源文件是否需要重新编译,就是根据源文件和可执行程序 Modify 时间的比较结果来判定的!


如何验证呢?
再来学习一个命令吧:

touch 文件名

这个 touch 命令除了能够创建一个普通文件,还有一个功能就是:当这个文件已经存在时,能更新一个文件的 ACM 时间到当前的最新时间。
因此,我们可以更新源文件的 ACM 时间到最新,使得 make 命令可以反复编译一个相同的源文件。
在这里插入图片描述我们看到,第一次可以顺利编译,这很正常。第二次使用 make 编译的时候就不能了!我们在更新源文件的 ACM 时间之后又能使用 make 编译了!由此可以验证就是通过比较源文件与可执行文件的时间来判断是否能使用 make 再次编译的!

如何让一个依赖关系一直被执行

我们上面讲了通过 touch 命令可以使用 make 一直编译。但是,还是不建议这么做,没有修改源文件就不要重复编译,这很好,不是吗!
但是清理可执行文件的依赖关系,我们就有这个需求,让他总是被执行。那么 makefile 文件应该怎么写呢?

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

makefile 文件中被 .PHONY 修饰的依赖关系就可以被一直执行啦!
你若不信,就可以给可执行文件 test 这个依赖关系加上 .PHONY 修饰,看能不能 make 重复编译(不建议这么做!!!)。

特殊符号

  • $@:表示:依赖关系:依赖方法 中冒号前面的一坨!
  • @^:表示:依赖关系:依赖方法 中冒号后面的一坨!

那么,我们写 makefile 文件就可以这么写啦:

test:test.cgcc -o $@ $^
.PHONY:clean
clean:rm -f test

在这个 makefile 文件中:$@ 就是 test$^ 就是 test.c

==这才是我们在平时用的最多的 makefile 文件的编写方法啦!==😊

取消回显

我们在使用 make 的时候是不是能看到 make 推导出来的要执行的指令的内容!像这样:
在这里插入图片描述
如果你不想回显命令,只需要在指令前面加上 @ 符号就可以啦!

test:test.c@gcc -o $@ $^
.PHONY:clean
clean:@rm -f test

在这里插入图片描述

相关文章:

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

什么是 make make 是一个命令&#xff0c;他会在源文件的当前目录下寻找 makefile 或者 Makefile 文件执行这个文件中的代码。 makefile 文件的编写 我们先来见见猪跑&#xff0c;看看 make 怎么用的&#xff1a; 下面是 makefile 文件的内容&#xff1a; 这是 test.c 中的…...

android trace文件的抓取与查看方法

本地手机抓取trace 解压android trace文件的抓取与查看方法 找到config.pbtx文件&#xff0c;连接手机push进去 # push config.pbtx &#xff0c;/data/local/tmp/为自定义push到的目录 adb push config.pbtx /data/local/tmp/ adb shell # 抓取trace&#xff0c; /data/loc…...

ffmpeg开发 环境配置

ffmpeg开发简图 1 下载ffmpeg开发包 https://ffmpeg.org/download.html 包含三个版本&#xff1a;Static、Shared以及Dev Static --- 包含3个应用程序&#xff1a;ffmpeg.exe , ffplay.exe , ffprobe.exe&#xff0c;体积都很大&#xff0c;相关的DLL已经被编译到exe里面去…...

C++STL——string类详解及其模拟实现

CSTL——string类 1. STL简介 STL全称standard template libaray&#xff0c;译为标准模板库 需要注意&#xff0c;STL不是C的标准库&#xff0c;而是C标准库的重要组成部分STL是一个包含众多数据结构和算法的软件框架 下面展示STL的六大组件&#xff1a; 本章&#xff0c;我…...

使用Three.js创建导航立方体

什么是导航立方体? 导航立方体是一个交互式的3D控件,它允许用户通过点击和拖动立方体的各个面来改变3D视图的方向。这是一种非常直观的方式,让用户能够轻松地在3D空间中导航。 创建导航立方体 下面是一个基本的步骤,说明如何使用Three.js创建一个导航立方体: // 创建场景…...

C++初识类和对象

前言 上一期我们介绍了一些C入门的基础知识&#xff0c;本期我们来介绍面向对象。初步认识一下面向对象和面向过程、类、以及封装&#xff01; 本期内容介绍 面向过程和面向对象 类的引入 类的定义 类的访问限定符和封装 类的作用域 类的实例化 类对象模型 this指针 一、面向…...

MYSQL where 子句

文章目录 前言MySQL where 子句语法 从命令提示符中读取数据使用PHP脚本读取数据后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;Mysql &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不断努力…...

系列十六、Spring IOC容器的扩展点

一、概述 Spring IOC容器的扩展点是指在IOC加载的过程中&#xff0c;如何对即将要创建的bean进行扩展。 二、扩展点 2.1、BeanDefinitionRegistryPostProcessor 2.1.1、概述 BeanDefinitionRegistryPostProcessor是bean定义的后置处理器&#xff0c;在BeanDefinition加载后&a…...

eclipse项目移到idea上部署运行

1.配置web模块 另外&#xff0c;模块这里&#xff0c;也要加上Spring 2.配置Artifact &#xff08;用于tomcat&#xff09; 就是从上面配置的web模块&#xff0c;产生的工件 3.添加lib 一般是在web-inf/lib &#xff0c; 遇到的坑&#xff1a; jdk版本问题&#xff0c;这里…...

支持向量机的算法原理

支持向量机&#xff08;Support Vector Machine&#xff0c;简称SVM&#xff09;是机器学习领域中一种常用的分类算法&#xff0c;它基于统计学习理论和结构风险最小化原则&#xff0c;具有很强的理论基础和良好的分类性能。本文将详细介绍支持向量机的算法原理&#xff0c;并解…...

gitlab 12升级14(解决各种报错问题)

1.这里是从自己公司的源下载的rpm包&#xff0c;需要换成自己的 2.从12的最后一个版本升级到14的最后一个版本 # 停服务 [rootdocker test]# gitlab-ctl stop puma && gitlab-ctl stop sidekiq && gitlab-ctl stop nginx && gitlab-ctl status# 进入…...

给element plus中动态form-item增加校验的可行方法

element plus中的form组件自带校验机制。在常规使用场景中&#xff0c;表单项是固定的、明确的&#xff0c;且数量不会太多。校验规则的使用也如下&#xff1a; <template><div class"edit-page"><el-form :model"formModel" ref"for…...

C++学习之值传递

c/c中存在三种传值方式&#xff0c;在局部函数中&#xff0c;对这三种传值方式传入的参数进行修改&#xff0c;会得到不同的结果。具体见下例&#xff1a; #include <stdlib.h> #include <stdio.h>static int dummny 10000;// 传值(传过来的是原始值的副本&#…...

网络视频播放卡顿原因分析

一、问题描述 某项目通过拉摄像机rtsp流转rtmp/http-flv/ws-flv的方案&#xff0c;使用户可以在网页中观看摄像机的视频画面。在 观看视频时偶发出现卡顿现象。 二、卡顿现象分析和解决 此问题涉及的原因较多&#xff0c;所以得考虑各环节的问题可能性&#xff0c;并根据现场实…...

Android 相机库CameraView源码解析 (二) : 拍照

1. 前言 这段时间&#xff0c;在使用 natario1/CameraView 来实现带滤镜的预览、拍照、录像功能。 由于CameraView封装的比较到位&#xff0c;在项目前期&#xff0c;的确为我们节省了不少时间。 但随着项目持续深入&#xff0c;对于CameraView的使用进入深水区&#xff0c;逐…...

计算机缺少d3dx9_43.dll怎么办?5个方法快速修复d3dx9_43.dll文件

在计算机使用过程中&#xff0c;我们常常会遇到一些错误提示&#xff0c;其中之一就是“d3dx9_43.dll丢失”。这个问题可能会影响到我们的游戏体验或者软件运行。为了解决这个问题&#xff0c;我查阅了一些资料并尝试了多种方法。在这里&#xff0c;我想分享一下我对d3dx9_43.d…...

2023亚太杯数学建模C题思路分析 - 我国新能源电动汽车的发展趋势

1 赛题 问题C 我国新能源电动汽车的发展趋势 新能源汽车是指以先进技术原理、新技术、新结构的非常规汽车燃料为动力来源( 非常规汽车燃料指汽油、柴油以外的燃料&#xff09;&#xff0c;将先进技术进行汽车动力控制和驱动相结 合的汽车。新能源汽车主要包括四种类型&#x…...

c语言新龟兔赛跑

以下是一个使用C语言编写的新的龟兔赛跑游戏&#xff1a; #include <stdio.h>#include <stdlib.h>#include <time.h>int main() { int distance, turtle_speed, rabbit_speed, turtle_time, rabbit_time, rabbit_lead; srand(time(NULL)); // 随机数种…...

Linux驱动开发——网络设备驱动(理论篇)

目录 一、前言 二、网络层次结构 三、网络设备驱动核心数据结构和函数 一、前言 网络设备驱动是 Linux 的第三大类驱动&#xff0c;也是我们学习的最后一类 Linux 驱动。这里我们首先简单学习一下网络协议层次结构&#xff0c;然后简单讨论 Linux 内核中网络实现的层次结构。…...

simulink仿真

1&#xff09;系统问题 连续系统&#xff0c;离散系统&#xff08;采样周期问题&#xff09; 系统分析问题 2&#xff09;求解器问题 变步长&#xff0c;定步长&#xff0c;步长时间与采样周期问题、 3&#xff09;积分器问题 连续积分&#xff0c;离散积分问题&#xff…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...

计算机基础知识解析:从应用到架构的全面拆解

目录 前言 1、 计算机的应用领域&#xff1a;无处不在的数字助手 2、 计算机的进化史&#xff1a;从算盘到量子计算 3、计算机的分类&#xff1a;不止 “台式机和笔记本” 4、计算机的组件&#xff1a;硬件与软件的协同 4.1 硬件&#xff1a;五大核心部件 4.2 软件&#…...

SQL Server 触发器调用存储过程实现发送 HTTP 请求

文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...

DBLP数据库是什么?

DBLP&#xff08;Digital Bibliography & Library Project&#xff09;Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高&#xff0c;数据库文献更新速度很快&#xff0c;很好地反映了国际计算机科学学术研…...

【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权

摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题&#xff1a;安全。文章将详细阐述认证&#xff08;Authentication) 与授权&#xff08;Authorization的核心概念&#xff0c;对比传统 Session-Cookie 与现代 JWT&#xff08;JS…...

Monorepo架构: Nx Cloud 扩展能力与缓存加速

借助 Nx Cloud 实现项目协同与加速构建 1 &#xff09; 缓存工作原理分析 在了解了本地缓存和远程缓存之后&#xff0c;我们来探究缓存是如何工作的。以计算文件的哈希串为例&#xff0c;若后续运行任务时文件哈希串未变&#xff0c;系统会直接使用对应的输出和制品文件。 2 …...