Linux小组件:makefile

引言:

我们在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全屏打开,不然就会

这是正常的捏,是动态的

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

好了先休息了

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值