make学习三:书写规则

系列文章目录

Make学习一:make初探

Make学习二:makefile组成要素

文章目录

  • 系列文章目录
  • 前言
  • 默认目标
  • 规则语法
  • order-only prerequisites
  • 文件名中的通配符
  • 伪目标 Phony Targets
  • 没有 Prerequisites 和 recipe
  • 内建特殊目标名
  • 一个目标多条规则或多个目标共享 prerequisites
  • 自动生成依赖
  • 总结


前言

前文 Make学习二:makefile组成要素 中提到,Makefile 组成中的重要成员就是显示规则。本文重点在编写规则上。

显示规则的格式如下:

target ... : prerequisites ...recipe...

引用前面 Make学习一:make初探 中的话,简单来说,一条规则就像一道菜。如果说 target 是我们的西红柿炒鸡蛋,那 prerequisites 就是西红柿炒鸡蛋的依赖,或者说输入,简单点就是西红柿和鸡蛋。而 recipe 就是西红柿炒鸡蛋的菜谱,也就是要做成这道菜,需要什么步骤,每一步要怎么做。 实际上,recipe 确实是菜谱的意思,prerequisites 意为先决条件。

默认目标

每一条规则在 Makefile 中的 书写顺序一般不影响功能,但会决定默认目标(default goal),即如果你运行 make 没有指定目标,默认会构建的那个目标。

默认目标的规则是:取第一个 Makefile 里第一条规则的第一个 target。

因此,通常会把整个程序编译的规则写在 Makefile 最前面,这个规则的目标经常叫做 all。类似,make 的约定。

如下示例,直接运行 make 输出 Hello, world!:

all: hellohello:@echo "Hello, world!"

规则语法

一条规则的基本写法是:

target ... : prerequisites ...recipe...

targets 一般是文件名,可以用空格分隔多个目标。也可以用 通配符(wildcards)。
配方(recipe)行必须以 Tab 开头(这一点非常重要!)。 也可以通过设置 .RECIPEPREFIX 变量来改成别的符号

一条规则可以有:

  • 多个依赖(prerequisites);
  • 多个目标(targets);
  • 也可以没有依赖(比如 .PHONY 规则);
  • 或者没有 recipe(表示目标是伪目标或不需要命令)。

多个依赖示例:

edit: main.o kbd.o command.o display.ogcc -o edit main.o kbd.o command.o display.o

多个目标示例:

clean temp:rm -f *.o temp

这里 clean 和 temp 是 两个目标,共享同一个配方。

长行可以用 \ 续行,但 recipe 不需要续行,必须每行一个 Tab。

规则的作用:如果目标不存在或目标的修改时间早于任何一个依赖,则按照 recipe 去更新它们

order-only prerequisites

有时你会想要某个依赖在目标之前被构建,但不希望因为这个依赖的更新导致目标被重建。这就需要用 order-only prerequisite(仅顺序依赖)。

简单一句话概括:该依赖必须存在,但同时它的更新不能影响到 target 的更新。那么你就需要 order-only prerequisite

写法:target : normal-prerequisites | order-only-prerequisites 使用管道符把 order-only-prerequisites 放在右边即可。

例如:我们想把当前目录下的 foo.c 和 bar.c 文件构建出来的 foo.o 和 bar.o 放在 obj 目录中。但这个目录可能一开始不存在。我们需要:先保证 obj/ 目录存在;但不要因为 obj/ 目录变化而导致目标重建。则 Makefile 的内容如下所示:

OBJDIR := obj
OBJS := $(OBJDIR)/foo.o $(OBJDIR)/bar.oall: $(OBJS)# 创建 obj/ 目录(order-only)
$(OBJDIR)/%.o : %.c | $(OBJDIR)@echo "Compiling $< to $@"@cp $< $@# 仅顺序依赖:保证 obj/ 目录存在,但不影响重建判断
$(OBJDIR):@echo "Creating directory $@"@mkdir -p $@

第一次执行 make,输出如下:

Creating directory obj
Compiling foo.c to obj/foo.o
Compiling bar.c to obj/bar.o

即便我们在 obj 目录下通过命令touch obj/temp创建文件,执行 make 依旧不会有任何更新:make: 对“all”无需做任何事。

文件名中的通配符

在 Makefile 里,一个文件名可以使用通配符(wildcard characters)来匹配多个文件。支持的通配符和 Linux shell 里的一样:* 匹配 任意长度的任意字符,? 匹配任意单个字符,[abc] 匹配括号中任意一个字符。~(波浪号)在文件名开头(单独出现或后跟 /)代表当前用户根目录。

一条规则分 target,prerequisite 和 recipe。target 和 prerequisite 中的 通配符由 make 来展开。而 recipe 中的通配符会把命令传递给 shell 由 shell 来处理。

例如:以下 Makefile 执行 make 之后会打印出所有更新之后的 .c 文件内容。$? = 依赖中比目标 print 新的文件。

print: *.c@echo $?

但变量中的通配符不会自动展开,需要使用函数 $(wildcard)。 例如:objects = *.o 中变量赋值并不会展开,objects 只是 “*.o” 字符串。

此时,当我们通过以下 Makefile 来执行 make 命令:

objects = *.ofoo : $(objects)cc -o foo $(CFLAGS) $(objects)

若当前目录下没有任何 .o 文件,则 *.o 就不会展开,则会出错:make: *** 没有规则可制作目标“*.c”,由“*.o” 需求。 停止。

为了解决这个问题,则需要在变量定义时则进行通配符展开为文件名列表。使用 wildcard 函数,格式:$(wildcard pattern...)

objects := $(wildcard *.o)
foo : $(objects)  cc -o foo $(CFLAGS) $(objects)

若当前目录下没有任何 .o 文件,则 *.o 仍旧会展开,只是变量 objects 为空字符串。报错:cc -o foo cc: fatal error: no input files

伪目标 Phony Targets

所谓 伪目标(phony target),指的是根本就不是某个实际文件名的目标,而是一个用于明确执行某个配方(recipe)的名字。

使用伪目标的两个主要原因是:避免与同名文件冲突 和 提高性能。例如,如下 Makefile 内容:

clean:rm *.o temp

上面例子中,clean 不是实际会被创建的文件。因此,每次运行 make clean 时,都会执行 rm 命令。

如果当前目录里真有一个名字叫 clean 的文件,make clean 就会误以为 clean 已是最新,不执行删除操作

使用 .PHONY: clean 后,clean 的配方 总是会执行,不会被任何文件阻止。

没有 Prerequisites 和 recipe

如果一个规则 既没有依赖(prerequisite)也没有配方(recipe),并且它的目标文件不存在,那么每次 make 运行时都会认为这个目标已经“更新”。这意味着:所有依赖于这个目标的规则,每次都会执行它们自己的配方。

这个机制常用于强制重新构建(不论文件是否真正变化)。例如以下示例:

clean: FORCErm $(objects)FORCE:

内建特殊目标名

有一些目标名称如果出现在 Makefile 中,会具有特殊的含义。这些名字通常以 . 开头。

.PHONY(伪目标声明),.PHONY 后面列出的所有目标都被认为是伪目标(phony targets)。

.SUFFIXES 目标后面列出的是用于后缀规则 suffix rules的后缀列表。可以用 .SUFFIXES: 清除所有后缀规则(这样就不会自动匹配如 .c → .o 这些规则了)。

.DEFAULT(没有匹配规则时的最后手段),如果一个目标没有任何匹配的规则(无显式也无隐式规则),就会使用 .DEFAULT 提供的配方。

.DEFAULT:@echo "No rule for $@"

等等等等

一个目标多条规则或多个目标共享 prerequisites

例如:kbd.o command.o files.o: command.h 这条 Makefile 内容中,多个目标同时依赖同一个头文件。此时可以用 $@ 来区分当前到底是哪个目标。

一个文件可以同时是多条规则的目标(target)。来自不同规则的所有依赖(prerequisites)会合并成一个总的依赖列表。只要目标比任何一个依赖旧,就会执行配方(recipe)。

例如以下 Makefile 的内容:

objects = foo.o bar.ofoo.o : defs.hbar.o : defs.h test.h$(objects) : config.h

foo.o 的依赖会合并成一个列表:config.h + defs.h

自动生成依赖

在程序的 Makefile 中,你通常需要写很多这种规则:main.o: defs.hmain.c 里用了 #include "defs.h",那么你就得写 main.o: defs.h

你写这个规则是为了让 make 知道:如果 defs.h 变了,就得重新编译 main.o。如果你的程序很大,#include 很多,每次增加或删掉头文件都得手动改 Makefile,非常麻烦,容易出错。

为了解决这个问题,现代 C 编译器可以自动帮你生成这些依赖规则。它们会扫描你的源码里的 #include 语句,通常使用 -M 选项来实现。例如:cc -M main.c 它会输出:main.o : main.c defs.h 所以你不再需要自己写这些规则,编译器会帮你搞定。

总结

完结撒花!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/77517.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络安全技能大赛B模块赛题解析Server12环境

已知靶机存在⽹站系统&#xff0c;使⽤Nmap⼯具扫描靶机端⼝&#xff0c;并将⽹站服务的端⼝号作为Flag &#xff08;形式&#xff1a;Flag字符串&#xff09;值提交 使用nmap扫描目标靶机网站服务的端口号为8089 Falg&#xff1a;8089 访问⽹站/admin/pinglun.asp⻚⾯&#…

1、Linux操作系统下,ubuntu22.04版本切换中英文界面

切换中英文界面的方法很多&#xff0c;我也是按照一个能用的方法弄过来并且记录&#xff0c; 1.如果刚开始使用Ubuntu环境&#xff0c;桌面的语言环境为英文&#xff0c;需要安装中文简体的字体包 打开桌面终端&#xff0c;输入 sudo apt install language-pack-zh-hans lan…

SmolVLM2: The Smollest Video Model Ever(六)

继续微调 微调视频的代码如下&#xff1a; # 此Python文件用于对SmolVLM2进行视频字幕任务的微调 # 导入所需的库 import os os.environ["CUDA_VISIBLE_DEVICES"] "1" import torch from peft import LoraConfig, prepare_model_for_kbit_training, get…

Spring Boot安装指南

&#x1f516; Spring Boot安装指南 &#x1f331; Spring Boot支持两种使用方式&#xff1a; 1️⃣ 可作为常规Java开发工具使用 2️⃣ 可作为命令行工具安装 ⚠️ 安装前提&#xff1a; &#x1f4cc; 系统需安装 Java SDK 17 或更高版本 &#x1f50d; 建议先运行检查命令…

数据结构(七)---链式栈

#### 链式栈实现 ##### linkstack.h #ifndef _LINKSTACK_H #define _LINKSTACK_H // 引入相关的库文件 #include <stdio.h> #include <stdlib.h> #include <string.h> // 定义元素类型的别名 typedef int DATA; //定义链式栈节点 typedef struct node { …

【Spring Boot】Maven中引入 springboot 相关依赖的方式

文章目录 Maven中引入 springboot 相关依赖的方式1. 不使用版本管理&#xff08;不推荐&#xff09;2、使用版本管理&#xff08;推荐&#xff09;2.1 继承 spring-boot-starter-parent2.2 使用 spring-boot-dependencies 自定义父工程2.3引入 spring-framework-bom Maven中引…

DataStreamAPI实践原理——快速上手

引入 通过编程模型&#xff0c;我们知道Flink的编程模型提供了多层级的抽象&#xff0c;越上层的API&#xff0c;其描述性和可阅读性越强&#xff0c;越下层API&#xff0c;其灵活度高、表达力越强&#xff0c;多数时候上层API能做到的事情&#xff0c;下层API也能做到&#x…

WPF 图片文本按钮 自定义按钮

效果 上面图片,下面文本 样式 <!-- 图片文本按钮样式 --> <Style x:Key="ImageTextButtonStyle" TargetType="Button"><Setter Property="Background" Value="Transparent"/><Setter Property="BorderTh…

驱动开发硬核特训 · Day 22(上篇): 电源管理体系完整梳理:I2C、Regulator、PMIC与Power-Domain框架

&#x1f4d8; 一、电源子系统总览 在现代Linux内核中&#xff0c;电源管理不仅是系统稳定性的保障&#xff0c;也是实现高效能与低功耗运行的核心机制。 系统中涉及电源管理的关键子系统包括&#xff1a; I2C子系统&#xff1a;硬件通信基础Regulator子系统&#xff1a;电源…

设计模式全解析:23种经典设计模式及其应用

创建型模式 1. 单例模式&#xff08;Singleton Pattern&#xff09; 核心思想&#xff1a;确保一个类只有一个实例&#xff0c;并提供一个全局访问点。适用场景&#xff1a;需要共享资源的场景&#xff0c;如配置管理、日志记录等。 public class Singleton {// 静态变量保存…

力扣热题100题解(c++)—矩阵

73.矩阵置零 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 int m matrix.size(); // 行数int n matrix[0].size(); // 列数bool firstRowZero false; // 标记第一行是否包含 0bool f…

本地部署DeepSeek-R1(Dify升级最新版本、新增插件功能、过滤推理思考过程)

下载最新版本Dify Dify1.0版本之前不支持插件功能&#xff0c;先升级DIfy 下载最新版本&#xff0c;目前1.0.1 Git地址&#xff1a;https://github.com/langgenius/dify/releases/tag/1.0.1 我这里下载到老版本同一个目录并解压 拷贝老数据 需先停用老版本Dify PS D:\D…

PostSwigger Web 安全学习:CSRF漏洞3

CSRF 漏洞学习网站&#xff1a;What is CSRF (Cross-site request forgery)? Tutorial & Examples | Web Security Academy CSRF Token 基本原理 CSRF Token 是服务端生成的唯一、随机且不可预测的字符串&#xff0c;用于验证客户端合法校验。 作用&#xff1a;防止攻击…

用 Nodemon 解决 npm run serve 频繁重启服务

Nodemon 是一个基于 Node.js 构建的开发工具&#xff0c;专为帮助开发者自动监控项目文件的更改而设计。每当文件发生变更时&#xff0c;Nodemon 会自动重启 Node.js 服务器&#xff0c;无需手动停止并重启。这对于提升开发速度、减少人工操作非常有帮助&#xff0c;尤其适用于…

django admin 中更新表数据 之后再将数据返回管理界面

在Django中&#xff0c;更新数据库中的数据并将其重新显示在Django Admin界面上通常涉及到几个步骤。这里我将详细说明如何在Django Admin中更新表数据&#xff0c;并确保更新后的数据能够立即在管理界面上显示。 定义模型 首先&#xff0c;确保你的模型&#xff08;Model&…

真.从“零”搞 VSCode+STM32CubeMx+C <1>构建

目录 前言 准备工作 创建STM32CubeMx项目 VSCode导入项目&配置 构建错误调试 后记 前言 去年10月开始接触单片机&#xff0c;一直在用树莓派的Pico&#xff0c;之前一直用Micropython&#xff0c;玩的不亦乐乎&#xff0c;试错阶段优势明显&#xff0c;很快就能鼓捣一…

C语言学习之结构体

在C语言中&#xff0c;我们已经学了好几种类型的数据。比如整型int、char、short等&#xff0c;浮点型double、float等。但是这些都是基本数据类型&#xff0c;而这些数据类型应用在实际编程里显然是不够用的。比如我们没有办法用一旦数据类型来定义一个”人“的属性。因此这里…

架构-计算机系统基础

计算机系统基础 一、计算机系统组成 &#xff08;一&#xff09;计算机系统层次结构 硬件组成 主机&#xff1a;包含CPU&#xff08;运算器控制器&#xff09;、主存储器&#xff08;内存&#xff09;。外设&#xff1a;输入设备、输出设备、辅助存储器&#xff08;外存&…

【计算机网络性能优化】从基础理论到实战调优

目录 前言技术背景与价值当前技术痛点解决方案概述目标读者说明 一、技术原理剖析核心概念图解核心作用讲解关键技术模块说明技术选型对比 二、实战演示环境配置要求核心代码实现案例1&#xff1a;iPerf3带宽测试案例2&#xff1a;TCP窗口优化案例3&#xff1a;QoS流量整形 运行…

Python 自动化办公:Excel 数据处理的“秘密武器”

引言 在日常的 IT 办公场景里&#xff0c;Excel 是数据处理与分析的 “常胜将军”。无论是财务人员整理账目、销售团队统计业绩&#xff0c;还是运营人员分析用户数据&#xff0c;Excel 都发挥着关键作用。但面对海量数据&#xff0c;手动操作 Excel 不仅效率低下&#xff0c;还…