Linux小组件:makefile

news2024/11/23 21:37:50

引言:

我们在Windows下编程时使用vs这种集成开发环境,里面什么编译运行调试清理等等服务都被一连串打包好了。在Linux下怎么实现呢?使用我们伟大的makefile

makefile是Linux下的一个工具,通过文本编辑器vim对文件内容编辑来操作该工具。

平常我们创建一个C语言文件,我们需要编辑它,然后编译,生成可执行文件,然后如果生成的结果错了我们还要重新编辑C语言文件,生成一个新的可执行文件等等,如果我们的工程同时有很多个C语言项目,那不是有很多重复的步骤吗?

我们用vim在一个文件里把我们需要的指令啊全部写进去,变成一个指令集。就像写代码会把函数的调用写在一个包里一样,我们只要输入一个指令就可以少写很多指令。我们的每一个项目都有不同的要求,所以我们要写不同的指令集来对应不同的项目

Target(目标) : prerequisites(依赖)
	Command(命令)

eg:
app:main.o fun.o
	gcc main.o fun.o -o app
main.o:main.c
	gcc -c main.c -o main.o -I ./inc
fun.o:fun.c
	gcc -c fun.c -o fun.o -I ./inc

#1.第一行即eg中的“app”为终极目标,下面的所有目标都是为了生成这个终极目标而编写
#2.第一行的依赖是指你的目标文件是和“依赖”有关系的,一个文件可以和很多个文件有依赖关系,用空格来分开,如:test.c test1.c test2.c
#3.当时间不对时,需要将时间调整正确之后才能使用 make 命令。
#4.makefile根据时间信息判断是否执行编译(目标文件与最终生成文件进行时间对比)。
#5.每个指令集中的目标,都可以是一个文件,也可以是一个标签,标签作为第一个会一直执行。标签不是实际的文件;(还没学到)
#6.每一个规则中的目标,不一定要有依赖。
#7.每一个规则,不一定非得有命令列表。
#8.每个规则中可以有多条命令规则,但是前面都得需要加 Tab 键。

好吧这么多规矩很容易看不懂,一定要每一步都自己实践一下

运行过程:

首先我们创建一个文件,这个文件的文件名就叫makefile/Makefile

默认的情况下,make 命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件。在这三个文件名中,最好使用“Makefile”这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。最好不要用“GNUmakefile”,这个文件是 GNU 的 make 识别的。有另外一些 make 只对全小写的“makefile”文件名敏感,但是基本上来说,大多数的 make 都支持“makefile”和“Makefile”这两种默认文件名。
  寻找顺序:“GNUmakefile” > “makefile” > “Makefile(推荐)”
  但是你可以指定执行某一个的MakeFile,使用make的 “-f” 和 “–file” 参数即可。比如:make -f Make.Linux 或 make --file Make.AIX

首先我们要先有一个自己的C语言文件来实操

touch test.c

然后在里面写上东西

然后用vim来写我们刚创建的makefile文件,写下图里的东西

图上代码的第一行的意思是:一个文件叫mytest,他完全依赖于test.c文件

第二行是说编译test.c文件生成的可执行文件为mytest

好了我们的指令集写好了,怎么使用?

在命令行界面输入make这一个单词,就执行了里面的命令,如下图所示:

你看,mytest是不是出现了。再把这个加进去

.PHONY:clean 
clean:
rm -f mytest

然后输入:make clean

就删除了刚刚生成的文件

make clean两个单词整体是一个命令(不要像我一样傻傻的以为make命令是执行所有命令的,那样的话make不久无法实现指令分组了吗。。)

这个.PHONY是一个声明。有时候你的文件名可能和指令名字重复,比如你的当前目录下如果有一个叫clean的文件,Linux就不会执行你这条命令(在它眼里clean已存在,没必要执行),。PHONY的意义就是声明:我这里的clean是个命令,不是文件哦

在Linux下,这类命令的专业名词叫伪目标

当你重复输入了make指令,执行里面<生成文件>的命令,但是文件已经生成了。我们makefile主打的就是省事,比如我们前面提到的生成执行文件再修改源码,我们的makefile会判断源码最近修改时间生成执行文件的时间谁更新,如果经过对比后我们makefile发现我们的源码修改过了,就会自动在旧文件上覆盖新的;如果没有改变源文件,则就会出现上图的提示:你的“mytest"取决于时间(五毛翻译)

输入

stat 文件名//查看文件属性

来认识一下makefile里面的内置符号:

$:取内容

@:目标文件   

^:依赖文件列表

就像占位符,makefile会自己把代替的变量带进去的

注释用:#

依赖关系可以有好几组,a依赖b,b依赖c......这样子,依赖文件前必须是Tab键 

在makefile下只要全部写上去就好,makefile是会自己整合这个依赖关系的

makefile/make会自动根据文件中的依赖关系,进行自动推导(入栈入栈入栈,出栈出栈出栈),帮助我们执行所有相关的依赖方法 

tips:makefile支持乱序,但要把最终要形成的文件放在最前面

code.exe:code.o//在最上面
   gcc code.o -o code.exe
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.i code.s code.o code.exe

就可以删除刚刚生成的文件了

在makefile中支持定义变量,格式是这样的

变量名=变量内容

上图的意思和下图的意思其实差不多捏

这个就跟宏定义差不多,在改文件的时候可以一键替换,而且以后很多地方会用到比较方便

如果不想让它打印命令就可以在前面加个@

就像这样:打印的时候显示器就不会把你的命令再打印一遍

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

如果想要打印提示信息,就加上下面那句:

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

总结一下:make的运行规则是:从上到下扫描,默认形成第一个目标文件(在默认情况下只执行一对依赖关系和依赖方法)

依赖关系就是,最终的文件按照依赖文件列表依赖方法来形成可执行程序(make会根据makefile的内容,完成编译、清理工作,关系是一种联系,方法就是一种两者间的诉求

再看这张图是不是大部分问题都解决了

Target(目标) : prerequisites(依赖)
	Command(命令)

eg:
app:main.o fun.o
	gcc main.o fun.o -o app
main.o:main.c
	gcc -c main.c -o main.o -I ./inc
fun.o:fun.c
	gcc -c fun.c -o fun.o -I ./inc

#1.第一行即eg中的“app”为终极目标,下面的所有目标都是为了生成这个终极目标而编写
#2.第一行的依赖是指你的目标文件是和“依赖”有关系的,一个文件可以和很多个文件有依赖关系,用空格来分开,如:test.c test1.c test2.c
#3.当时间不对时,需要将时间调整正确之后才能使用 make 命令。
#4.makefile根据时间信息判断是否执行编译(目标文件与最终生成文件进行时间对比)。
#5.每个指令集中的目标,都可以是一个文件,也可以是一个标签,标签作为第一个会一直执行。标签不是实际的文件;(还没学到)
#6.每一个规则中的目标,不一定要有依赖。
#7.每一个规则,不一定非得有命令列表。
#8.每个规则中可以有多条命令规则,但是前面都得需要加 Tab 键。

缓冲区

前情提要:老式打字机在打完一行的时候,红色的是换行,蓝色的是回车

写完以后想再从左边打字,就要把纸往上拉,换新的一行;然后把打字的滑块滑到最左边,叫回车

在计算机里只想回车是\r,当\r存在时(也就是\r\n的时候,\n才表示换行),这个只想回车就代表着你可以在同一行一直刷新

那么缓冲区是什么?

缓冲区其实就是一块内存空间,将缓存区内容刷新前的东西都放到缓存区,然后再刷新到显示器上

程序要结束时一般要强制刷新缓冲区,而\n也可以率先你缓冲区,可以进行行刷新,就是刷新一行

缓冲区满了也会进行刷新

eg:

#include <stdio.h>
#include<unistd.h>
int main()
{
	printf("hello world!!!\n");
	sleep(3);
	return 0;
}
//效果:先打印hello world,再休眠
#include <stdio.h>
#include<unistd.h>
int main()
{
	printf("hello world!!!");
	sleep(3);
	return 0;
}
//先休眠三秒,再打印

上面的原理很好理解,下面的是为什么呢?程序是怎么运行的呢?

程序永远是自上而下的执行的,也就是说执行sleep的时候,已经执行完printf了;那执行完了我怎么没看见?

因为上面的有\n,进行了行刷新,把缓冲区的hello world打印出来了

而下面的没有行刷新,直到程序结束,才开始刷新缓冲区,所以是先睡觉再打印。

程序为什么还要有缓冲区和刷新这两个概念?

缓冲区是为了提高效率,向外刷新的次数越少,效率就越高

#include<stdio.h>
#include<unistd.h>
int main()
{
	int cnt = 9;
	while (cnt >= 0)
	{
		printf("倒计时:%d\r",cnt);
		fflush(stdout);
		cnt--;
		sleep(1);
	}
	return 0;
}
//一个可以倒计时的代码,应用刚才学的缓冲区概念

fflush

fflush()函数:更新缓存区。
头文件:#include<stdio.h>

函数定义:int fflush(FILE *stream);

函数说明:调用fflush()会将缓冲区中的内容写到stream所指的文件(如果文件是显示器,就可以写在显示器上)中去.若stream为NULL,则会将所有打开的文件进行数据更新。

其实C程序在启动的时候会默认启动三个输入输出流,他们分别是:

extern FILE* stdin;           //键盘
extern FILE* stdout;          //显示器,可以打印到显示器上
extern FILE* stderr;           //标准错误,收集错误,也属于显示器

为什么一定要打开他们三个输入输出流?

首先,打开文件是编译器和系统帮忙做的,在源代码编译时添加了一部分代码,打开键盘显示器上的代码,计算机的作用是计算,要让用户把自己的数据输入进来,经过计算后显示到显示器上让人看到结果(方便用户输入输出

不回显说明:键盘和你交互了,没把数据给显示器

字符设备

#include<stdio.h>
#include<unistd.h>
int main()
{
	int cnt = 10;
	while (cnt >= 0)
	{
		printf("倒计时:%d\r",cnt);
		fflush(stdout);
		cnt--;
		sleep(1);
	}
	return 0;
}

按上面的代码,显示器就会显示为10,90,80,70......为什么会这么显示?

因为我们之前说显示器是字符设备,上面的东西默认打出来都是字符。在\r行刷新时,把"0"前面的东西刷新了,0没刷新,所以它作为一个字符一直放在那里。如果不是0是别的数字,也是会这么一直放在显示器上面的

这么写就可以正常显示

#include<stdio.h>
#include<unistd.h>
int main()
{
	int cnt = 10;
	while (cnt >= 0)
	{
		printf("倒计时:%2d\r",cnt);
		fflush(stdout);
		cnt--;
		sleep(1);
	}
	return 0;
}

进度条

先创建一个新目录,我们整点新活

mkdir proccess
cd proccess
touch Processbar.c
touch Processbar.h
touch Main.c
touch makefile

这是一个进度条,有图形化显示进度,有百分比,我们也写一个

Processbar.h
#pragma once //防止头文件重复展开

#include<stdio.h>

void ProcBar();

Processbar.c

#include"Processbar.h"
#include<string.h>
#include<unistd.h>
#define length 101
#define Style '#'
 
const char* lable="|/-\\";
void ProcBar()
{
        char bar[length];
        memset(bar,'\0',sizeof(bar));
        int len=strlen(lable);
        int cnt=0;
        while(cnt<=100)
        {
                printf("[%-100s][%3d%%][%c]\r",bar,cnt,lable[cnt%len]);
                fflush(stdout);
                bar[cnt++]=Style;
                usleep(20000);
        }
        printf("\n");
        return;
}
Main.c
#include"Processbar.h"
int main()
{
        ProcBar();
        return 0;
}
makefile
processbar:Main.c Processbar.c
	gcc -o $@ $^
 
.PHONY:clean
clean:
	rm -f processbar

使用的时候记得把你的shell全屏打开,不然就会

这是正常的捏,是动态的

进度条是要跟随一些操作进行的,比如下载时候的进度条

好了先休息了

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

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

相关文章

Linux内核编程(十一)设备模型

本文目录 一、知识点1. 设备模型2. sysfs 文件系统3. kobject、kset设备模型框架 二、kobject实验1. 创建kobject2. 释放kobject★示例 三、kset实验1. 创建kset2. 注销kset★示例 四、引用计数器1. 概念2. 为什么要引入引用计数器&#xff1f;3. 常用函数&#xff08;1&#x…

【Nuxt】发送请求

概述 以下方式只能在 setup / 生命周期钩子 里面使用。 useFetch 下面的 API / hooks 具体用法查看官网文档。 const BASE_URL http://codercba.com:9060/juanpi/api;// 1. $fetch server and client // $fetch(BASE_URL /homeInfo, { // method: GET // }).then(res &…

python爬虫04 | Reuqests库快速入门,干穿urllib

文章目录 Requests库简介提出请求响应内容二进制响应内容JSON 响应内容原始响应内容自定义标头更复杂的 POST 请求POST 多部分编码的文件响应状态代码响应标头Cookie重定向和历史记录超时错误和异常 Ending Requests库简介 什么是Requests库 Requests是一个简单易用的HTTP库&…

分享一个基于SpringBoot和Vue的闲置物品交易与物品租赁平台(源码、调试、LW、开题、PPT)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人 八年开发经验&#xff0c;擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等&#xff0c;大家有这一块的问题可以一起交流&…

人工智能计算机视觉先锋——OpenCv 的颜色检测

红色 在计算机的世界里&#xff0c;只有 0 或者1&#xff0c;如何让计算机认识颜色是计算机视觉工作者首先需要考虑的事情&#xff0c;我们知道整个世界的颜色虽然五彩缤纷&#xff0c;但是都是3种原色彩合成的&#xff08;R G B&#xff09;&#xff0c;有了&#xff08;R G …

C语言 | Leetcode C语言题解之第327题区间和的个数

题目&#xff1a; 题解&#xff1a; #define FATHER_NODE(i) (0 (i) ? -1 : ((i) - 1 >> 1)) #define LEFT_NODE(i) (((i) << 1) 1) #define RIGHT_NODE(i) (((i) << 1) 2)/* 优先队列&#xff08;小根堆&#xff09;。 */ typedef s…

数字人解决方案——音频驱动机器人

音频集成 机器人 标志着 人工智能&#xff08;AI&#xff09;。 想象一下&#xff0c;机器人可以通过视觉和听觉导航并与周围环境互动。音频驱动的机器人使这成为可能&#xff0c;提高了它们更高效、更直观地执行任务的能力。这一发展可能会影响到各个领域&#xff0c;包括家庭…

解决PermissionError: [Errno 13] Permission denied: “xx“报错

这个报错我是使用 shutil.copy(src_file, dst_file) 语句产生的&#xff0c;因此有些问题我会围绕此句代码来进行解决&#xff0c;如果有更好的建议&#xff0c;欢迎积极留言。 目录 1.路径拼写错误&#xff0c;建议使用绝对路径 2.此文件正在使用&#xff0c;关闭当前打开的…

vba 保存word里面的图片_1分钟批量处理100张图片,有Word在

天下苦Word久矣&#xff01;Word不仅是个码字工具&#xff0c;还是个排版工具&#xff0c;而Word在排版方面经常遇到的问题&#xff0c;恐怕说个三天三夜都说不完&#xff01; 好不容易做完了100页的活动方案&#xff0c;交到处女座上司那里&#xff0c;他告诉我&#xff1a;“…

调用azure的npm实现outlook_api模拟查看邮件、发送邮件(实现web版接受outlook邮件第一步)

文章目录 ⭐前言⭐注册azure应用&#x1f496;添加权限 ⭐调用npm 实现收发邮件&#x1f496;安装依赖&#x1f496;创建appSettings.js 放置密钥&#x1f496;创建graphHelper.js封装功能&#x1f496;主文件index.js 对外暴露&#x1f496;效果 ⭐结束 ⭐前言 大家好&#x…

我的cesium for UE踩坑之旅(蓝图、UI创建)

我的小小历程 过程创建对应目录&#xff0c;并将要用到的图片、资源放入对应目录下内容浏览器 窗口中右键&#xff0c;创建一个控件蓝图&#xff0c;用来编辑界面UI绘制画布面板&#xff08;canvas&#xff09;调整整体布局加入对应的控件将UI加入到关卡中 备注搜索不到 Add To…

【原创】简易学生成绩查询系统Excel版

简易学生成绩查询系统通常是为了方便学校、教师和学生能够快速查询和管理成绩而设计的一种工具。从之前提供的信息来看&#xff0c;我们可以总结出简易学生成绩查询系统的一些常见功能&#xff1a; ### 易查分成绩查询系统功能特点&#xff1a; - **成绩导入与管理**&#xff…

Spark_获取id对应日期的所在月份的天数完整指南

开发背景 前段时间有一个开发需求的一小块用到了这&#xff0c;是一个利用率的计算。规则是某id下的近半年的值的小时利用率。 计算规则是某值除以近半年 天数以及24h,但是月份里面数据有空值&#xff0c;所以要计算一下id对应的月份的天数&#xff0c;并且过滤掉数据有空值的天…

Azure openai connection with javascript

题意&#xff1a;使用JavaScript与Azure OpenAI进行连接 问题背景&#xff1a; I have created my chatbot with javascript and used open ai. I need to change it to azure open ai but can not find the connection details for javascript. This is how i connect with p…

十九、虚拟机VMware Workstation(CentOSDebian)的安装

目录 &#x1f33b;&#x1f33b; 一、安装 VMware Workstation1.1 安装 VMware Workstation1.2 虚拟机上安装 CentOS1.3 虚拟机安装 Debian 二、配置Debian方便第三方工具远程连接2.1 配置debian2.2 安装远程SSH工具并连接 一、安装 VMware Workstation 官网下载 本地资源库…

端到端自动驾驶:终局还是误区?

近年来&#xff0c;端到端自动驾驶技术成为了汽车行业的热议话题。尤其是在2024年&#xff0c;各家新兴车企纷纷打出端到端的旗号&#xff0c;似乎谁没有搞端到端&#xff0c;就会被市场淘汰。然而&#xff0c;端到端自动驾驶真的是自动驾驶技术的终局吗&#xff1f;本文将深入…

使用QML的ListView自制树形结构图TreeView

背景 感觉QML自带的TreeView不是很好用&#xff0c;用在文件路径树形结构比较多&#xff0c;但是想用在自己数据里&#xff0c;就不太方便了&#xff0c;所以自己做一个。 用‘ListView里迭代ListView’的方法&#xff0c;制作树形结构&#xff0c;成果图&#xff1a; 代码…

尚硅谷谷粒商城项目笔记——四、使用docker安装redis【电脑CPU:AMD】

四、使用docker安装redis 注意&#xff1a; 因为电脑是AMD芯片&#xff0c;自己知识储备不够&#xff0c;无法保证和课程中用到的环境一样&#xff0c;所以环境都是自己根据适应硬件软件环境重新配置的&#xff0c;这里的虚拟机使用的是VMware。 在解决了 Docker 安装的问题之…

app逆向抓包技巧:SSL Pinning检测绕过

本篇博客旨在记录学习过程&#xff0c;不可用于商用等其它途径 场景 在charles抓包下&#xff0c;某斑马app在注册时发现点击登录毫无反应&#xff0c;看抓包结果提示SSL handshake with client failed&#xff0c;确定是触发了SSL/TLS Pinning&#xff08;证书锁定&#xff…

Flutter 正在迁移到 Swift Package Manager ,未来会弃用 CocoaPods 吗?

什么是 Swift Package Manager &#xff1f;其实 Swift Package Manager (SwiftPM) 出现已经挺长一段时间了&#xff0c;我记得第一次听说 SwiftPM 的时候&#xff0c;应该还是在 2016 年&#xff0c;那时候 Swift 3 刚发布&#xff0c;不过正式出场应该还是在 2018 年的 Apple…