1. 什么是make/makefile?
make
是一个命令工具,是一个解释makefile
中指令的命令工具,那么究竟什么是makefile呢?
其实,在一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作作。makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
其实,总的来说,make是一条命令,makefile是一个文件
,两个搭配使用,完成项目自动化构建。
2. make/makefile的使用
2.1 实例代码
这里我们可以先创建一个test.c
文件来写一个简单的C语言程序,然后用vim来创建一个Makefile
文件(当然了,文件名是makefile也是可以的),Makefile中的内容和test.c中的内容如下:
接下来我们使用make这个命令来进行编译:
在这里我们发现使用make指令后,自动帮我们执行了gcc test.c -o mytest
这条指令。并且生成了可执行程序mytest
,执行mytest这个可执行程序后,内容已经显示到了屏幕上。
下面我们看一下Makefile文件中原理。
2.2 依赖关系和依赖方法
在Makefile文件中有两行内容,第一行内容中的mytest
和test.c
是依赖关系,因为mytest这个文件的生成必然要依赖于test.c的编译。
但是test.c是如何生成mytest的呢?是通过gcc编译器的编译而形成的。而编译所需要的命令就是gcc test.c -o mytest
,所以这条命令就是所谓的依赖方法。
下面我们通过上一篇博客讲到的程序翻译的四个阶段来深刻理解一下所谓的依赖关系和依赖方法:
当我们使用make指令来执行Makefile这个文件时,依赖关系和依赖方法会自上而下展开,mytest
依赖于test.o
,但是原来的文件中并不存在test.o,所以会继续向下寻找依赖关系,test.o
又是依赖于test.s
的,但是test.s还是找不到,所以会依次向下寻找依赖关系,直到最后一次test.i
依赖于test.c
,而==test.c却是存在的==,所以Makefile会自下而上去执行依赖关系所对应的依赖方法,下面我们用make
指令来演示一下这个过程:
2.3 项目清理
工程是需要被清理的,例如我们在写代码的时候把已经写好的代码编译后生成了可执行程序,但这个可执行程序的执行结果却和我们预想的不一样,那我们就需要将这个可执行程序清理掉,重新编写源代码并生成新的可执行程序,基于这个原因,makefile给我们提供了项目清理工具。
我们可以定义一个clean
,这里的clean没有被第一个目标mytest
直接或间接依赖,所以clean不会被自动执行:
我们需要可以在==make命令后面跟clean目标==作为参数来执行其后所定义的命令。
.PHONY
伪目标
基于上面的clean,我们可以使用.PHONY
来修饰,.PHONY
修饰的对象就是伪目标,伪目标的特性是:总是被执行的。
这里我们需要注意的是:.PHONY修饰的一定能被反复执行,而能反复执行的不一定被.PHONY修饰。
当我们不加.PHONY时,clean依然可以被反复执行。但是我们一般都要给clean加上.PHONY,因为我们不知道目标文件是否已经被删除,所以保证每次都清理一遍。
2.4 make是如何确定是否编译的
我们发现如果我们用make编译过一遍我们需要执行的源程序后,第二遍编译的时候就不能再编译了。
这里我们发现当我们编译过一遍后,他告诉我们的可执行程序mytest
已经是最新的了,所以从第二次开始就不能再编译了。那么make是如何知道我们的可执行程序已经是最新的呢?
其实这是因为存在两条时间线,一条是我们源程序的时间线,另一条是我们的可执行程序形成的时间线。而make呢就是通过这两条时间线上我们源程序和可执行程序最新形成的时间对比来确定是否要进行编译的。
如果可执行程序最新形成的时间在最新的源程序的时间之后,那么make认为当前的可执行程序是最新的,为了提高效率并不需要执行编译。相反,如果可执行程序最新形成的时间在最新的源程序的时间之前,那么make则认为需要重新进行编译形成新的可执行程序。
当然我们也可以通过touch指令来执行我们的源程序,如果touch的文件不存在那就会新建一个文件,如果要是存在的话,就会将文件的时间更新到最新。
3. Linux第一个小程序—进度条
3.1 \r 和 \n
‘\n’是我们在学习C语言的过程中经常遇到的转义字符,它的意思是 ==回车+换行==,但其实’\n‘原本的意思仅有换行的意思,只不过是在C语言中将它做了优化,让他既具有回车的功能又具有换行的功能。下面我们看一下\r
和\n
原本的意思。
\r
:回车,将光标移动到当前行的行首
\n
:换行,直接将光标移动到下一行
这里我们可以先提前看一下\r
这种转义字符的效果:
1 #include<stdio.h>
2 #include<unistd.h>
3
4 int main()
5 {
6 printf("hello world\r");
7 fflush(stdout);//刷新一下缓冲区
8 sleep(3);
9 return 0;
10 }
3.2 进度条小程序
首先我们需要先创建三个文件:process.h、process.c、test.c
;
process.h
1 #pragma once
2
3 #include<stdio.h>
4
5 #include<unistd.h>
6
7 #include<string.h>
8
9 #define NUM 101
10 extern void proccesson();
process.c
1 #include"process.h"
2 #define STYLE '='
3 #define ARR '>'
4
5 void processon()
6 {
7 char bar[NUM];
8 memset(bar,'\0',sizeof(bar));
9 const char*lable = "|/-\\";
10 int n = 0;
11 while(n<=100)
12 {
13 printf("[%-100s][%-3d%%][%c]\r",bar,n,lable[n%4]);
14 fflush(stdout);
15 bar[n++] =STYLE;
16 if(n!=100) bar[n]=ARR;
17
18
19 usleep(100000);
20 }
21 printf("\n");
22 }
test.c
1 #include"process.h"
2
3 int main()
4 {
5 processon();
6 return 0;
7 }