Linux 自动化构建工具(make/Makefile)

文章介绍了Linux中make和Makefile用于自动化构建的原理和使用方法,强调了依赖关系和依赖方法的编写,以及make如何通过比较文件时间来决定是否执行。此外,还讲解了如何创建简单的进度条小程序,涉及回车换行、缓冲区刷新和休眠函数的使用。
摘要由CSDN通过智能技术生成

绪论

        拼着一切代价,奔你的前程。——巴尔扎克. 本章继续学习Linux常用的工具,make是可以帮我们解决一些重复使用相同指令的冗杂的自动化构建工具。

话不多说安全带系好,发车啦(建议电脑观看)


附:红色,部分为重点部分;蓝颜色为需要记忆的部分(不是死记硬背哈,多敲);黑色加粗或者其余颜色为次重点;黑色为描述需要


思维导图:

要XMind思维导图的话可以私信哈


目录

1.make/Makefile

2.Linux小程序----进度条


1.make/Makefile

知识点:

make是一个指令而Makefile(makefile首字母大小写不管)是一个文件(这个文件内装着所要通过make快速执行的指令)

在Makefile内有着多个依赖关系和依赖方法,每个依赖关系都有其对应的依赖方法

使用方法:

用vim打开Makefile在文件中,写下依赖关系和依赖方法

  1. 依赖关系写在依赖方法的上边:
  2. 有依赖关系的文件用冒号(:)隔开,其中的目标文件是冒号左边的文件,而目标文件所依赖的文件放在右边,一个目标文件可以依赖于多个文件
  3. 在写依赖方法的之前必须用tab隔开一段距离后再写依赖方法
  4. 依赖方法就是指令(如gcc生成可执行程序的指令)

简单举例:

写完Makefile文件后,直接使用make指令就能使用第一个Makefile内部的依赖方法(自顶向下)

再在Makefile中写下一段依赖关系和依赖方法

此时要调用的话就需要指定依赖关系(其中目标文件clear不需要依赖任何文件所以就不用写):

指定使用的依赖关系:make clear指定到clear这个依赖关系上

细节:

依赖关系、依赖方法:

以例子来直接的表示如:mycode:mycode.c

其中,目标文件是mycode,而目标文件依赖于后面的mycode.c,

而既然有了依赖关系的话就需有他们之间的依赖方法gcc -o mycode mycode.c(这样才有意义),也就是根据依赖关系来执行依赖方法。

Makefile依赖关系的自动化推导

对于在Makefile中的依赖关系来说,假如其目标文件所依赖的文件不存在则会在Makefile中查找是否有生成该依赖文件的方法有则进行如递归式(栈式)的先形成依赖文件再返回到开始的依赖关系,反之没有形成依赖关系文件的方法则会报错。

附:

对于make来说当你进行了一次后,如果所指向的依赖关系中的文件没有被修改的话,一般来说就不会进行make指令(因为已近存在了所以编译器就选择不再执行,来提高编译效率)

那这种行为是如何实现的?:比较时间

首先了解:

在Linux中文件的时间(ACM):

Access(访问):打印、改变都算访问、增删查改(几乎任何操作)

Modify(修改文件内容)、change(改变文件属性)

其中当修改了文件内容的话文件属性也会更改(文件属性中包括文件的大小、最久的修改时间所以修改文件内容的话文件的属性也会跟着改变,同样对于Access来说也可能会更改)

查看文件的时间的指令:stat + 文件名

当我对文件进行修改过后其时间就会发生改变

当也可以发现Access并没有改变这是因为,Access因为太常用所以编译器进行了优化设置了他被使用一定次数后才会改变,当一个文件被调用很多次的时候能减少一定的损耗。

单独改变文件属性的例子:

手动更新时间指令 touch:

附加指令:

-a 

 -m 

总结:而对于make来说他是否执行依赖关系进行编译是通过判断比较的是源文件和可执行程序的modify内容修改时间的老旧()

比较源文件和可执行程序的修改时间:

  1. 源文件修改时间 老于 可执行程序的修改时间 则make不会再次执行
  2. 源文件文件修改时间 新于 可执行程序的修改时间 则make会再次执行
  3. 因为一般而言,源文件的修改时间是比可执行程序的修改早的(老的)所以此时就表示这源文件生成可执行程序后并没有被修改过,所以就不需要被再次编译。
  4. 若想不管这个时间的话可以用.PHONY来修饰(总是被执行),其中.PHONY修饰的文件被称为伪目标

特殊符号:

  1. $@:可以代替目标文件(冒号左边)$^:可以代替目标文件所依赖的所有文件(冒号右边)所以就能改成来自动识别目标文件和所依赖的文件:
  2. 在依赖方法前面加上@:可以让依赖方法不显示
  3. #:相当于注释的效果
  4. 定义变量(或者说成 ) 左边是变量名 右边是初始化   使用方法:$变量名

2.Linux小程序----进度条

知识点:

  1. 回车换行:
    1. 一般来说\n就直接代表着回车换行
    2. 回车、换行也能分开来看
      1. 回车:回到所在行的最开始(\r)
      2. 换行:换到下一行(就是\n)
    3. 缓冲区
      1. 对于\n来说可以用来刷新缓冲区,只有刷新缓冲区(刷新标准输出流stdout)才能拿出缓冲区的东西,如:用printf打印时会先把要打印的东西放进缓冲区内所以只有在后面加上\n才会在该语句结束时直接打印出所要打印的。反之若没有\n则会在程序结束后自动刷新缓冲区才会打印出来。
      2. 对此我们如果不想刷换行(\n)又想刷新缓冲区的话,那就可以用函数fflush(stdout)( int fflush ( FILE * stream ) 、stdout是标准输出流(一般编译器会在执行程序时会自动打开标准输入、输出、错误流))

练习(倒计时小程序):

sleep(second)函数的用处是休眠second秒、头文件:#include <unistd.h>

usleep(usec) 函数用处是休眠usec微秒(1s = 1000000um ) 、头文件...一样

#include<stdio.h>
#include<unistd.h>
int main()
{
     int count = 10; 
 
     while(count>=0)
      {
         printf("%-2d\r",count--);//%-2d 对打印的整形进行右对齐并且每次打印两个字符
         fflush(stdout);//因为没有\n所以需要自行刷新缓冲区
         sleep(1);//休眠1s
     }
     return 0;
}                                                                                                                                       

附:此程序是在Linux,gcc编译器下,

若在vs下的话需要把sleep换成Sleep(ms) 内部为毫秒 并且加上头文件#include<windows.h>

下面开始正式的来实现一个进度条:

  1. 分源管理创建文件: processBar.c、processBar.h、main.c 
  2. 创建Makefile
  3. 根据进度条的特性模仿一个进度条
    1. 方法一:单纯的实现进度条
      1. processBar.c

        #include "processBar.h"
        
        const char *lable = "|/-\\";// \ 反斜杠是特殊的字符所以需要用\\ 来表示成 \
        
        
        void processBar(int speed)
        {
            int cnt = 0;
            char bar[102];
            memset(bar,'\0',sizeof(bar));
            int len =  strlen(lable);
        
            while(cnt <= 100)
            {
                printf("[%-100s][%d%%][%c]\r",bar,cnt,lable[cnt%len]);//%-100d 先预先开辟100个字符位并且向左对齐、对于最后的旋转可以用不断遍历数组显示、%%表示%因为%是特殊字符 所以若要表示出来单独的%则需要用%% 或者 \% 
                fflush(stdout);//没有\n来刷新,所以需要自己刷新缓冲区
                bar[cnt++] = BODY;//逐渐增加的进度条
                if(cnt < 100) bar[cnt] = RIGHT;// > 在最后一个位置就不需要在出现了
                usleep(speed);//休眠多少微秒
            }
            printf("\n");
        
        }
        
      2. processBar.h

        #include<stdio.h>
        #include<unistd.h>
        #include<string.h>
        
        #define BODY '='
        
        #define RIGHT '>'
        
        
        void processBar(int speed);
        
        //extern void processBar(); extern 可写可不写因为对于函数来说这样写就表示这是一个声明
        
        //但注意的是假如是一个变量的话就需要加上extern了因为分不清到底是不是编译器声明
        
      3. main.c

        #include<stdio.h>
        #include"processBar.h"
        #include<unistd.h>
        
        int main()
        {
            processBar(50000);
        
            return 0;
        }

    2. 方法二:更好的适应正常情况下载东西时的表现
      1. processBar.c
        #include "processBar.h"
        
        const char *lable = "|/-\\";// \ 反斜杠是特殊的字符所以需要用\\ 来表示成\ 
        
        char bar[102] = {0};
        //函数processBar来展示下载的过程
        //原理和之前一样,只不过此时把逐渐完成的过程放到了外部
        void initbar()//清理数组中的内容
        {
            memset(bar,'\0',sizeof(bar));//将内容全部置为\0
        }
        
        void processBar(int rata)
        {
            if(rata < 0 || rata > 100) return ;
            int len =  strlen(lable);
            
            printf("[%-100s][%d%%][%c]\r",bar,rata,lable[rata%len]); 
            fflush(stdout);
            bar[rata++] = BODY;
            if(rata < 100) bar[rata] = RIGHT;
                
        }
        
      2. processBar.h

        #include<stdio.h>
        #include<unistd.h>
        #include<string.h>
        
        #define BODY '='
        
        #define RIGHT '>'
        
        typedef void (*processbar)(int);//将函数指针类型重命名为processbar 然后利用函数指针来实现一个回调函数的过程
        
        void processBar(int rata);
        
        //extern void processBar(); extern 可写可不写因为对于函数来说这样写就表示这是一个声明
        
        //但注意的是假如是一个变量的话就需要加上extern了因为分不清到底是不是编译器声明
      3. main.c

        #include<stdio.h>
        #include"processBar.h"
        #include<unistd.h>
        // 模拟 下载 的情况
        void DownLoad(processbar i)//利用函数指针
        {
            int total = 1000;//假如有1000MB
            int cur = 0;//一开始从0开始
            while(cur <= total)
            {
                int rata  = cur*100 / total;//计算完成的百分比
                i(rata);//把rata传给i 也就是 processBar
                cur += 10;//模拟下载逐渐完成
                usleep(50000);//模拟下载的所需的时间
            }
            printf("\n");
        }
        
        int main()
        {
            DownLoad(processBar);
            initbar();//清理一下数组内容,为下面处理时腾出空的空间
            return 0;
        }
        

本章完。预知后事如何,暂听下回分解。

如果有任何问题欢迎讨论哈!

如果觉得这篇文章对你有所帮助的话点点赞吧!

持续更新大量Linux细致内容,早关注不迷路。

简介 XMake 是一个跨平台自动构建工具,支持在各种主流平台上构建项目,类似 cmake、automake、premake,但是更加的方便易用,工程描述语法更简洁直观,支持平台更多,并且集创建、配置、编译、打包、安装、卸载、运行于一体。 支持特性 支持windows、mac、linux、ios、android等平台,自动检测不同平台上的编译工具链(也可手动配置) 编译windows项目采用原生vs的工具链,不需要使用cygwin、mingw(当然这些也支持) 支持自定义平台编译配置,可以很方便的扩展第三方平台支持 采用lua脚本语法描述项目,描述规则简单高效,逻辑规则可灵活修改,并且不会生成相关平台的工程文件,是工程更加简单明了 支持创建模板工程、配置项目、编译项目、运行、打包、安装和卸载等常用功能(后续还会增加:自动生成文档、调试等模块) 支持编译c/c /objc成静态库、动态库、命令行可执行程序(后续还会增加:mac、ios、android的app的生成规则) 提供丰富的工程描述api,使用简单灵活,例如添加编译文件只需(还支持过滤排除): add_files("src/*.c", "src/asm/**.S", "src/*.m") 支持头文件、接口、链接库依赖、类型的自动检测,并可自动生成配置头文件config.h 支持自定义编译配置开关,例如如果在工程描述文件中增加了enable_xxx的开关,那么配置编译的时候就可以手动进行配置来启用它: xmake config --enable_xxx=true 提供一键打包功能,不管在哪个平台上进行打包,都只需要执行一条相同的命令,非常的方便 支持自定义编译工具和规则,例如想要增加对masm/yasm的编译规则,只需将自己写的masm.lua/yasm.lua规则文件,放到当前项目目录下即可。。 支持全局配置,一些常用的项目配置,例如工具链、规则描述等等,都可以进行全局配置,这样就不需要每次编译不同工程,都去配置一遍 除了可以自动检测依赖模块,也支持手动强制配置模块,还有各种编译flags。 简单例子 创建一个c console项目:xmake create -l c  -t 1 console  or xmake create --language=c  --template=1 console 工程描述文件:xmake.luaadd_target("console")     set_kind("binary")     add_files("src/*.c") 配置工程: 这个是可选的步骤,如果只想编译当前主机平台的项目,是可以不用配置的,默认编译release版本。   当然每次配置都会被缓存,不需要每次全部重新配置。xmake f -p iphoneos -m debug or xmake f --ldflags="-Lxxx -lxxx" or xmake f --plat=macosx --arch=x86_64 or xmake config --plat=iphoneos --mode=debug or xmake config --plat=iphonesimulator or xmake config --plat=android --arch=armv7-a --ndk=xxxxx or xmake config --cross=i386-mingw32- --toolchains=/xxx/bin or xmake config --cxflags="-Dxxx -Ixxx" or xmake config --help 编译工程:xmake or xmake -r or xmake --rebuild 运行目标:xmake r console or xmake run console 打包所有:xmake p or xmake p --archs="armv7, arm64" or xmake package or xmake package console or xmake package -o /tmp or xmake package --output=/tmp 安装目标:xmake i or xmake install or xmake install console or xmake install -o /tmp or xmake install --output=/tmp 详细使用方式和参数说明,请参考文档 或者运行:xmake -h or xmake --help or xmake config --help or xmake package --help ... 也可以参考使用xmake的实际项目:TBOX 后续工作 完善打包模块,支持对ios、mac、android的app进行一键打包和签名,生成.ipa、.apk、.app的应用程序文件 完善安装功能,支持对ios、android的app进行安装到设备 实现调试功能 实现自动生成doxygen文档功能 增加一些实用的工程描述api,例如:下载api,可以自动下载缺少的依赖库等等。。 解析automake、cmake的工程,并自动生成xmake的描述文件,实现无缝编译(如果这个实现成功的话,以后移植编译一些开源代码就更方便了) 标签:构建工具
评论 77
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

溟洵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值