当前位置: 首页 > news >正文

15-makefile

一、Makefile的概述

1.认识make

  1. make 是一个命令,是个可执行程序,用来解析 Makefile 文件的命令;
  2. linux 环境下,这个命令存放在 /usr/bin/ 目录下;
  3. 当用户输入 make 指令时,系统会自动寻找 makefile、Makefile、GNUmakefile 中任何一个,根据该文件中的指令编译工程。

2.什么是makefile

  1. makefile 是一个编译指令的脚本,里面记录了一条条编译指令,用于描述程序的编译规则;
  2. Makefile 文件里面的编译指令是根据我们自己写的程序,编写的编译规则。

3.makeflie与gcc的对比

  1. 用 gcc 编译的时候,我们要在后面跟上要编译的文件名,如:

    gcc main.c func.c
    

    文件量和代码量较少的时候,用这种方式编译还比较方便,但如果整体项目很多,文件很多的时候,编译起来就比较困难,而且还容易漏掉一些文件。没人能保证自己的代码一次运行成功,会反复编译,调试,那每次调试都需要输入很长的 gcc 命令。遇到后期追加功能的时候,时间长了,也忘记项目用到了哪些文件了,对后期工作也会造成困扰;

  2. 而在 makeflie 里面提前写好了编译指令,只是在编写 makefile 文件的时候需要花费时间,但后面重复编译每次只需要 make 指令调用编译就行了,简化了编译过程。同时后期即使追加功能,以前的编译的文件指令都保存在 makefile 文件里了,也不用担心忘记需要编译哪些文件;

  3. gcc 编译很大项目的时候,编译时间会很长,如果对代码进行了修改再编译,即使没有修改代码的文件,也要一起重新编译一次,又得消耗很长的时间;

  4. makefile 只会对修改过的代码文件重新编译,其它未修改的直接使用以前编译好的可执行文件,除了第一次编译需要花费较长时间外,后面编译都比较快。因此,make 不仅简化了编译步骤,还节省编译时间,提高编译效率。

二、makefile的语法规则

1.makefile语法规则

1.1语法

先创建一个 makefile 文件,在文件里面写命令:

目标:依赖文件列表命令列表 // 命令列表前有缩进
  • gcc 编译
gcc main.c -o main
  1. 目标:通常是要产生的可执行文件名,如上面的 main;
  2. 依赖文件:是用来输入从而产生目标的文件,一个目标通常有一个或多个依赖文件,如上面的 main.c;
  3. 命令:make 执行的动作,一个规则可以含几个命令,有多个命令时,每个命令占一行,如上面的gcc main.c -o main
  • 注意: makefile 文件里面写的是脚本,要严格遵守脚本的语法规则,有时候多一个少一个空格都可能造成错误。

1.2makefile编译演示

先创建 makefile 文件,在文件里写入如下命令:

main:main.c func.cgcc main.c func.c -o main

接着在 linux 终端输入make命令即可实现编译。

  • 注意:上面只是演示 makefile 命令的语法,和真正的 makefile 实现的功能差远了。这里的 make 只会执行第一个目标的语句,如果下方还有其它语句不会执行。

1.3make命令格式

1.3.1自定义 makefile 文件名
make -f 自定义文件名
  • 在终端输入 make 命令时默认在工作目录中寻找名为 GNUmakefile、makefile、Makefile 的文件作为 makefile 输入文件,但如果我们不按照上面的文件名命名,而是修改为自定义的文件名,那么就需要在 make 命令后面加上我们自定义的文件名,如:
make -f my_makefile

但一般不建议这样做。

1.3.2make后跟其它目标

前面说到,make 执行第一个目标的语句后,后面目标的语句就不会执行了,那要执行后面目标的语句,我们可以指定目标:

make 目标
  • 例如:makefile
main:main.c func.cgcc main.c func.c -o main
clean:rm main.c

可以通过输入:终端输入make clean,来执行指定的语句。

1.3.3复杂版makefile
main:main.o fun.ogcc main.o fun.o -o main
main.o:main.cgcc -c main.c -o main.o
fun.o:fun.cgcc -c fun.c -o fun.o
clean:rm main *.o
  • 说明:
    1. 当终端输入 make 命令时,会执行 makefile 文件里的第一条命令,但发现依赖的main.o fun.o文件不存在,会执行下面的命令,先生成main.o fun.o再执行第一条的命令;
    2. *.o里面的 * 是通配符,这里表示所有以 .o结尾的文件;
    3. clean:为假想目标,只有目标,没有依赖。

三、makefile的变量

makefile 变量类似于 C 语言中的宏,当 makefile 被 make 工具解析时,其中的变量会被展开。变量一般用于:保存文件名列表、保存文件目录列表、保存编译器名、保存编译参数、保存编译的输出等。

1.自定义变量

  • 定义格式
变量名=变量值
  • 说明

    1. 注意,等号两边没有空格,应该严格遵循脚本语言的语法编写;
    2. makefile 变量名可以以数字开头,区分大小写;
    3. 变量一般都在 makefile 的头部定义,几乎可在 makefile 的任何地方使用;
    4. 如果需要读取变量的值,需要在变量名前加上美元符号 , , (变量名)或${变量名};
    5. 自定义变量的作用,主要是为了方便切换不同编译方式,方便指定生成的可执行文件名等,如果一个字符在命令中出现多次,就可以通过变量来代替,如:
    CC=gcc
    EXEC=main$(EXEC):main.o fun.o$(CC) main.o fun.o -o $(EXEC)
    main.o:main.c$(CC) -c main.c -o main.o
    fun.o:fun.c$(CC) -c fun.c -o fun.o
    clean:rm $(EXEC) *.o
    

2.系统环境变量

make 工具会拷贝系统的环境变量并将其设置为 makefile 的变量,在 makefile 中可直接读取或修改拷贝后的变量,读取环境变量的方法和前面读取自定义变量的方法相同,如:

CC=gcc
EXEC=main$(EXEC):main.o fun.o$(CC) main.o fun.o -o $(EXEC)
main.o:main.c$(CC) -c main.c -o main.o
fun.o:fun.c$(CC) -c fun.c -o fun.o
clean:rm $(EXEC) *.oecho $(PWD) // PWD 是一个环境变量   
  • 说明:
    1. echo 相当于C语言里的 printf,将内容输出到终端;
    2. 我们除了可以使用系统的环境变量,还可以自己导出环境变量:export NUM=100
    3. 自己导出的环境变量只在当前终端有效,不会影响系统的环境变量。

3.预定义环境变量

预定义环境变量即 makefile 定义好的环境变量。

  1. $@ 当前命令的目标名
  2. $< 当前命令依赖文件列表中的第一个文件
  3. $^ 当前命令依赖文件列表,带有除去重复文件的功能
  4. AR 归档维护程序的程序名,默认值为 ar
  5. ARFLAGS 归档维护程序的选项
  6. AS 汇编程序的名称,默认值为 as
  7. ASFLAGS 汇编程序的选项
  8. CC C 编译器的名称,默认值为 cc
  9. CFLAGS C 编译器的选项
  10. CPP C 预编译器的名称,默认值为$(CC) -E
  11. CPPFLAGS C 预编译的选项
  12. CXX C++编译器的名称,默认值为 g++
  13. CXXFLAGS C++编译器的选项

较常用的是前三个,对上面命令的升级版:

CC=gcc
EXEC=main$(EXEC):main.o fun.o$(CC) $^ -o $@
main.o:main.c$(CC) -c $< -o $@
fun.o:fun.c$(CC) -c $< -o $@
clean:rm $(EXEC) *.oecho $(PWD)

上面的代码可以发现,7、8行代码重复,因此可以合并,使用通配符 %:

CC=gcc
EXEC=main
OBJ=main.o fun.o // 将依赖文件通过自定义变量保存    $(EXEC):$(OBJ)$(CC) $^ -o $@
%.o:%.c$(CC) -c $< -o $@
clean:rm $(EXEC) $(OBJ)echo $(PWD)
http://www.lryc.cn/news/509769.html

相关文章:

  • yii2 手动添加 phpoffice\phpexcel
  • 使用 AI 辅助开发一个开源 IP 信息查询工具:一
  • HNUST-数据分析技术课堂实验
  • P3456 [POI2007] GRZ-Ridges and Valleys BFS-连通块思想
  • WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
  • Clickhouse(Centos)
  • Yolo11改进策略:Block改进|使用FastVit的RepMixerBlock改进Yolo11,重参数重构助力Yolo11涨点(全网首发)
  • 微信小程序-基于Vant Weapp UI 组件库的Area 省市区选择
  • NIO(New IO)和BIO(Blocking IO)的区别
  • ROS1入门教程6:复杂行为处理
  • 碰撞检测算法之闵可夫斯基差集法(Minkowski Difference)
  • 【唐叔学算法】第18天:解密选择排序的双重魅力-直接选择排序与堆排序的Java实现及性能剖析
  • 2008-2020年各省技术服务水平相关指标数据
  • 机器学习DAY4续:梯度提升与 XGBoost (完)
  • ML-Agents:训练配置文件(一)
  • 【物联网技术与应用】 实验13:雨滴传感器实验
  • 帝国cms电脑pc站url跳转到手机站url的方法
  • Django models中的增删改查与MySQL SQL的对应关系
  • 双指针——快乐数
  • Docker 默认安装位置迁移
  • jmeter跨进程实现变量共享-全局变量
  • Vue.js组件(6):echarts组件
  • yolov3算法及其改进
  • Python + 深度学习从 0 到 1(02 / 99)
  • HTML+CSS+JS制作在线书城网站(内附源码,含5个页面)
  • 【FastAPI】中间件
  • 5个实用的设计相关的AI网站
  • STL 六大组件
  • Python选择题训练工具:高效学习、答题回顾与音频朗读一站式体验
  • Python实现机器学习驱动的智能医疗预测模型系统的示例代码框架