Makefile原理及使用

2023-11-02

makefile

make 是一个命令工具,是一个解释 makefile 中指令的命令工具。make 工具在构造项目的时候需要加载一个叫做 makefile 的文件,makefile 关系到了整个工程的编译规则。


一、规则

每条规则由三个部分组成分别是目标(target), 依赖(depend) 命令(command)
在这里插入图片描述
命令(command): 当前这条规则的动作,一般情况下这个动作就是一个 shell 命令
例如:通过某个命令编译文件、生成库文件、进入目录等。动作可以是多个,每个命令前必须有一个Tab缩进并且独占占一行。
依赖(depend): 规则所必需的依赖条件,在规则的命令中可以使用这些依赖。
例如:生成可执行文件的目标文件(*.o)可以作为依赖使用如果规则的命令中不需要任何依赖,那么规则的依赖可以为空当前规则中的依赖可以是其他规则中的某个目标,这样就形成了规则之间的嵌套依赖可以根据要执行的命令的实际需求,指定很多个.
目标(target): 规则中的目标,这个目标和规则中的命令是对应的通过执行规则中的命令,可以生成一个和目标同名的文件规则中可以有多个命令,因此可以通过这多条命令来生成多个目标,所有目标也可以有很多个通过执行规则中的命令,可以只执行一个动作,不生成任何文件,这样的目标被称为伪目标。例如:

# 举例: 有源文件 a.c b.c c.c head.h, 需要生成可执行程序 app
################# 例1 #################
app:a.c b.c c.c
	gcc a.c b.c c.c -o app

################# 例2 #################
# 有多个目标, 多个依赖, 多个命令
app,app1:a.c b.c c.c d.c
	gcc a.c b.c -o app
	gcc c.c d.c -o app1
	
################# 例3 #################	
# 规则之间的嵌套
app:a.o b.o c.o
	gcc a.o b.o c.o -o app
# a.o 是第一条规则中的依赖
a.o:a.c
	gcc -c a.c
# b.o 是第一条规则中的依赖
b.o:b.c
	gcc -c b.c
# c.o 是第一条规则中的依赖
c.o:c.c
	gcc -c c.c

二、工作原理

在调用 make 命令编译程序的时候,make 会首先找到当前目录中的 Makefile 文件中的第 1 个规则,分析并执行相关的动作(如果当前路径没有Makefile则make不起作用)。但是需要注意的是,好多时候要执行的动作(命令)中使用的依赖是不存在的,如果使用的依赖不存在,这个动作也就不会被执行。

对应的解决方案是先将需要的依赖生成出来,我们就可以在 makefile 中添加新的规则,将不存在的依赖作为这个新的规则中的目标,当这条新的规则对应的命令执行完毕,对应的目标就被生成了,同时另一条规则中需要的依赖也就存在了。

这样,makefile 中的某一条规则在需要的时候,就会被其他的规则调用,直到 makefile 中的第一条规则中的所有的依赖全部被生成,第一条规则中的命令就可以基于这些依赖生成对应的目标,make 的任务也就完成了。

MAKEFILE
# makefile
# 规则之间的嵌套
# 规则1
app:a.o b.o c.o
	gcc a.o b.o c.o -o app
# 规则2
a.o:a.c
	gcc -c a.c
# 规则3
b.o:b.c
	gcc -c b.c
# 规则4
c.o:c.c
	gcc -c c.c

知识点拓展:
如果想要执行 makefile 中非第一条规则对应的命令,那么就不能直接 make, 需要将那条规则的目标也写到 make 的后边,比如只需要执行规则 3 中的命令,就需要: make b.o。

三、自动推导

make 有自动推导的能力,不会完全依赖 makefile。例如:
在这里插入图片描述

四、变量

使用 Makefile 进行规则定义的时候,为了写起来更加灵活,我们可以在里边使用变量。makefile 中的变量分为三种:自定义变量预定义变量自动变量

4.1 自定义变量

用 Makefile 进行规则定义的时候,用户可以定义自己的变量,称为用户自定义变量。makefile 中的变量是没有类型的,直接创建变量然后给其赋值就可以了。

# 错误, 只创建了变量名, 没有赋值
变量名 
# 正确, 创建一个变量名并且给其赋值
变量名=变量值

在给 makefile 中的变量赋值之后,如何在需要的时候将变量值取出来呢?

# 如果将变量的值取出?
$(变量的名字)

# 举例 add.o  div.o  main.o  mult.o  sub.o
# 定义变量并赋值
obj=add.o  div.o  main.o  mult.o  sub.o
# 取变量的值
$(obj)

自定义变量使用举例:

# 这是一个规则,普通写法
calc:add.o  div.o  main.o  mult.o  sub.o
        gcc  add.o  div.o  main.o  mult.o  sub.o -o calc
        
# 这是一个规则,里边使用了自定义变量
obj=add.o  div.o  main.o  mult.o  sub.o
target=calc
$(target):$(obj)
        gcc  $(obj) -o $(target)

4.2 预定义变量

在 Makefile 中有一些已经定义的变量,用户可以直接使用这些变量,不用进行定义。在进行编译的时候,某些条件下 Makefile 会使用这些预定义变量的值进行编译。这些预定义变量的名字一般都是大写的,经常采用的预定义变量如下表所示:
在这里插入图片描述
示例:

# 这是一个规则,普通写法
calc:add.o  div.o  main.o  mult.o  sub.o
        gcc  add.o  div.o  main.o  mult.o  sub.o -o calc
        
# 这是一个规则,里边使用了自定义变量和预定义变量
obj=add.o  div.o  main.o  mult.o  sub.o
target=calc
CFLAGS=-O3 # 代码优化
$(target):$(obj)
        $(CC)  $(obj) -o $(target) $(CFLAGS)

4.3 自动变量

Makefile 中的变量除了用户自定义变量和预定义变量外,还有一类自动变量。Makefile 中的规则语句中经常会出现目标文件和依赖文件,自动变量用来代表这些规则中的目标文件和依赖文件,并且它们只能在规则的命令中使用。

下表中是一些常见的自动变量。
在这里插入图片描述
示例:

# 这是一个规则,普通写法
calc:add.o  div.o  main.o  mult.o  sub.o
        gcc  add.o  div.o  main.o  mult.o  sub.o -o calc
        
# 这是一个规则,里边使用了自定义变量
# 使用自动变量, 替换相关的内容
calc:add.o  div.o  main.o  mult.o  sub.o
	gcc $^ -o $@ 			# 自动变量只能在规则的命令中使用

五、模式匹配

在介绍概念之前,先读一下下面的这个 makefile 文件:

calc:add.o  div.o  main.o  mult.o  sub.o
        gcc  add.o  div.o  main.o  mult.o  sub.o -o calc
# 语法格式重复的规则, 将 .c -> .o, 使用的命令都是一样的 gcc *.c -c
add.o:add.c
        gcc add.c -c

div.o:div.c
        gcc div.c -c

main.o:main.c
        gcc main.c -c

sub.o:sub.c
        gcc sub.c -c

mult.o:mult.c
        gcc mult.c -c

在阅读过程中能够发现从第二个规则开始到第六个规则做的是相同的事情,但是由于文件名不同不得不在文件中写出多个规则,这就让 makefile 文件看起来非常的冗余,我们可以将这一系列的相同操作整理成一个模板,所有类似的操作都通过模板去匹配 makefile 会因此而精简不少,只是可读性会有所下降。

# 模式匹配 -> 通过一个公式, 代表若干个满足条件的规则
# 依赖有一个, 后缀为.c, 生成的目标是一个 .o 的文件, % 是一个通配符, 匹配的是文件名
%.o:%.c
	gcc $< -c

在这里插入图片描述

六、函数

makefile 中有很多函数并且所有的函数都是有返回值的。makefile 中函数的格式和 C/C++ 中函数也不同,其写法是这样的: $(函数名 参数1, 参数2, 参数3, ...),主要目的是让我们能够快速方便的得到函数的返回值。
这里介绍两个 makefile 中使用频率比较高的函数:wildcard 和 patsubst。

6.1 wildcard

这个函数的主要作用是获取指定目录下指定类型的文件名,其返回值是以空格分割的、指定目录下的所有符合条件的文件名列表。函数原型如下:

# 该函数的参数只有一个, 但是这个参数可以分成若干个部分, 通过空格间隔
$(wildcard PATTERN...)
	参数:	指定某个目录, 搜索这个路径下指定类型的文件,比如: *.c
  • 参数功能:
    • PATTERN 指的是某个或多个目录下的对应的某种类型的文件,比如当前目录下的.c 文件可以写成 *.c
    • 可以指定多个目录,每个路径之间使用空格间隔
  • 返回值:
    • 得到的若干个文件的文件列表, 文件名之间使用空格间隔
    • 示例:$(wildcard .c ./sub/.c)
      • 返回值格式: a.c b.c c.c d.c e.c f.c ./sub/aa.c ./sub/bb.c
        示例:
# 使用举例: 分别搜索三个不同目录下的 .c 格式的源文件
src = $(wildcard /home/robin/a/*.c /home/robin/b/*.c *.c)  # *.c == ./*.c
# 返回值: 得到一个大的字符串, 里边有若干个满足条件的文件名, 文件名之间使用空格间隔
/home/robin/a/a.c /home/robin/a/b.c /home/robin/b/c.c /home/robin/b/d.c e.c f.c

6.2 patsubst

这个函数的功能是按照指定的模式替换指定的文件名的后缀,函数原型如下:

# 有三个参数, 参数之间使用 逗号间隔
$(patsubst <pattern>,<replacement>,<text>)
  • 参数功能:
    • pattern: 这是一个模式字符串,需要指定出要被替换的文件名中的后缀是什么
      • 文件名和路径不需要关心,因此使用 % 表示即可 [通配符是 %]
      • 在通配符后边指定出要被替换的后缀,比如: %.c, 意味着 .c 的后缀要被替换掉
    • replacement: 这是一个模式字符串,指定参数 pattern 中的后缀最终要被替换为什么
      • 还是使用 % 来表示参数 pattern 中文件的路径和名字
      • 在通配符 % 后边指定出新的后缀名,比如: %.o 这表示原来的后缀被替换为 .o
    • text: 该参数中存储这要被替换的原始数据
    • 返回值:
      • 函数返回被替换过后的字符串。
        示例:
src = a.cpp b.cpp c.cpp e.cpp
# 把变量 src 中的所有文件名的后缀从 .cpp 替换为 .o
obj = $(patsubst %.cpp, %.o, $(src)) 
# obj 的值为: a.o b.o c.o e.o

Makefile示例:

# 添加自定义变量 -> makefile中注释前 使用 # 
# 使用函数搜索当前目录下的源文件 .c
src=$(wildcard *.c)
# 将源文件的后缀替换为 .o
obj=$(patsubst %.c, %.o, $(src))
target=calc

$(target):$(obj)
        gcc $(obj)  -o $(target)

%.o:%.c
        gcc $< -c

# 添加规则, 删除生成文件 *.o 可执行程序
# 声明clean为伪文件
.PHONY:clean
clean:
        # shell命令前的 - 表示强制这个指令执行, 如果执行失败也不会终止
        -rm $(obj) $(target) 
        echo "hello, 我是测试字符串"

注意:clean 是一个伪目标,不对应任何实体文件,在前边讲关于文件时间戳更新问题的时候说过,如果目标不存在规则的命令肯定被执行, 如果目标文件存在了就需要比较规则中目标文件和依赖文件的时间戳,满足条件才执行规则的命令,否则不执行。

解决这个问题需要在 makefile 中声明 clean 是一个伪目标,这样 make 就不会对文件的时间戳进行检测,规则中的命令也就每次都会被执行了。

在 makefile 中声明一个伪目标需要使用 .PHONY 关键字,声明方式为: .PHONY:伪文件名称

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Makefile原理及使用 的相关文章

  • 使用 posix_spawn 启动进程

    我正在使用以下代码在 Linux 中启动新进程 pid t processID char argV 192 168 1 40 char 0 int status 1 status posix spawn processID home use
  • 如何将后台作业的输出分配给 bash 变量?

    我想在 bash 中运行后台作业并将其结果分配给一个变量 我不喜欢使用临时文件 并且希望同时运行多个类似的后台任务 root root var echo hello world root root echo var hello world
  • 配置:错误:无法运行C编译的程序

    我正在尝试使用 Debian Wheezy 操作系统在我的 Raspberry Pi 上安装不同的软件 当我运行尝试配置软件时 我尝试安装我得到此输出 checking for C compiler default output file
  • 在 Linux 上访问 main 之外的主要参数

    是否可以访问参数main在外面main 即在共享库构造函数中 在 Linux 上除了通过解析之外 proc self cmdline 您可以通过将构造函数放入 init array部分 功能在 init array 不像 init 使用相同
  • pprof 和 ps 之间的内存使用差异

    我一直在尝试分析用 cobra 构建的 cli 工具的堆使用情况 这pprof工具显示如下 Flat Flat Sum Cum Cum Name Inlined 1 58GB 49 98 49 98 1 58GB 49 98 os Read
  • 如何在gnuplot中将字符串转换为数字

    有没有办法将表示数字 以科学格式 的字符串转换为 gnuplot 中的数字 IE stringnumber 1 0e0 number myconvert stringnumber plot 1 1 number 我可能使用 shell 命令
  • 如何确定代码是否在信号处理程序上下文中运行?

    我刚刚发现有人正在从信号处理程序调用我编写的绝对不是异步信号安全的函数 所以 现在我很好奇 如何避免这种情况再次发生 我希望能够轻松确定我的代码是否在信号处理程序上下文中运行 语言是 C 但该解决方案不适用于任何语言吗 int myfunc
  • 如何从远程 ssh 连接上运行的 tmux(复制模式)复制到本地剪贴板

    我通过 OS X 上的 VirtualBox 运行 Linux 我通过在无头状态下运行虚拟机 然后使用端口转发 sshing 到 Linux 机器来实现这一点 现在 无论复制到我的虚拟机上的剪贴板 我都可以粘贴到我的远程 ssh 会话上 但
  • 将node.js +expressjs应用程序的NODE_ENV设置为ubuntu下的守护进程

    我按照这些说明让守护进程正常工作 http kevin vanzonneveld net techblog article run nodejs as a service on ubuntu karmic http kevin vanzon
  • 在 debian wheezy amd64 上安装 ia32-libs

    我正在使用 Debian 7 喘息 amd64 uname a Linux tzwm debian 3 2 0 4 amd64 1 SMP Debian 3 2 51 1 x86 64 GNU Linux 我想安装ia32 libs在我的
  • 使用 ProcessBuilder 运行 shell 脚本

    我正在尝试使用 Java 和 ProcessBuilder 运行脚本 当我尝试运行时 我收到以下消息 error 2 没有这样的文件或目录 我不知道我做错了什么 但这是我的代码 ps 我尝试只执行不带参数的脚本 错误是相同的 String
  • 如何在 Linux/OS X 上温和地终止 Firefox 进程

    我正在使用 Firefox 进行一些自动化操作 尽管我可以从 shell 打开 Firefox 窗口 但我无法正确终止它 如果我kill火狐进程与kill 3 or kill 2当我下次打开新的 Firefox 窗口时 命令会询问我是否要在
  • 计算 TCP 重传次数

    我想知道在LINUX中是否有一种方法可以计算一个流中发生的TCP重传的次数 无论是在客户端还是服务器端 好像netstat s解决了我的目的
  • 在 Linux 控制台中返回一行?

    我知道我可以返回该行并用以下内容覆盖其内容 r 现在我怎样才能进入上一行来改变它呢 或者有没有办法打印到控制台窗口中的特定光标位置 我的目标是使用 PHP 创建一些自刷新的多行控制台应用程序 Use ANSI 转义码 http en wik
  • SMP 上如何处理中断?

    SMP 对称多处理器 多核 机器上如何处理中断 内存管理单元是只有一个还是多个 假设两个线程 A 和 B 运行在不同的内核上 同时 访问页表中不存在的内存页面 在这种情况下 将会出现页面错误 并从内存中引入新页面 将会发生的事件的顺序是什么
  • 如何将 elf 解释器(ld-linux.so.2/ld-2.17.so)构建为静态库?

    如果我的问题不准确 我深表歉意 因为我没有太多 Linux 相关经验 我目前正在构建一个 Linux 从头开始 主要遵循 linuxfromscratch org 版本的指南 7 3 我遇到了以下问题 当我构建可执行文件时 获取一个称为 E
  • 了解 Linux oom-killer 日志

    我的应用程序被 oom killer 杀死了 它是在实时 USB 上运行的 Ubuntu 11 10 无需交换 PC 具有 1 Gig 的 RAM 唯一运行的应用程序 除了所有内置的 Ubuntu 东西 是我的程序 flasherav 请注
  • php_network_getaddresses: getaddrinfo 失败: 名称或服务未知 (0) 连接失败..!

    我正在使用 php 邮件程序功能 但出现以下错误 如何修复它 2016 01 22 06 15 48 SMTP 错误 无法连接到服务器 php network getaddresses getaddrinfo失败 名称或服务未知 0 连接失
  • 在用户程序中使用 或在驱动程序模块代码中使用 ...这有关系吗?

    我正在开发一个设备驱动程序模块和关联的用户库来处理ioctl 来电 该库获取相关信息并将其放入一个结构中 该结构被传递到驱动程序模块中并在那里解压 然后进行处理 我省略了很多步骤 但这就是总体思路 一些数据通过结构体传递ioctl is u
  • 亚马逊 Linux - 安装 openjdk-debuginfo?

    我试图使用jstack在 ec2 实例上amazon linux 所以我安装了openjdk devel包裹 sudo yum install java 1 7 0 openjdk devel x86 64 但是 jstack 引发了异常j

随机推荐

  • 知识蒸馏的说明

    本文参考 ChatGPT 温度系数t与top p 超参怎么设置最优 知乎 知识蒸馏系列 一 三类基础蒸馏算法 OpenMMLab的博客 CSDN博客 知识蒸馏算法汇总 知乎 知识蒸馏 Old Summer的博客 CSDN博客 目录 1 Ch
  • IDEA批量替换文件换行符、分隔符CRLF、LF、CR

    全局设置 只对新文件有效 file gt setting gt code style gt Line separator 选中需要替换的分隔符类型 注意 Line separator下面有行小字 applied to new files 意
  • 各种邮箱服务软件对比

    1 宝塔邮局管理器 特点 简单易用 可视化操作 小白也能搞 还有备份功能 一般足够用了 缺点 稳定性真是差 隔三差五的不能收发 没有接口 不能任意修改邮箱密码 只能管理员修改 注意要点 一定要开启ssl 否则有些邮箱给你发邮件你收不到 建议
  • python 学习笔记07: set(集合)类型的操作

    coding UTF 8 version Python2 7 15 set gt 集合学习笔记 Help on class set in module builtin class set object set gt new empty se
  • windows-x86安装qemu-arm虚拟机及文件互传

    一 规划安装目录及环境准备 a 这里是在D盘创建 vm arm64文件夹 然后再里面部署 正在上传 重新上传取消 b 在D盘创建一个存放镜像的目录 image并把镜像放到里面 c 下载qemu 并将软件安装在D vm arm64下qemu下
  • 【解决方案】5G时代浪潮来袭,EasyNVR助力5G厂区视频监控安防采集可视化展示

    智慧工厂被认为是5G技术的重要应用场景之一 利用5G网络将生产设备无缝连接 并进一步打通设计 采购 仓储 物流等环节 满足工业环境下设备互联和远程交互应用需求 TSINGSEE青犀视频面向工厂智能化升级需求 推出5G 智慧工厂方案 构建连接
  • Java-Final关键字

    Java Final关键字 1 概念 final 最终的 final可以修饰的结构 类 最终的类 此类不能被其他类继承 比如String类 StringBuffer类 方法 最终的方法 此方法不能被重写 比如Object 类中的getCla
  • JVM小册(1)------jstat和Parallel GC日志

    JVM小册 1 jstat和Parallel GC日志 一 背景 在生产环境中 有时候会遇到OOM的情况 抛开Arthas 等比较成熟的工具以外 我们可以使用java 提供的jatat和jps jmap等工具来帮助我们排查问题和定位原因 本
  • Kubernetes笔记(6) - Service和Ingress

    Service资源概述 创建Service资源 向Service对象请求服务 Service会话粘性 服务发现 服务暴露 Ingress和Ingress Controller Ingress资源 Ingress控制器 Service资源概述
  • FID(Fusion-in-Decoder models)源码笔记

    源码 源码 https github com facebookresearch FiD 目录 源码 数据集 数据格式 预训练模型 训练 测试 src slurm py 资源调度管理 util py 配置管理 evaluation py 查找
  • 夜光带你走进 传奇语言C#(24)

    夜光序言 一只站在树上的鸟儿 从来不会害怕树枝断裂 因为它相信的不是树枝 而是它自己的翅膀 有时候 经济不独立 你发的一切飚都是浮云 正文 任务18 班级编码表维护
  • oracle 16058,Oracle 11g Data Guard ORA-16058 错误处理

    采用RMAN 备份恢复搭建Oracle 11g的Data Guard 恢复结束之后 DG 同步一直异常 主库提示如下信息 https www cndba cn Dave article 4330 SQL gt select DEST NAM
  • java设计模式之观察者模式(含完整例子和UML类图)

    java设计模式之观察者模式 1 观察者模式 1 1定义 观察者 Observer 模式的定义 指多个对象间存在一对多的依赖关系 当一个对象的状态发生改变时 所有依赖于它的对象都得到通知并被自动更新 这种模式有时又称作发布 订阅模式 模型
  • 高性能服务器架构思路(一)——缓冲策略

    本文首发腾云阁 高性能服务器架构思路 一 缓冲策略 作者介绍 韩伟 1999年大学实习期加入初创期的网易 成为第30号员工 8年间从程序员开始 历任项目经理 产品总监 2007年后创业4年 开发过视频直播社区 及多款页游产品 2011年后就
  • 孕妇有什么副业做?孕妇在家有哪些兼职可以做?

    孕妇有什么副业做 孕妇在家有哪些兼职可以做 孕妇在家选择兼职工作时 有一点一定要遵循 不能从事极端的体力工作 因为体力工作容易让孕妇出现并发症 所以孕妇一定要选择一些没有太大压力的兼职工作 有很多兼职工作时间很自由 所以特别适合怀孕的女性做
  • LAMP部署

    文章目录 LAMP简介 web服务器 web服务器的资源分为两种 静态资源和动态资源 工作流程 http响应报文 httpd与php结合的方式 httpd与php结合的方式有以下三种 lamp平台构建 环境说明
  • 不会下载软件?这5个网站别错过,纯净、安全、无捆绑

    虽然下载网站有很多 但是一不小心就会下载到各种捆绑安装包 这里就给大家分享5个比较靠谱的软件下载网站 纯净 安全 无捆绑 可以放心使用 1 Microsoft Store 一个微软旗下的电脑软件商城 它里面有很多类型的软件可以下载 首页也有
  • c/c++中,预编译指令用法汇总

    切换模式 写文章 登录 注册
  • java多线程实战( 多个线程 修改同一个变量)

    java多线程实战 多个线程 修改同一个变量 synchronized 同步 介绍 java多线程实战 需求 创建两个线程 分别输出 a b 要求输出总和为30个 线程介绍 一 定义线程 1 扩展java lang Thread类 此类中有
  • Makefile原理及使用

    makefile make 是一个命令工具 是一个解释 makefile 中指令的命令工具 make 工具在构造项目的时候需要加载一个叫做 makefile 的文件 makefile 关系到了整个工程的编译规则 文章目录 makefile