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.o
	gcc test.o -o test
test.o:test.s
	gcc -c test.s -o test.o
test.s:test.i
	gcc -S test.i -o test.s
test.i:test.c
	gcc -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.c
	gcc -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.c
   gcc -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.c
	gcc -o test1 test1.c
test2:test2.c
	gcc -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.c
	gcc -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.c
	gcc -o test test.c
.PHONY:clean
clean:
	rm -f test

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

特殊符号

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

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

test:test.c
	gcc -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

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue19 列表过滤

直接上代码 以下代码使用了两种实现方式&#xff0c;监视属性和计算属性 当能用计算属性实现时&#xff0c;推荐使用计算属性 <!DOCTYPE html> <html><head><meta charset"UTF-8" /><title>列表过滤</title><script type&q…

python项目报错

解决办法&#xff1a;不要用配置的镜像脚本&#xff0c;直接用此命令 pip install pandas -i http://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com

C++类与对象(7)—友元、内部类、匿名对象、拷贝对象时编译器优化

目录 一、友元 1、定义 2、友元函数 3、友元类 二、内部类 1、定义 2、特性&#xff1a; 三、匿名对象 四、拷贝对象时的一些编译器优化 1、传值&传引用返回优化对比 2、匿名对象作为函数返回对象 3、接收返回值方式对比 总结&#xff1a; 一、友元 1、定义…

Javaweb之Vue组件库Element之Form表单的详细解析

4.3.4 Form表单 4.3.4.1 组件演示 Form 表单&#xff1a;由输入框、选择器、单选框、多选框等控件组成&#xff0c;用以收集、校验、提交数据。 表单在我们前端的开发中使用的还是比较多的&#xff0c;接下来我们学习这个组件&#xff0c;与之前的流程一样&#xff0c;我们首…

深入了解MD5加密技术及其应用与局限

一、MD5简介 MD5&#xff08;Message Digest Algorithm 5&#xff09;是一种单向散列函数&#xff0c;由美国密码学家罗纳德李维斯特&#xff08;Ronald Linn Rivest&#xff09;于1991年发明。它主要用于将任意长度的消息映射成固定长度的摘要&#xff0c;从而实现消息的完整…

20分钟拥有自己的ChatGPT4,高效低成本,小白必看

准备工作 1、准备一个3.5的账号 2、一张虚拟卡 开始步骤 从ChatGPT第一版发布到现在&#xff0c;还不到一年的时间中&#xff0c;可是它使用的GPT架构已经从3.5版本进化到现在的4.0版本&#xff0c;随之而来的是其能力的极大提升。下面是GPT-4在其官网的介绍中的一句话&…

java基础进阶-线程池

1、线程池 线程池就是一个可以复用线程的技术。 2、应用场景 用户每发起一个请求&#xff0c;后台就需要创建一个新线程来处理&#xff0c;下次新任务来了肯定又要创建新线程处理的&#xff0c;而创建新线程的开销是很大的&#xff0c;并且请求过多时&#xff0c;肯定会产生大…

Python数据预处理详解

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 数据预处理是数据科学中至关重要的步骤&#xff0c;它包括清洗、转换、归一化等操作&#xff0c;以使数据适合于机器学习模型的使用。Python提供了多种强大的库和工具&#xff0c;能够帮助进行数据预处理。本文将…

C语言枚举的作用是什么?

我在知乎上看到这个问题&#xff0c;一开始&#xff0c;也有一些疑惑&#xff0c;后面查了一些资料&#xff0c;对于这个问题&#xff0c;简单的说一下我的看法。 枚举有多大 枚举类型到底有多大&#xff0c;占多少空间呢&#xff1f;这个要具体情况具体分析&#xff0c;编译器…

第八节HarmonyOS @Component自定义组件的生命周期

在开始之前&#xff0c;我们先明确自定义组件和页面的关系&#xff1a; 1、自定义组件&#xff1a;Component装饰的UI单元&#xff0c;可以组合多个系统组件实现UI的复用。 2、页面&#xff1a;即应用的UI页面。可以由一个或者多个自定义组件组成&#xff0c;Entry装饰的自定…

LeetCode Hot100 108.将有序数组转为二叉搜索树

题目&#xff1a; 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。 方法&#xff1a; class Solution {public…

深信服防火墙设置应用控制策略(菜鸟必看)

PS&#xff1a;前几天发布了关于深信服防火墙路由部署的流程&#xff1a;深信服防火墙路由模式开局部署-手把手教学&#xff08;小白篇&#xff09;-CSDN博客 昨天晚上有csdn的朋友联系我&#xff0c;说有一个关于ACL访问的问题要帮忙看一下 解决了以后&#xff0c;写个大概的…

Week-T11-优化器对比试验

文章目录 一、准备环境二、准备数据三、搭建训练网络三、训练模型&#xff08;1&#xff09;VSCode训练情况&#xff1a;&#xff08;2&#xff09;jupyter notebook训练情况&#xff1a; 四、模型评估 & 模型预测1、绘制Accuracy-Loss图2、显示model2的预测效果 五、总结1…

C++ 泛型编程,函数模版和类模版

1.泛型编程 泛型编程&#xff1a;编写与类型无关的通用代码&#xff0c;是代码复用的一种手段。模板是泛型编程的基础 就比如说活字印刷术&#xff0c;就是提供一个模具&#xff0c;然后根据模具来印刷出不同的字。 泛型编程跟着类似&#xff0c;提供一个模版&#xff0c;根据这…

五、Microsoft群集服务(MSCS)环境的搭建

一、【目的】 学会利用Windows Server布置群集环境。 二、【设备】 FreeNAS11.2&#xff0c;Windows Server 2019 三、【要求】 学会利用Windows Server布置群集环境&#xff0c;掌握处理问题的能力。 配置表&#xff1a; 节点公网IP(public)内网IP(private)群集IP(clust…

欧拉LINUX 23.09版本上安装ORACLE 19c

前面解决了在RHEL9上安装ORACLE 19C的问题后&#xff0c;发现龙蜥 LINUX23 上可以安装ORACLE19C,网上搜了一下&#xff0c;欧拉 linux 22.03 上&#xff0c;没有成功安装ORACLE 19c 的先例&#xff0c;23.09就更不用说了&#xff0c;但看到的错误&#xff0c;不外服都是缺 libp…

玻色量子真机测试完整报告

​ 真机测试 2023年 2023.8 量子计算突破云渲染资源调度&#xff01;真机测试完整报告公开&#xff01; 2023.8 量子计算突破金融信用评分&#xff01;真机测试完整报告公开&#xff01; 组合优化问题专题 2023年 2023.7 玻色量子“揭秘”之旅行商问题与Ising建模 2023.…

OPENWRT解决配置pppoe后无法光猫路由管理界面

一、新建一个wan口 二、设置流量转发 设置完成后保存应用即可

pdf加密文件解密(pdf文件解密小工具)

工具放在文章末尾&#xff01; 1.pdf文件加密后会有很多使用权限的限制很不方便&#xff0c;只要是为了pdf的数据不被二次利用&#xff0c;未加密的pdf功能都是可以正常使用的 2.加密后的pdf使用权限会被限制部分 3.工具只能解决pdf编辑等加密情况&#xff0c;不能解决文件打…

耗时一个星期整理的APP自动化测试工具大全

在本篇文章中&#xff0c;将给大家推荐14款日常工作中经常用到的测试开发工具神器&#xff0c;涵盖了自动化测试、APP性能测试、稳定性测试、抓包工具等。 一、UI自动化测试工具 1. uiautomator2 openatx开源的ui自动化工具&#xff0c;支持Android和iOS。主要面向的编程语言…