【命令行魔法:掌握Linux基础工具开发的独门技艺】(三):https://developer.aliyun.com/article/1425542
4.3原理
- make是如何工作的,在默认的方式下,也就是我们只输入make命令。那么
1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“mybin”这个文件, 并把这个文件作为最终的目标文件。
3. 如果mybin文件不存在,或是mybin所依赖的后面的test.o文件的文件修改时间要比mybin这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成mybin这个文件。
4. 如果mybin所依赖的test.o文件不存在,那么make会在当前文件中找目标为test.o文件的依赖性,如果找到则再根据那一个规则生成test.o文件。(这有点像一个堆栈的过程)
5. 当然,你的C文件和H文件是存在的啦,于是make会生成 test.o 文件,然后再用 test.o 文件声明 make的终极任务,也就是执行文件mybin了。
6. 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
7. 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错, 而对于所定义的命令的错误,或是编译不成功,make根本不理。
8. make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起, 我就不工作啦。
4.4项目清理
- 工程是需要被清理的
- 像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行, 不过,我们可以显示要make执行。即命令——“make clean”,以此来清除所有的目标文件,以便重编译。
- 但是一般我们这种clean的目标文件,我们将它设置为伪目标,用 .PHONY 修饰,伪目标的特性是,总是被执行的。
- 可以将我们的 hello 目标文件声明成伪目标,测试一下。
怎么清理呢?vim打开Makefile
即命令——“make clean”,以此来清除所有的目标文件,以便重编译。
我们再来改一下vim里面的顺序
1.Makefile和make形成目标文件的时候,默认是从上到下扫描makefile文件的,默认形成的是第一个目标文件。
2.有多个依赖关系和依赖方法,默认只形成一个。
5.Linux第一个小程序-进度条
5.1.缓冲区概念
首先我们将makefile和源文件搭建好
然后我们验证一下我们写的是否有问题
然后我们验证两个代码,观看它们的现象。
#include <stdio.h> int main() { printf("hello Makefile!\n"); sleep(3); return 0; }
我们可以发现上面代码的运行结果是先输出printf,然后等待三秒
#include <stdio.h> int main() { printf("hello Makefile!"); sleep(3); return 0; }
我们发现上代码的运行结果是先等待三秒,然后再输出printf
但是我们不要被上面的现象所迷惑,C语言代码是遵守顺序结构的,所以我们可以知道上面的代码永远是先执行printf("hello Makefile!");然后在执行sleep(3);但是为什么会出现上面的想象呢?
这是因为我们的C语言有一个缓冲区,当sleep没有执行完的时候,printf的字符串被保存到缓冲区了,当sleep执行完的时候,此时就要求刷新缓冲区,于是此时才输出printf语句,而第一个代码直接输出是因为'\n'字符会立刻刷新缓冲区,于是就先输出了printf语句。
#include <stdio.h> int main() { printf("hello Makefile!"); fflush(stdout); sleep(3); return 0; }
我们可以通过手动刷新缓冲区,先输出printf内容。
5.2.\n:换行符与\r:回车符之间的区别
所以准备工作已经完成了,现在开始写我们的进度条小程序。
5.3.进度条代码
但是此时我们发现make编译后运行我们的程序没有任何结果
因为我们上面的printf内容被保存到缓存区了,所以没有输出我们想要的结果,所以我们就要使用fflush进行强制刷新缓冲区。
然后我们再运行我们的代码。
这样就实现了我们的倒计时小程序,不过我们上面只能从9倒计时,如果我们想从10倒计时呢?
我们发现我们的程序不能完成从10倒计时,输出的时候后面多了一个0,为什么呢?
当我们向显示器打印10的时候,打印的是字符'1'和'0',当我们后面打印的时候,字符只依次覆盖'1'字符,并没有覆盖'0'字符。怎么解决呢?-2d:表示输出这个整数的时候预留两个字符的空间并左对齐输出这个整数
输出结果:
现在我们就来写一下我们的进度体条小程序,首先学习一下usleep函数,按照微秒的时间进行休眠。
我们先来建立一下多个文件并写上一个简单的打印输出工作
然后我们先验证一下make能否编译
接下来就可以写我们的进度条了。
我们的程序输出的进度条主要在processbar.c中实现的,字符数组设置为102是因为在我们的程序中,循环共循环了101次,bar[100]='#',如果设置数组大小101,此时下标最大就是100,而100下标处存放了bar[100]='#',没有位置存放'\0',这样程字符串就没有结束的标识,程序就达不到预期的结果。这里要注意最后一次,下标的位置是bar[100]='#',那从0开始到100不就有101个'#'符号吧吗?并不是,我们自己看我们的程序,当下标为0的时候,我们的程序时不打印'#'的,下标为1的时候才打印'#'的。
#include <iostream> using namespace std; int main() { int cnt = 0; char bar[5]; memset(bar, '\0', sizeof(bar)); while (cnt <= 3)//下标为0,1,2,3 { printf("[%s]", bar); bar[cnt++] = '#'; } return 0; }
运行结果:
所以此时小程序下标就是100,即可输出100个字符'#",结果运行:
但是我们平常的进度条不是#,我们期待能出现一个箭头的形式,我们可以在0下标处设置为字符'<',后续用字符'='覆盖前面的字符'<',我们来看一下程序的运行结果:
我们发现上面的程序输出了101个字符,我们期待最后一个字符应该是'=',我们来看一下我们的程序,当i=100的时候,此时bar[100]='>',循环再次进入的时候,最后一个元素输出是'>',然后i++,bar[100]位置被修改为'=',然后i++变为101,此时不再进入循环,虽然bar[100]位置被修改为'=',但是此时没有执行打印输出工作,所以最后一个仍然是'>'字符。
#include <iostream> using namespace std; int main() { int cnt = 0; char bar[6]; memset(bar, '\0', sizeof(bar)); bar[0] = '>'; while (cnt <= 3)//下标是0,1,2,3 { printf("[%s]", bar); bar[cnt++] = '='; if (cnt <= 3) bar[cnt] = '>'; } return 0; }
运行结果:
监视窗口可以看到bar最后一个元素是'=',只不过输出的时候没有打印出来。
因此我们可以修改一下我们的程序,当i<100的时候就不要再执行修改'>'了。
但是实际上进度条并不是一个独立的程序,而是依附于其他应用的,比如我们下载一个文件就要进度条,进度条应该表现为下载量除以文件大小。接下来我们模拟一个下载的场景。
运行结果:
因此我们可以改善一下进度条。
上面的程序我们使用了我们的函数指针,通过回调函数可以调用函数,并且我们修改旋转光标不和rate进度条相关,并且给printf输出加上颜色打印,使我们的进度条更加美观。
进度条: