Linux 自动化构建工具(make/Makefile)

news2024/12/23 5:29:16

绪论

        拼着一切代价,奔你的前程。——巴尔扎克. 本章继续学习Linux常用的工具,make是可以帮我们解决一些重复使用相同指令的冗杂的自动化构建工具。

话不多说安全带系好,发车啦(建议电脑观看)


附:红色,部分为重点部分;蓝颜色为需要记忆的部分(不是死记硬背哈,多敲);黑色加粗或者其余颜色为次重点;黑色为描述需要


思维导图:

要XMind思维导图的话可以私信哈


目录

1.make/Makefile

2.Linux小程序----进度条


1.make/Makefile

知识点:

make是一个指令而Makefile(makefile首字母大小写不管)是一个文件(这个文件内装着所要通过make快速执行的指令)

在Makefile内有着多个依赖关系和依赖方法,每个依赖关系都有其对应的依赖方法

使用方法:

用vim打开Makefile在文件中,写下依赖关系和依赖方法

  1. 依赖关系写在依赖方法的上边:
  2. 有依赖关系的文件用冒号(:)隔开,其中的目标文件是冒号左边的文件,而目标文件所依赖的文件放在右边,一个目标文件可以依赖于多个文件
  3. 在写依赖方法的之前必须用tab隔开一段距离后再写依赖方法
  4. 依赖方法就是指令(如gcc生成可执行程序的指令)

简单举例:

写完Makefile文件后,直接使用make指令就能使用第一个Makefile内部的依赖方法(自顶向下)

再在Makefile中写下一段依赖关系和依赖方法

此时要调用的话就需要指定依赖关系(其中目标文件clear不需要依赖任何文件所以就不用写):

指定使用的依赖关系:make clear指定到clear这个依赖关系上

细节:

依赖关系、依赖方法:

以例子来直接的表示如:mycode:mycode.c

其中,目标文件是mycode,而目标文件依赖于后面的mycode.c,

而既然有了依赖关系的话就需有他们之间的依赖方法gcc -o mycode mycode.c(这样才有意义),也就是根据依赖关系来执行依赖方法。

Makefile依赖关系的自动化推导

对于在Makefile中的依赖关系来说,假如其目标文件所依赖的文件不存在则会在Makefile中查找是否有生成该依赖文件的方法有则进行如递归式(栈式)的先形成依赖文件再返回到开始的依赖关系,反之没有形成依赖关系文件的方法则会报错。

附:

对于make来说当你进行了一次后,如果所指向的依赖关系中的文件没有被修改的话,一般来说就不会进行make指令(因为已近存在了所以编译器就选择不再执行,来提高编译效率)

那这种行为是如何实现的?:比较时间

首先了解:

在Linux中文件的时间(ACM):

Access(访问):打印、改变都算访问、增删查改(几乎任何操作)

Modify(修改文件内容)、change(改变文件属性)

其中当修改了文件内容的话文件属性也会更改(文件属性中包括文件的大小、最久的修改时间所以修改文件内容的话文件的属性也会跟着改变,同样对于Access来说也可能会更改)

查看文件的时间的指令:stat + 文件名

当我对文件进行修改过后其时间就会发生改变

当也可以发现Access并没有改变这是因为,Access因为太常用所以编译器进行了优化设置了他被使用一定次数后才会改变,当一个文件被调用很多次的时候能减少一定的损耗。

单独改变文件属性的例子:

手动更新时间指令 touch:

附加指令:

-a 

 -m 

总结:而对于make来说他是否执行依赖关系进行编译是通过判断比较的是源文件和可执行程序的modify内容修改时间的老旧()

比较源文件和可执行程序的修改时间:

  1. 源文件修改时间 老于 可执行程序的修改时间 则make不会再次执行
  2. 源文件文件修改时间 新于 可执行程序的修改时间 则make会再次执行
  3. 因为一般而言,源文件的修改时间是比可执行程序的修改早的(老的)所以此时就表示这源文件生成可执行程序后并没有被修改过,所以就不需要被再次编译。
  4. 若想不管这个时间的话可以用.PHONY来修饰(总是被执行),其中.PHONY修饰的文件被称为伪目标

特殊符号:

  1. $@:可以代替目标文件(冒号左边)、$^:可以代替目标文件所依赖的所有文件(冒号右边)所以就能改成来自动识别目标文件和所依赖的文件:
  2. 在依赖方法前面加上@:可以让依赖方法不显示

2.Linux小程序----进度条

知识点:

  1. 回车换行:
    1. 一般来说\n就直接代表着回车换行
    2. 回车、换行也能分开来看
      1. 回车:回到所在行的最开始(\r)
      2. 换行:换到下一行(就是\n)
    3. 缓冲区
      1. 对于\n来说可以用来刷新缓冲区,只有刷新缓冲区(刷新标准输出流stdout)才能拿出缓冲区的东西,如:用printf打印时会先把要打印的东西放进缓冲区内所以只有在后面加上\n才会在该语句结束时直接打印出所要打印的。反之若没有\n则会在程序结束后自动刷新缓冲区才会打印出来。
      2. 对此我们如果不想刷换行(\n)又想刷新缓冲区的话,那就可以用函数fflush(stdout)( int fflush ( FILE * stream ) 、stdout是标准输出流(一般编译器会在执行程序时会自动打开标准输入、输出、错误流))

练习(倒计时小程序):

sleep(second)函数的用处是休眠second秒、头文件:#include <unistd.h>

usleep(usec) 函数用处是休眠usec微秒(1s = 1000000um ) 、头文件...一样

#include<stdio.h>
#include<unistd.h>
int main()
{
     int count = 10; 
 
     while(count>=0)
      {
         printf("%-2d\r",count--);//%-2d 对打印的整形进行右对齐并且每次打印两个字符
         fflush(stdout);//因为没有\n所以需要自行刷新缓冲区
         sleep(1);//休眠1s
     }
     return 0;
}                                                                                                                                       

附:此程序是在Linux,gcc编译器下,

若在vs下的话需要把sleep换成Sleep(ms) 内部为毫秒 并且加上头文件#include<windows.h>

下面开始正式的来实现一个进度条:

  1. 分源管理创建文件: processBar.c、processBar.h、main.c 
  2. 创建Makefile
  3. 根据进度条的特性模仿一个进度条
    1. 方法一:单纯的实现进度条
      1. processBar.c

        #include "processBar.h"
        
        const char *lable = "|/-\\";// \ 反斜杠是特殊的字符所以需要用\\ 来表示成 \
        
        
        void processBar(int speed)
        {
            int cnt = 0;
            char bar[102];
            memset(bar,'\0',sizeof(bar));
            int len =  strlen(lable);
        
            while(cnt <= 100)
            {
                printf("[%-100s][%d%%][%c]\r",bar,cnt,lable[cnt%len]);//%-100d 先预先开辟100个字符位并且向左对齐、对于最后的旋转可以用不断遍历数组显示、%%表示%因为%是特殊字符 所以若要表示出来单独的%则需要用%% 或者 \% 
                fflush(stdout);//没有\n来刷新,所以需要自己刷新缓冲区
                bar[cnt++] = BODY;//逐渐增加的进度条
                if(cnt < 100) bar[cnt] = RIGHT;// > 在最后一个位置就不需要在出现了
                usleep(speed);//休眠多少微秒
            }
            printf("\n");
        
        }
        
      2. processBar.h

        #include<stdio.h>
        #include<unistd.h>
        #include<string.h>
        
        #define BODY '='
        
        #define RIGHT '>'
        
        
        void processBar(int speed);
        
        //extern void processBar(); extern 可写可不写因为对于函数来说这样写就表示这是一个声明
        
        //但注意的是假如是一个变量的话就需要加上extern了因为分不清到底是不是编译器声明
        
      3. main.c

        #include<stdio.h>
        #include"processBar.h"
        #include<unistd.h>
        
        int main()
        {
            processBar(50000);
        
            return 0;
        }

    2. 方法二:更好的适应正常情况下载东西时的表现
      1. processBar.c
        #include "processBar.h"
        
        const char *lable = "|/-\\";// \ 反斜杠是特殊的字符所以需要用\\ 来表示成\ 
        
        char bar[102] = {0};
        //函数processBar来展示下载的过程
        //原理和之前一样,只不过此时把逐渐完成的过程放到了外部
        void initbar()//清理数组中的内容
        {
            memset(bar,'\0',sizeof(bar));//将内容全部置为\0
        }
        
        void processBar(int rata)
        {
            if(rata < 0 || rata > 100) return ;
            int len =  strlen(lable);
            
            printf("[%-100s][%d%%][%c]\r",bar,rata,lable[rata%len]); 
            fflush(stdout);
            bar[rata++] = BODY;
            if(rata < 100) bar[rata] = RIGHT;
                
        }
        
      2. processBar.h

        #include<stdio.h>
        #include<unistd.h>
        #include<string.h>
        
        #define BODY '='
        
        #define RIGHT '>'
        
        typedef void (*processbar)(int);//将函数指针类型重命名为processbar 然后利用函数指针来实现一个回调函数的过程
        
        void processBar(int rata);
        
        //extern void processBar(); extern 可写可不写因为对于函数来说这样写就表示这是一个声明
        
        //但注意的是假如是一个变量的话就需要加上extern了因为分不清到底是不是编译器声明
      3. main.c

        #include<stdio.h>
        #include"processBar.h"
        #include<unistd.h>
        // 模拟 下载 的情况
        void DownLoad(processbar i)//利用函数指针
        {
            int total = 1000;//假如有1000MB
            int cur = 0;//一开始从0开始
            while(cur <= total)
            {
                int rata  = cur*100 / total;//计算完成的百分比
                i(rata);//把rata传给i 也就是 processBar
                cur += 10;//模拟下载逐渐完成
                usleep(50000);//模拟下载的所需的时间
            }
            printf("\n");
        }
        
        int main()
        {
            DownLoad(processBar);
            initbar();//清理一下数组内容,为下面处理时腾出空的空间
            return 0;
        }
        

本章完。预知后事如何,暂听下回分解。

如果有任何问题欢迎讨论哈!

如果觉得这篇文章对你有所帮助的话点点赞吧!

持续更新大量Linux细致内容,早关注不迷路。

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

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

相关文章

TortoiseGit 入门指南08:浏览引用以及在引用间切换

在上一节 创建分支 中&#xff0c;我们学会了在分支上开发新功能&#xff0c;那么随之而来的问题是&#xff1a;如何查看项目又多少分支&#xff1f;如何再切换到主分支&#xff1f;这节来解决这些问题。 在回答之前&#xff0c;需要先了解一个 Git 术语&#xff1a;引用&…

学无止境·MySQL⑨(MongoDB)

MongoDB的安装及使用 MongoDB1、MongoDB的安装与启动2、创建一个数据库 名字grade3、数据库中创建一个集合名字 class4、集合中插入若干数据文档格式如下5、查找查看班级所有人信息查看班级中年龄为8岁的学生信息查看年龄大于10岁的学生信息查看年龄在 4---8岁之间的学生信息找…

接口测试之基于SaaS平台的iHRM项目的前端部署配置(踩坑版)

基于SaaS平台的iHRM项目的前端部署配置 下载安装node.js3.从Git上下载项目安装依赖包的改进方法 关于的部署可以参考基于SaaS平台的iHRM项目的前端部署教程博客 但本人在参考配置的过程中遇到了很多问题…于是写下这篇博客 下载安装node.js 可以访问https://nodejs.org/en下载…

利用Python绘制直方图和散点图

1 问题 利用python如何绘制直方图和散点图。 2 方法 # ------ 直方图import matplotlib.pyplot as pltimport numpy as npimport matplotlib# 设置matplotlib正常显示中文和负号matplotlib.rcParams[font.sans-serif] [SimHei] # 用黑体显示中文matplotlib.rcParams[axes.unic…

前端学习记录~2023.7.3~CSS杂记 Day4

前言一、溢出1. 默认情况2. overflow属性3. BFC 二、CSS 的值与单位1. 长度&#xff08;1&#xff09;绝对长度单位&#xff08;2&#xff09;相对长度单位 2、百分比3、数字4、颜色&#xff08;1&#xff09;颜色关键字&#xff08;2&#xff09;十六进制 RGB 值&#xff08;3…

单片机第一季:零基础7——定时器和计时器

目录 1&#xff0c;单片机定时器原理 2&#xff0c;51单片机定时器/计数器结构 3&#xff0c;定时器配置 4&#xff0c;示例代码-通过定时器控制LED灯间隔1s闪烁 51 单片机有两组定时器/计数器&#xff0c;因为既可以定时&#xff0c;又可以计数&#xff0c;故称之为定时…

JPA实现多对多关系

本文已收录于专栏 《Java》 目录 概念说明优势利弊实现方式通过两个ManyToMany注解实现类图代码 通过OneToMany和ManyToOne注解实现类图代码 总结提升 概念说明 多对多关系是指两个实体之间存在多对多的关联关系。在数据库中&#xff0c;多对多关系无法直接表示&#xff0c;需要…

javascript截取两个符号之间的字符串(2):lastIndexOf匹配和正则表达式匹配

lastIndexOf匹配和正则表达式匹配 项目需求1.规范的字符串2.不规范的字符串3-1.万能封装3-2.ChatGPT的优化写法4.正则表达式的用法5.补充知识&#xff1a;lastIndexOf的用法 项目需求 javascript中截取字符串中最后一个“/”和“?”之间的内容。 1.规范的字符串 https://tes…

100天精通Python(可视化篇)——第96天:Pyecharts绘制多种炫酷箱形图参数说明+代码实战

文章目录 专栏导读1. 箱形图介绍1&#xff09;箱形图介绍2&#xff09;怎么看箱型图&#xff1f;3&#xff09;解释说明 2. 普通箱型图3. 水平箱型图4. 群组箱型图5. 带异常点的箱型图 专栏导读 &#x1f525;&#x1f525;本文已收录于《100天精通Python从入门到就业》&#…

Docker实战总结

Docker 官方文档地址:https://www.docker.com/get-started 中文参考手册:https://docker_practice.gitee.io/zh-cn/ 1.什么是 Docker 1.1 官方定义 最新官网首页 # 1.官方介绍 - We have a complete container solution for you - no matter who you are and where you are…

线性回归--波士顿房屋价格预测

一、波士顿房屋价格预测代码 import sysimport pandas as pd import numpy as np from sklearn.linear_model import LinearRegression import matplotlib.pyplot as plt import matplotlib as mpt from sklearn.model_selection import train_test_split##加载数据 datapd.re…

Druid工作原理

Druid工作原理-连接池初始化流程 标题 Druid工作原理-获取连接流程&#xff1a; 连接回收:

类定义练习

运行代码&#xff1a; //类定义练习 #include"std_lib_facilities.h" #include"GUI/Simple_window.h" #include"GUI/GUI.h" #include"GUI/Graph.h" #include"GUI/Point.h" //定义类 class Person { private:string first_n…

直流有刷驱动板电流电压采集

直流有刷驱动板电流电压采集 电流采集会涉及到电流环的使用。 #include "./adc/bsp_adc.h" #include ".\motor_control\bsp_motor_control.h" #include "./led/bsp_led.h" #include "./usart/bsp_debug_usart.h"__IO uint16_t…

vue-element-admin深入系列-数据Mock

本文介绍 vue-element-admin 如何使用 MockJS 实现数据Mock,毕竟对于一个合格的前端来讲,自己能Mock数据是必须的: 自给自足,不用依赖服务端接口。毕竟环境问题是个大问题,更何况环境不稳定是常态,再加上偶尔服务端的数据格式变化,问题就更复杂。 数据复杂,页面复杂时。…

HackTheBox - 学院【CPTS】复习6 - Active Directory

ACTIVE DIRECTORY ENUMERATION & ATTACKS 这个模块其实与thm的AD教程相比&#xff0c;还是thm更适合刚开始接触AD以及学习从枚举到持久化的全阶段&#xff08;红队&#xff09;。而htb学院这个模块更注重于枚举和各种攻击手段&#xff0c;有点纯渗透的风格&#xff0c;弱化…

通过动态IP解决网络数据采集问题

动态地址的作用 说到Python网络爬虫&#xff0c;很多人都会遇到困难。最常见的就是爬取过程中IP地址被屏蔽。虽然大部分都是几个小时内自动解封的&#xff0c;但这对于分秒必争的python网络爬虫来说&#xff0c;是一个关键性的打击&#xff01;当一个爬虫被阻塞时&#xff0c;…

Java内部类笔记

1.为什么使用内部类? 使用内部类最吸引人的原因是&#xff1a;每个内部类都能独立地继承一个&#xff08;接口的&#xff09;实现&#xff0c;所以无论外围类是否已经继承了某个&#xff08;接口的&#xff09;实现&#xff0c; 对于内部类都没有影响 1.1.使用内部类最大的优点…

TCP/IP基础知识笔记

应用层&#xff1a;为用户提供应用功能&#xff0c;比如 HTTP、FTP、Telnet、DNS、SMTP等。 应用层是工作在操作系统中的用户态&#xff0c;传输层及以下则工作在内核态。 传输层&#xff1a;为应用层提供网络支持。 *TCP包含众多特性比如流量控制、超时重传、拥塞控制等因此可…

pytorch深度学习逻辑回归 logistic regression

# logistic regression 二分类 # 导入pytorch 和 torchvision import numpy as np import torch import torchvision from torch.autograd import Variable import torch.nn as nn import torch.nn.functional as F import torch.optim as optim import matplotlib.pyplot as …