我与Linux的爱恋:自动化构建工具-make/Makefile
🔥个人主页:guoguoqiang. 🔥专栏:Linux的学习
文章目录
- 背景
- makefile的使用
- .PHONY
- makefile中常用选项
- makefile的自动推导
- 进度条程序
- 倒计时程序
- 实现进度条
背景
-会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力
- 一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的
规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂
的功能操作 - makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编
译,极大的提高了软件开发的效率。 - make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命
令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一
种在工程方面的编译方法。 - make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。
makefile的使用
makefile主要由两部分组成 1.依赖关系 2.依赖方法
首先我们要创建打开makefile文件
vim makefile
打开后开始写入内容
再查看一下text.c中的内容
查看当前目录后执行make,如何发现mytest文件,然后执行。
.PHONY
当我们要删除文件时应该怎么操作呢?
我们这时需要使用.PHONY来修饰。
PHONY是一个特殊的目标标记,用于告诉make工具,该目标不是一个真正的文件名,而是一个伪目标。
因为我们使用clean的目的是清除某些文件,而删除操作又不需要依赖文件,所以我们创建了一个伪目标,然后依赖这个伪目标,然后执行依赖方法。
我们执行make clean就发现mytest被删除了。
如果我们不修改text.c然后一直执行make会发生什么呢?
然后就发现可以一直make clean但是不能一直make
原因就是.PHONY的作用
在makefile中如果我们想无限执行mytest,只需将.PHONY:目标文件 即可
无限执行mytest并不好,未修改源文件而持续执行make会给编译器造成负担,但是可以无限执行clean,因为清理是必要的。
刚刚我们在重复make时看到提示说 mytest文件已经最新了。
那么编译器如何判断我的可执行程序为最新的呢?----->是根据文件的最新修改时间。
stat可以查看一个文件重要的三个时间
Access:最近的访问时间.
Modify: 最近的内容修改时间,比如你修改了文件的内容,这个时间就会改变.
Change:最近的属性修改时间,比如你修改了这个文件的读写权限,这样它的属性修改时间就会被修改.
一个文件是由内容+操作属性组成(一个空文件在系统中也占内存),如果文件的内容被修改,那么文件的大小也会被修改,相关的属性也会发生变化,并且可执行文件形成的时间一定比源文件晚,比较一下时间,只要可执行文件的时间比源文件晚,就说明这个可执行文件一定是最新的。
执行make默认执行的是第一个依赖关系和依赖方法。
makefile中常用选项
写一下makefile中的内容
说明:
1 makefile支持定义变量,这是一门解释性语言,无需像C语言那样 int a = 10;
2 可以理解为取出对象; 可以理解为取出对象; 可以理解为取出对象;@ 是一个特殊变量,它代表当前规则的目标文件名;$^ 是一个特殊的宏,它代表当前规则的所有前置条件(即依赖文件列表)
3 通过观察可知,echo是用来打印内容的,但是在这个echo中,$(src) $(bin)会被替换打印
4 如果我们执行make/clean语句,不想打印出相关信息,可以在依赖方法前加@表明@:在命令前使用@可以阻止make打印这条命令
在Makefile中, @ 表示生成的目标文件, @表示生成的目标文件, @表示生成的目标文件,<表示从依赖文件列表中取出一个文件,对应的还有$^表示依赖文件列表中的所有文件
而对于gcc来说,在Makefile中可以使用内置变量CC(表示C编译器的名字)代替
makefile的自动推导
发现make/makefile会自动根据文件中的依赖关系,进行自动推导,帮助我们执行所有相关的依赖方法。
注意:
-
语句第一条必须是最终结果,如果最终目标颠倒顺序,程序不可执行
-
除了最终结果代码之外,其余的可以随意调换位置
-
整个推导过程是递归的,与栈类似
进度条程序
我们先了解一下\n 与 \r
在c语言和其他高级语言中 \n表示回到下一行开始,但是在实际上这是\r +\n
回车(\r):回到当前光标所在行的开始
换行(\n):前往光标所在行的下一行,但是光标是平行向下移动
输出缓冲区:
观察下面程序运行的结果:
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello linux\n");
sleep(2);
return 0;
}
如果在Linux终端运行该程序,可以看到程序先打印了hello linux,然后等待了2秒才显示prompt提示
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello linux");
sleep(2);
return 0;
}
可以看出程序先等待了两秒,如何再打印的hello linux 。
c语言默认是从上往下顺序执行代码,之所以会发生这样是因为缓冲区的存在,程序在输出时并不会直接将内容输出到显示器中,而是先输出到缓冲区中,再通过刷新结束缓冲区将内容打印到屏幕上,而之所以在有\n时会显示再等待是因为\n刷新了缓冲区,导致内容打印到了屏幕上。
如果使用将\n替换为\r则同样会先等待再打印,因为\r也不具备刷新缓冲区的效果,如果在当前情况下想刷新缓冲区但又不想使用\n,则可以使用fflush()函数,传递参数为标准输出stdout
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello linux");
fflush(stdout);
sleep(2);
return 0;
}
倒计时程序
#include <stdio.h>
#include <unistd.h>
int main()
{
int cnt = 10;
while(cnt>=0)
{
printf("倒计时: %2d\r", cnt);
fflush(stdout);
cnt--;
sleep(1);
}
printf("\n");
return 0;
}
实现进度条
思路:首先要创建三个文件,分别是测试文件 main.c 和头文件process.c和实现文件process.c。对于进度条实际上就是打印原数组内容,再填充数组然后刷新缓冲区,对于百分比就是一个循环来控制,对于最右侧的变化符号,就是指定范围内进行循环。
// 实现文件
#include "process.h"
void process()
{
char bar[NUM] = {0};// 进度条数组
int count = 0;
const char *label = "|/-\\";//控制进度条最右侧的闪烁符号(| 顺时针旋转)
size_t len = strlen(label);
while(count <= 100)
{
// [%-100s]预留100个字符的位置,每一次打印数组bar中的字符,因为初始化为0,所以数组中全是'\0',当遇到第一个'\0'就停止,加上-表示从右往左(默认从左往右)打印
// 使用count控制进度条百分比,百分号%需要额外转义
// label闪烁符号,使用count%4使下标循环在0-3,思路可以联想循环队列数组版控制下标轮回的方式
printf("[%-100s][%d%%][%c]\r", bar, count, label[count%len]);
fflush(stdout); // 先刷新缓冲区,打印出停留在缓冲区的内容
bar[count++] = STYLE; // 向数组中添加字符
usleep(20000);// 睡眠时间单位为微秒,1秒 = 1000毫秒=1000000微秒
}
printf("\n");
}
void procBar(double total,double current){
char bar[NUM]={0};
int len=strlen(label);
int cnt=0;
double rate=(current*100.0)/total;
int loop_count=(int)rate;
while(cnt<=loop_count){
bar[cnt++]=STYLE;
}
printf("[%-100s][%.lf%%][%c]\r",bar,rate,label[cnt%len]);
fflush(stdout);
}
// 头文件
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#define NUM 101 // 定义进度条字符的个数
#define STYLE '#' // 定义进度条的样式
void process();
// 测试文件
#include "process.h"
int main()
{
process();
return 0;
}
对应的makefile
TARGET=process
SRC=process.c main.c
$(TARGET):$(SRC)
gcc $^ -o $@
.PHONY:clean
clean:
rm -rf $(TARGET)
```