首页 > 运维 > linux运维 > 正文

【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码

蓮花仙者
发布: 2025-07-23 12:28:26
原创
481人浏览过

?前言

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

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

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

? Makefile 格式

Makefile 由一系列规则组成,每个规则的格式如下:

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">target: dependencies    commands</code>
登录后复制
target: 要生成的文件或目标。可以是文件名,也可以是一个标签(label)。dependencies: 生成 target 所需的文件或目标,用空格分隔。commands: 生成 target 所需执行的命令,每行一条命令,必须以制表符( )开头。

例如:

【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码

Makefile 语法

伪目标:

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">.PHONY: cleanclean:    # commands</code>
登录后复制

伪目标不对应任何文件,通常用于定义常用的构建任务。

【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码

make 命令

常用的 make 命令有:

make: 构建 Makefile 中的默认目标。make target: 构建指定的目标。make clean: 清理构建产生的中间文件。make all: 构建 Makefile 中的所有目标。make -n: 显示执行命令,但不实际执行。make -j <number></number>: 并行构建,指定同时执行的命令数量。

使用make命令,可以直接执行Makefile的文件命令

【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码

但是,当我再次执行make命令,这里的proc的文件无法再次执行:

【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码

这个问题是因为:

.PHONY是让目标文件,对应方法,总是被执行。(让依赖方法,忽略时间对比),这里的rm-f命令本来就不关心时间,只要make,这个指令就会执行,所以我们把.PHONY加在这里,无法看出效果。

【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码

把伪目标加在前面,让他忽略时间对比,仍然执行目标文件指令:gcc -o proc proc.c:如图:

【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码

make执行

【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码

这里提及到了时间,有一问题:对于源文件和可执行出程序,有时候需要重新编译有时候不需要?为什么? 对于源文件和可执行程序,可以说都是文件,而文件 = 内容 + 属性------》而属性其中包含了文件的时间:

stat命令: stat 命令用于显示文件或文件系统的状态信息。它可以输出文件的各种属性,如文件类型、权限、所有者、大小、访问和修改时间等。

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">stat [OPTION]... FILE... </code>
登录后复制
【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码
【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码
?Makefile命令符号

Makefile 中,有几个常用的命令符号和特殊规则,它们用于定义和管理构建过程。以下是一些常用的命令符号和其用途:

命令符号 @

用法:@ 符号用于抑制命令的回显。通常,make 会在执行每一条命令时打印命令本身。使用 @ 符号可以让 make 只输出命令的结果,而不输出命令行。

示例:

#目标文件 依赖关系列表 2 .PHONY:proc 3 proc:proc.c 4 @echo “hello make” 5 @echo “hello make” 6 @echo “hello make” 7 @echo “hello make” 8 9 # gcc -o proc proc.c 10 #.PHONY:clean 11 clean: 12 rm -f proc

代码语言:javascript代码运行次数:0运行复制
<code class="javascript"> ```</code>
登录后复制
【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码

伪目标 .PHONY

用法:.PHONY 用于声明伪目标。伪目标不是实际存在的文件,而是用于执行特定的命令。声明伪目标可以防止与实际文件名冲突,确保每次 make 都执行相关命令。

示例:

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">.PHONY: cleanclean:    rm -f *.o my_program</code>
登录后复制

变量赋值

用法:Makefile 支持变量赋值,用于简化和重用配置。变量可以在 Makefile 中定义并在规则中使用。

示例:

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">CC = gccCFLAGS = -Wall -gTARGET = my_program$(TARGET): main.o utils.o    $(CC) $(CFLAGS) -o $(TARGET) main.o utils.o</code>
登录后复制

自动变量

用法:自动变量在规则中使用,能够引用当前目标、依赖文件等。

常见自动变量:

$@:表示规则中的目标文件。$:表示第一个依赖文件。<code>$^:表示所有的依赖文件(去重)。$?:表示比目标文件更新的所有依赖文件。

示例:

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">program: main.o utils.o    $(CC) -o $@ $^main.o: main.c    $(CC) -c $<utils.o: utils.c    $(CC) -c $<</code>
登录后复制
条件判断

用法:可以使用条件判断来决定是否执行某些规则或命令。

示例:

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">ifeq ($(DEBUG), 1)    CFLAGS += -gelse    CFLAGS += -O2endif</code>
登录后复制

模式规则

代码小浣熊
代码小浣熊

代码小浣熊是基于商汤大语言模型的软件智能研发助手,覆盖软件需求分析、架构设计、代码编写、软件测试等环节

代码小浣熊 51
查看详情 代码小浣熊

用法:模式规则允许你定义一类规则,从而简化多个类似文件的编译过程。

示例:

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">%.o: %.c    $(CC) -c $(CFLAGS) $< -o $@</code>
登录后复制

内置规则

用法:make 提供了一些内置规则来处理常见的文件生成任务,例如编译 .c 文件到 .o 文件。如果不定义这些规则,make 会尝试使用默认规则。

示例:

代码语言:javascript代码运行次数:0运行复制
<code class="javascript"># 默认规则%.o: %.c    $(CC) -c $(CFLAGS) $< -o $@</code>
登录后复制

包括其他 Makefile

用法:可以使用 include 指令来包含其他 Makefile 文件,以实现配置的模块化。

示例:

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">include config.mk</code>
登录后复制

这些符号和规则是编写和维护 Makefile 的基础,掌握它们可以帮助你更高效地管理构建过程。

【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码
?makefile/make基本原理make解释makefile的时候,是会自动推导的。一直推导,推导过程,不执行依赖方法。直到推导到有依赖文件存在,然后在逆向的执行所有的依赖方法代码语言:javascript代码运行次数:0运行复制
<code class="javascript">Makefile  ⮀                                                                                                         ⮂⮂ buffers   1 #目标文件   依赖关系列表  2 .PHONY:proc clean  3 proc:proc.o  4     gcc proc.o -o proc  5 proc.o:proc.s  6     gcc -c proc.s -o proc.o  7 proc.s:proc.i  8     gcc -S proc.i -o proc.s  9 proc.i:proc.c 10     gcc -E proc.c -o proc.i 11  12 .PHONY:clean clean-all 13 clean: 14   rm -f proc.i proc.s proc.o proc 15 clean-all: 16   rm -f proc  </code>
登录后复制
【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码

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

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

项目清理

工程是需要被清理的 像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令——“make clean”,以此来清除所有的目标文件,以便重编译。 但是一般我们这种clean的目标文件,我们将它设置为伪目标,用 .PHONY 修饰,伪目标的特性是,总是被执行的。 可以将我们的 hello 目标文件声明成伪目标,测试一下。

依赖关系:右侧的依赖文件,一个一个一个的交给gcc -c选项,形成同名的.o文件

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">Makefile :                                                                                                        1 #目标文件   依赖关系列表  2   3 proc:proc.o  4     gcc proc.o -o proc  5 %.o:%.c  6   gcc -c $<                                                                                                                        7   8 .PHONY:clean  9 clean: 10   rm -f proc.o proc 11   </code>
登录后复制
【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码
【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码
代码语言:javascript代码运行次数:0运行复制
<code class="javascript">Makefile+ :                                                                                                    1 bin=proc  2 src=proc.o  3 $(bin):$(src)  4     gcc %^ -o $@  5 %.o:%.c  6   gcc -c $<  7                                                                                                                                    8 .PHONY:clean  9 clean: 10   rm -f proc.o proc 11   </code>
登录后复制

原型为:

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">proc1.c proc2.c proc3.c%.o:proc1.c proc2.c proc3.cgcc -c proc1.c -o proc1.o以下同理:gcc -c proc2.cgcc -c proc3.c</code>
登录后复制
【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码
?总和小案例

我们将使用gcc/g++, vim, make/makefile 进行制作一 linux第一个偏系统的一个样例程序︰进度条

回车与换行 回车 (Carriage Return, CR): - 回车将光标移动到行首回车是一个控制字符:将光标移动到当前行的开头。在早期的打字机和电传打字机上,回车会使打字头或打字轮返回到行首。在 macOS 和早期的 Mac OS 系统中,文本文件使用回车 (ASCII 编码 0x0D) 作为行末标记。

换行 (Line Feed, LF): - 换行将光标移动到下一行

换行是一个控制字符:将光标移动到下一行。在早期的打字机和电传打印机上,换行会使纸张向下移动一行。在 Unix/Linux 系统中,文本文件使用换行 (ASCII 编码 0x0A) 作为行末标记。

回车+换行 (CR+LF):

在 Windows 系统中,文本文件使用回车+换行 (ASCII 编码 0x0D 0x0A) 作为行末标记。这是为了向后兼容早期的打字机和电传打印机,既移动光标到行首,又移动到下一行。代码语言:javascript代码运行次数:0运行复制
<code class="javascript">新起一行:本质:先回车,在换行              
      ln</code>
登录后复制
【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码
缓冲区刷新: 在 Linux 系统中, 在刷新缓冲区方面有以下区别:

(换行符):

当遇到 时,Linux 系统会将缓冲区中的数据立即刷新到输出设备(如终端或文件)。这是因为 在 Linux 中被视为行末标记,表示一个完整的行已经写入。当缓冲区中有 时,系统会立即将缓冲区中的数据刷新到输出设备,以确保数据能够及时显示。

(回车符):

当遇到 时,Linux 系统不会立即刷新缓冲区。 只是将光标移动到当前行的开头,并不表示一个完整的行已经写入。缓冲区中的数据会一直保留在缓冲区中,直到遇到 或者缓冲区被手动刷新。

手动刷新缓冲区:

除了遇到 时自动刷新,您也可以手动刷新缓冲区。常见的手动刷新方式包括调用 fflush() 函数或者关闭文件/终端。手动刷新可以确保缓冲区中的数据立即被写入输出设备,而不需要等待 的出现。

总结:在 Linux 系统中, 会触发缓冲区的自动刷新,而 不会。如果需要立即将缓冲区中的数据写入输出设备,可以手动调用 fflush() 或者关闭文件/终端。这样可以确保数据能够及时显示,而不需要等待 的出现。

如:

【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码
【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码
代码语言:javascript代码运行次数:0运行复制
<code class="javascript"> #include <stdio.h> #include <unistd.h> int main() {   printf("Hello world!");   sleep(2);    return 0;                                                                                                                     }  </code>
登录后复制
首先会执行 printf("Hello world!"); 语句,将字符串 “Hello world!” 输出到标准输出(通常是终端)。 然后会执行 sleep(2); 语句,程序会暂停 2 秒钟。

在程序执行 sleep(2) 期间:

“Hello world!” 字符串已经被输出到标准输出了,此时它已经在终端上显示出来了。 程序进入睡眠状态,不会执行任何其他操作,只是等待 2 秒钟后继续执行后续的语句。

所以,在程序执行 sleep(2) 期间,“Hello world!” 字符串已经被输出到终端上了,不会在缓冲区中等待。

这是因为 printf() 函数在 Linux 系统上默认是行缓冲的,也就是说当遇到换行符 时,才会将缓冲区中的数据刷新到输出设备(终端)。在这个例子中,由于没有换行符,printf() 会立即将数据刷新到终端上。

所以,在程序执行 sleep(2) 期间,“Hello world!” 字符串已经显示在终端上了,不会在缓冲区中等待。

倒数:

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">Count.c                                                                                            ⮂⮂ buffers  #include <stdio.h> #include <unistd.h> int main() {     int count = 10;    while(count >= 0)     {       printf("%d
",count);//
回车,但是没有换行也就咩有 刷新         fflush(stdout);                                                                                                                 count--;       sleep(1);     }     printf("
"); }</code>
登录后复制

显示器是一个一个刷新的,因此需要 printf(“%-2d ”,count);整两个内存区域一起进缓冲区,一起刷新。

【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码
【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码
?进度条代码

版本1: main.c:

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">#include "process.h"                                                                                                                                                                                   int main()                                                                                         {                                                                                            Process();                                                                                             return 0;                                                                                                                  } </code>
登录后复制

process.h:

【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码
代码语言:javascript代码运行次数:0运行复制
<code class="javascript">#include <stdio.h>    #include <string.h>    #include <time.h>    #include <unistd.h>        //version 1    void Process();  </code>
登录后复制

process.c:

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">#include "process.h"#define NUM 100#define STYLE '='//version1void Process(){    const char * lable = "|/-\";    int len = strlen(lable);    char bar[NUM + 1];    memset(bar,'',sizeof(bar));    int cnt = 0;    while(cnt <= NUM)    {        printf("[%-100s][%d%%][%c]
",bar,cnt,lable[cnt%len]);            fflush(stdout);               bar[cnt] = STYLE;                           cnt++;                     if(cnt == NUM)            {                                                                          bar[cnt-1] = STYLE;                                                  printf("[%-100s][%d%%][%c]
",bar,cnt,lable[cnt%len]);                break;                                                                                                                                  }                                                            bar[cnt] = '>';                                   usleep(50000);                                                                                           }                         printf("
"); 」</code>
登录后复制
【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码

################################################################################################

process.h

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">#include <stdio.h>    #include <string.h>    #include <time.h>    #include <unistd.h>        //version 1    //void Process(); //version 2    void FlushProcess(double total,double current); </code>
登录后复制

process.c

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">#include "process.h"    #include <stdio.h>    #include <string.h>    #include <unistd.h>        #define NUM 101    #define STYLE '='    #define POINT '.'    #define SPACE ' '    const int pnum = 6;    void FlushProcess(double total ,double current)    {          //1,更新当前进度条百分比        double rate = (current / total)*100;        //printf("%.1lf%%
",rate);       // fflush(stdout);            //2,更新进度条主体        char bar[NUM];//我们认为1%更新一个=        memset(bar,'',sizeof(bar));        int i;        for(i = 0; i < (int)rate; ++i)        {            bar[i] = STYLE;                                                                                                                         }            //3,更新旋转光标或者其他风格        static int num = 0;        num++;        num %=pnum;                                                                                                                                 char points[pnum+1];    memset(points,'',sizeof(points));    int j ;    for(j = 0; j < pnum; ++j)    {      if(j < num)  points[j] = POINT;      else points[j] = SPACE;    }    printf("[%-100s][%.1lf%%][%s]
",bar,rate,points);    usleep(10000);    fflush(stdout);}</code>
登录后复制

main.c

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">#include "process.h"#include <stdlib.h>typedef void(*flush_t)(double total,double current);//这是一个动态刷新的函数指针类型        const int base = 100;    double total = 2048.0;//2048mb    double once = 0.1;//0.5mb        //进度条调度方式    void download(flush_t f)    {        double current = 0.0;        while(current < total)        {            //            int r = rand() % base +1;            double speed = r * once;            current += speed;            if(current >= total)            {                                                                                                                                             current = total;            }            usleep(10000);                f(total,current);        f(total,current);    }}int main(){    srand(time(NULL));    download(FlushProcess);    printf("
");        return 0;}</code>
登录后复制
【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码

?总结makefile文件,会被make从上到下开始扫描,第一个目标名,是缺省要形成的。如果我们想执行其他组的依赖关系和依赖方法,make namemake makfile在执行gcc命令的时候,如果发生了语法错误,就会终止推导过程make解释makefile的时候,是会自动推导的。一直推导,推导过程,不执行依赖方法。直到推导到有依赖文件存在,然后在逆向的执行所有的依赖方法make默认只形成一个可执行程序

以上就是【linux学习指南】Linux项目自动化构建工具 make /makefile&&进度条代码的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号