make和makefile

一、认识make和Makefile

1、会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力


2、一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作


3、makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。


4、make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。


5、make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。
 

二、初见make和Makefile

2.1、makefile的创建

这里我创建一个Makefile文件,直接使用 vim makefile,主义makefile的m可以大写也可以小写,我们在写makefile文件时,别打错字母,因为系统只认识makefile和Makefile;

make和makefile_第1张图片

注意: Makefile 文件中,命令行前面是用 tab 键来缩进的,不能用空格空格键
             如果命令行是用 tab 键来缩进,那命令行就会变颜色

 

makefile文件格式:

    目标文件:依赖文件
    			命令
  • 目标文件一般就是最终生成可执行文件,而生成目标文件需要去找他所需的所有依赖文件,如果依赖文件也需要其他的文件,就一直找,直到不需要依赖文件,就好比递归,需要一层一层递归,直到最后不需要为止
  • 这里拿 add.c add.h main.c 来举例,我现在需要把 add.c 和 main.c编译出可执行文件
  • make和makefile_第2张图片

2.2、make的使用

这里我们就把简单的Makefile文件创建好了,我们来在来创建我们要实现的add.c文件。

make和makefile_第3张图片

我们保存并退出,在命令行输入  make   编译我们的add.c

make和makefile_第4张图片

执行完该Makefile文件后,显示了文件中的执行命令,并且文件中多出了 add.o , main.o 还有我们想要的myadd可执行文件
我们写的这个Makefile中还有一句标签为

clean:
      rm -f *.o myadd

而这里的clean只是一个标签名,我们可以使用make工具来操作这行命令,例如

我们使用 make clean时就会

执行之后,所有 .o 文件和myadd可执行文件也被删除了

原理

make是如何工作的,在默认的方式下,也就是我们只输入make命令。那么,


1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“hello”这个文件,并把这个文件作为最终的目标文件。
3. 如果hello文件不存在,或是hello所依赖的后面的hello.o文件的文件修改时间要比hello这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成hello这个文件。
4. 如果hello所依赖的hello.o文件不存在,那么make会在当前文件中找目标为hello.o文件的依赖性,如果找到则再根据那一个规则生成hello.o文件。(这有点像一个堆栈的过程)
5. 当然,你的C文件和H文件是存在的啦,于是make会生成 hello.o 文件,然后再用 hello.o 文件声明make的终极任务,也就是执行文件hello了。
6. 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
7. 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。
8. make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作。
 

三、Makefile中的变量

3.1、自定义变量


Makefile 中允许使用等号自定义变量
Makefile中的变量定义和 shell 变量的定义非常相似,不同的是 Makefile中的变量的名称可以是任何不包含 :, #,= 和空字符的字符序列,并且等号两边可以有空格(shell 定义变量的等号两个不允许出现空格)
变量名=值 --等号两边不能有空格

3.2、变量的调用

在变量标识符前加美元符 $ 来引用,例如

如果你不想看到执行命令时,把makefile中的命令再打印一遍可以在makefile中的命令前加一个@

3.3、变量的赋值

Makefile一共提供了四个赋值运算符 (=、:=、?=、+=)

1).普通赋值 =

make和makefile_第5张图片

如果按照c语言的逻辑,var2应该等于12,但是这里最后打印var2的值为100,所以普通赋值表示赋值最新的变量值

2).立即赋值 :=

立即赋值则是直接赋值,不会在改变

make和makefile_第6张图片

3).询问赋值 ?=

如果该变量前面已经赋过值了,此次就不能再赋值了,如果没有就赋值,只有在该变量为空时才设置值

make和makefile_第7张图片

4.追加赋值 +=

类似字符串连接,将值追加到变量末尾,在追加的时候,自动添加空格

make和makefile_第8张图片

4.4、特殊变量

$^         --表示所有依赖文件
$@         --表示目标文件
$<         --表示第一个依赖文件
%          --通配符,表示所有,和linux中的 * 一样

利用这些特殊变量和自定义变量,我们就可以将Makefile文件写的更加简洁

make和makefile_第9张图片

make和makefile_第10张图片

 四、进度条的实现

我们使用make和makedile工具实现一个进度条的功能

4.1、预备知识

  • 换行和回车的区别
    • \r:回车,回到当前行的行首,而不会换到下一行,如果接着输出的话,本行以前的内容会被逐一覆盖,如果之前的内容比下一次来的的内容长,就不会被完全覆盖
    • \n:换行,换到当前位置的下一行,而不会回到行首
  1#include 
  2 int main()
  3 {
  4     printf("hello world\n");                                                                                                                               
  5     return 0;
  6 }

当然上面的实现是可以打印出 hello world

#include 
int main()
{
   printf("hello world\r");                                                                                                                               
   return 0;
}

第二个是打印不出来hello world

不难发现 \n 可以打印出来,而 \r,不能打印出来,因为显示器模式是行刷新缓冲区是按行缓冲的,没有\n,就不能立即刷新。 \r 回到行首后,会进行覆盖写,shell 提示符会覆盖掉之前写的 “hello world”,如果我们在 “hello world” 不加 \r,则不会进行覆盖写,shell 提示符会顺着 “hello world” 往后写。


缓冲区的理解

行缓冲是缓冲区刷新策略的一种,在行缓冲模式下,当输入和输出中遇到 ‘\n’ 换行时,就会刷新缓冲区,下面我们认识头文件的三个函数

sleep : Linux 下的休眠函数,单位是秒
u s l e e p usleepusleep:和sleep 一样,单位ms(即10-6 m)
f f l u s h fflushfflush :刷新缓冲区

  1 #include 
  2 #include                                                                                                                                        
  3 int main()
  4 {
  5     printf("hello world");
  6     sleep(3);
  7     return 0;
  8 }

我们知道函数代码语句是从上到下依次进行的,而我们看到的却是先休眠三秒,然后再打印出"hello world",原因是因为数据保存在缓冲区中,没有主动刷新。当程序退出后,保存在缓冲区中的数据被自动刷新出来了,如果我们想提前刷新,便可以调用f f l u s h fflushfflush函数来刷新缓冲区。

  1 #include 
  2 #include                                                                                                                                        
  3 int main()
  4 {
  5     printf("hello world");
  6		fflush(stdout);
  7		printf("\n");
  8     sleep(3);
  9     return 0;
  10}


 

4.2进度条的实现

我们需要以下文件

make和makefile_第11张图片

main.c文件实现:宏定义,参数设置,函数调用和头函数包含

当然我们实现进度条都是要在一个场景下实现的,这里我模拟实现了一个下载的场景

  1 #include"processbar.h"
  2 #include
  3 #include
  4 #include
  5 
  6 #define FILESIZE 1024*1024*1024
  7 //模拟一种场景,表示一种下载的任务
  8 void download(callback_t cb)
  9 {
 10     srand(time(NULL)^1023);
 11     int total=FILESIZE;
 12     while(total)
 13     {
 14         usleep(10000);    //下载动作
 15         int one = rand()%(1024*1024*10);
 16         total-=one;
 17         if(total<0)
 18         {                                                                                                                                          
 19             total=0;
 20         }
 21         int download=FILESIZE-total;
 22         double rate=(download*1.0/(FILESIZE))*100.0;  //0 23.4   45.6  67.5
 23         cb(rate);
 24     }
 25 }
 26 int main()
 27 {
 28   // process();
 29    download(process_flush);
 30     return 0;
 31 }
~
~

processbar.h文件实现

  1 #pragma once                                                                                                                                       
  2 #include
  3 #include 
  4 #define NUM 103
  5 #define Body '='
  6 #define Head '>'
  7 typedef void (*callback_t)(double);
  8 
  9 
 10 //version1
 11 void process();
 12 
 13 //version2
 14 void process_flush(double rate);
~

processbar.c文件实现

  1 #include"processbar.h"
  2 #include
  3 #include
  4 
  5 const char*lable="|/-\\";
  6 void process()
  7 {
  8     char buffer[NUM];
  9     memset(buffer,'\0',sizeof(buffer));
 10     int cnt=0;
 11     int n=strlen(lable);
 12     buffer[0]=Head;
 13     while(cnt<=100)
 14     {
 15         printf("[%-100s][%3d%%][%c]\r",buffer,cnt,lable[cnt%n]);
 16         fflush(stdout);
 17         buffer[cnt++]=Body;
 18         if(cnt<100)
 19         {
 20             buffer[cnt]=Head;
 21             usleep(500000);
 22         }
 23     }
 24     printf("\n");
 25 }
 26 
 27 //version2
 28 //进度是多少,进度条是不知道的。,另外,进度应该是依附于一个东西的,比如下载
 29 char buffer[NUM]={0};
 30 void process_flush(double rate)
 31 {
 32     static int cnt =0;
 33     int n=strlen(lable);                                                                                                                           
 34     if(rate<=1.0)
 35     {
 36         buffer[0]=Head;
 37     }
 38     printf("[\033[4;32;44m%-100s\033[0m][%.1f%%][%c]\r",buffer,rate,lable[cnt%n]);
 39     fflush(stdout);
 40 
 41     buffer[(int)rate]=Body;
 42     if((int)rate+1<100)
 43     {
 44         buffer[(int)(rate+1)]=Head;
 45     }
 46     if(rate>=100.0)
 47     {
 48         printf("\n");
 49     }
 50     cnt++;
 51     cnt%=n;
 52 }

makefile文件的实现

   processbar:main.o processbar.o                                                                                                                     
       gcc -o $@ $^
   main.o:main.c
       gcc -c main.c
   processbar.o:processbar.c
       gcc -c processbar.c
   
  .PHONY:clean
   clean:
      rm -f main.o processbar.o processbar

效果展示:

你可能感兴趣的:(linux,vim,开发语言)