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

29. Makefile 创建和使用变量


🧭 一、Makefile 中变量的创建与使用

1️⃣ 创建变量

在 Makefile 中,可以使用 =:= 来定义变量。

1.1 使用 = 定义变量(延迟求值)

这种赋值方式是 延迟求值,即变量的值只有在它被 引用时 才会被计算。这适用于你需要依赖其他变量值的情况。

CC = gcc
CFLAGS = -Wall -g
SRC = main.c utils.call:$(CC) $(CFLAGS) $(SRC) -o my_program

解释

  • CC, CFLAGSSRC 使用 = 定义,只有在 all 规则中引用它们时,它们的值才会被计算。

优点: 它可以向后引用变量
缺点: 不能对该变量进行任何扩展,例如
CFLAGS = $(CFLAGS) -O 会造成死循环

1.2 使用 := 定义变量(立即求值)

这种赋值方式是 立即求值,即在变量定义时就计算出它的值。通常在需要 避免递归引用 的场景下使用。

SRC := $(wildcard *.c)

解释

  • SRC := $(wildcard *.c) 使用 := 立即求值,这会在 Makefile 解析时立即计算 SRC 的值。

  • 用这种方式定义的变量,会在变量的定义点,按照被引用的变量的当前值进行展开
  • 这种定义变量的方式更适合在大的编程项目中使用,因为它更像我们一般的编程语言
1.3 ?= 定义变量(条件赋值)

?= 用于 条件赋值,只有在变量 未定义时 才会为其赋值。这个功能在配置文件中很常见,可以为变量提供默认值,但不会覆盖用户在命令行中指定的值。

CC ?= gcc
CFLAGS ?= -Wall -g

解释

  • 如果 CCCFLAGS 没有被定义,那么会将它们分别赋值为 gcc-Wall -g
  • 如果你在命令行执行 make CC=clang,则 CC 会被设置为 clang,而不是默认值 gcc

用?=定义变量
dir := /foo/bar
FOO ?= bar
FOO是?

含义是,如果FOO没有被定义过,那么变量FOO的值就是“bar”,如果FOO先前被定义过,那么这条语将什么也不做,其等价于:
ifeq ($(origin FOO), undefined)
FOO = bar
endif


🧩 二、Makefile 中变量的使用

2️⃣ 引用变量

在 Makefile 中,变量的引用可以使用 $()${} 语法。

  • 推荐使用 $()$(CC)$(SRC)
  • ${} 是与 $() 等效的,但通常 $() 更常用。

示例:

CC = gcc
CFLAGS = -Wall -g
SRC = main.c utils.call:$(CC) $(CFLAGS) $(SRC) -o my_program
  • $(CC) 会被替换为 gcc$(SRC) 会被替换为 main.c utils.c,最终的命令是:gcc -Wall -g main.c utils.c -o my_program

🧩 三、Makefile 中的自动变量

好的!既然你对 Makefile 中的自动变量 完全没有了解,我将从基础开始,详细讲解这些变量的作用和用法,确保你能够理解并在实际的 Makefile 中灵活使用它们。


🧭 3.1 什么是 Makefile 中的自动变量?

自动变量Makefile 中一类特殊的变量,它们由 make 自动提供,通常用于表示规则中的 目标文件依赖文件命令等内容。
自动变量通常在构建规则中使用,帮助你简化和动态化构建过程。

自动变量的作用:

  • 目标文件$@
  • 第一个依赖文件$<
  • 所有依赖文件$^
  • 更新过的依赖文件$?
  • 目标文件的文件名部分$*
  • 目标文件的目录部分$(@D)
  • 目标文件的文件名部分$(@F)

这些变量的值是由 make 自动计算的,具体取决于 Makefile 的规则和依赖关系。


🧩 3.2、常见的自动变量及其用法

1️⃣ $@:目标文件的名称

$@ 代表当前规则的 目标文件,即你要生成的文件。在链接规则中,这通常是最终生成的目标文件名。

示例:
my_program: main.o utils.ogcc -o $@ $^

解释

  • $@ 代表目标文件 my_program
  • gcc -o $@ $^ 会变成 gcc -o my_program main.o utils.o,用于生成最终的可执行文件 my_program

2️⃣ $<:第一个依赖文件的名称

$< 代表规则中 第一个依赖文件,它通常用于编译规则中,指向要编译的源文件。

示例:
%.o: %.cgcc -c $< -o $@

解释

  • $< 是源文件 %.c,比如 main.c,在这个规则中表示 第一个依赖文件
  • 这里 gcc -c $< -o $@ 会变成 gcc -c main.c -o main.o,用于编译源文件 main.c 成目标文件 main.o

3️⃣ $^:所有依赖文件的名称(去重)

$^ 代表规则中所有的依赖文件的名称,去重后用空格分隔。它常常用于链接阶段,表示所有需要链接的对象文件。

示例:
my_program: main.o utils.ogcc -o $@ $^

解释

  • $^ 代表所有的依赖文件 main.outils.o
  • gcc -o $@ $^ 会变成 gcc -o my_program main.o utils.o,用于链接生成目标文件 my_program

4️⃣ $?:所有比目标文件新的依赖文件

$? 代表所有比目标文件 新的依赖文件,即 修改时间比目标文件更新的依赖文件。这是增量构建时非常有用的变量,只有更新过的依赖文件才会重新编译。

示例:
my_program: main.o utils.ogcc -o $@ $^echo "更新过的依赖文件: $?"

解释

  • 如果 main.o 被修改了,而 utils.o 没有被修改,那么 $? 将返回 main.o,而不是 utils.o

5️⃣ $*:目标文件的文件名部分(不包含扩展名)

$* 代表规则中目标文件的 文件名部分,去掉扩展名。它对于处理文件名而不考虑扩展名非常有用。


🧭$* 的真正含义

$* 表示 目标文件的“stem”部分(即去掉扩展名之后的名字)
它只在 模式规则(pattern rules) 中有意义。


✅ 举个最直观的例子:

%.o: %.cecho "目标文件名: $@"echo "目标文件名(不含扩展名): $*"echo "依赖文件: $<"

当你执行:

make main.o

输出可能是:

目标文件名: main.o
目标文件名(不含扩展名): main
依赖文件: main.c

🧩 理解 $* 是如何被“推导”的

在模式规则中:

%.o : %.c

% 匹配同一部分的字符串(称为 stem)。

👉 也就是说:

  • 如果目标是 main.o
  • 那么 % 对应的 “stem” 就是 main
  • 所以 $* 的值就是 "main"

🧱 什么时候会用 $*

在一些高级自动生成规则中,$* 特别有用。
比如:

✅ 示例 1:用 $* 自动生成中间文件

假设我们编译 .c 文件时,还要生成 .d 依赖文件(记录头文件依赖)。

%.o: %.cgcc -c $< -o $@gcc -MM $< > $*.d

🔍 分析:

  • $<:源文件,例如 main.c
  • $@:目标文件,例如 main.o
  • $*:去掉扩展名后的文件名,例如 main

于是第二行命令实际执行:

gcc -MM main.c > main.d

👉 这样我们自动生成了对应的依赖文件 main.d


✅ 示例 2:编译不同后缀的文件但共用同一名字

假设我们有 .c.s 文件(汇编),希望都编译成同名 .o 文件:

%.o: %.cgcc -c $< -o $@%.o: %.sas $< -o $@echo "汇编目标的基名:$*"

当执行:

make start.o

如果 start.s 存在,则:

  • $@ = start.o
  • $< = start.s
  • $* = start

✅ 示例 3:批量操作同名文件

你可以用 $* 来生成多个文件:

%.bak: %.txtcp $< backup/$*.bak

当执行:

make hello.bak

实际命令执行:

cp hello.txt backup/hello.bak

💡 $* 让我们轻松拿到文件名的中间部分。


🧠 $* 的局限性与注意事项

  1. 只在“模式规则”中有效
    如果你写的是普通规则(如 main.o: main.c),那么 $* 为空。

  2. **没有扩展名的目标无法推导出 ∗∗∗比如目标是‘Makefile‘(没有‘.‘),那么‘*** 比如目标是 `Makefile`(没有 `.`),那么 `比如目标是Makefile(没有‘.‘),那么*` 无法计算。

  3. 多后缀时取第一个匹配的规则
    如果你有 .c.cpp 同名文件,只会根据第一个匹配的规则计算 $*



🧩 3.3、Makefile 中自动变量的综合示例

为了更好地理解这些自动变量的用法,我们来看一个完整的 Makefile 示例:

示例:一个简单的 C 语言项目

假设你有以下目录结构:

project/├── src/├── main.c├── utils.c├── Makefile

Makefile 中,你需要编译源文件并链接生成可执行文件。我们使用自动变量来简化过程。

CC = gcc
CFLAGS = -Wall -g
SRC = src/main.c src/utils.c
OBJ = $(SRC:.c=.o)# 目标规则
my_program: $(OBJ)$(CC) $(CFLAGS) -o $@ $^# 编译规则
%.o: %.c$(CC) $(CFLAGS) -c $< -o $@

解释:

  1. $(SRC:.c=.o):这是一个模式替换的用法,它将 .c 文件转换为 .o 文件,即将每个源文件 .c 映射为对应的目标文件 .o

  2. my_program: $(OBJ)

    • 这是构建目标文件 my_program 的规则。
    • 目标文件 my_program 依赖于 .o 文件(main.outils.o)。
    • $@ 会被替换为 my_program
    • $^ 会被替换为 main.o utils.o,表示所有依赖文件。
  3. 编译规则:%.o: %.c

    • 这是一个模式规则,表示任何 .c 文件都可以被编译成 .o 文件。
    • $< 代表源文件 %.c,比如 main.cutils.c
    • $@ 代表目标文件 %.o,比如 main.outils.o

执行流程:

  1. make 会首先看到 my_program 规则,看到 $(OBJ),它会找到 main.outils.o 作为依赖文件。
  2. 对于 main.cutils.cmake 会根据 %.o: %.c 规则生成对应的 .o 文件。
  3. 一旦所有的 .o 文件生成完成,make 会使用 gcc 来链接生成 my_program

🧩 3.4、常见的自动变量错误与调试技巧

错误 1:混淆 $@$*

  • 错误:在规则中错误地使用 $* 作为目标文件。
  • 原因$* 是目标文件的 文件名部分(没有扩展名),而 $@ 是完整的目标文件名。

正确用法:

%.o: %.cgcc -c $< -o $@

解释$@ 是目标文件(如 main.o),而 $* 会返回 main(不包括 .o 扩展名)。


错误 2:忘记 $(CC)$(CFLAGS) 的求值

  • 错误:在变量中定义了 CCCFLAGS,但没有在规则中正确引用它们。
  • 原因:没有正确使用 $() 来引用变量。

正确用法:

CC = gcc
CFLAGS = -Wall -g%.o: %.c$(CC) $(CFLAGS) -c $< -o $@

解释$(CC)$(CFLAGS) 被正确地引用来调用编译器和编译选项。


3.5 🧭 总结

自动变量总结:

自动变量说明使用场景
$@目标文件的名称用于表示当前规则的目标文件
$<第一个依赖文件用于表示规则中的第一个依赖文件
$^所有依赖文件(去重)用于表示所有的依赖文件
$?所有比目标文件新的依赖文件用于增量构建时,更新过的依赖文件
$*目标文件的文件名部分用于获取目标文件的文件名(不含扩展名)
$(@D)目标文件的目录部分用于获取目标文件所在的目录
$(@F)目标文件的文件名部分用于获取目标文件的文件名部分

通过这节讲解,你应该能够理解和应用 Makefile 中的自动变量。掌握这些自动变量能大大简化构建规则,并且使你的 Makefile 更加灵活。如果你有任何问题或者需要更多的例子,随时告诉我!😊


🧩 四、环境变量

环境变量 是由操作系统或父进程设置的变量,它们存储系统配置信息。在 Makefile 中,环境变量通常用于 系统路径工具链路径 或其他系统级的配置。

4️⃣ 访问环境变量

你可以直接通过 $(VARIABLE) 语法在 Makefile 中访问环境变量。例如,访问 PATHHOME 环境变量:

all:echo "PATH: $(PATH)"echo "HOME: $(HOME)"

5️⃣ 设置环境变量

你还可以通过 exportMakefile 中的变量导出为环境变量,使得子进程也能访问它们:

export MY_VAR = valueall:echo "MY_VAR: $(MY_VAR)"
  • 这将把 MY_VAR 导出为环境变量,所有通过 make 调用的子进程都能访问到这个变量。

6️⃣ Makefile 中使用环境变量

你可以在 Makefile 中引用环境变量来设置构建工具的路径或其他配置:

CC = $(MY_CC)  # 如果 MY_CC 环境变量已定义

🧩 五、+= 和追加值

Makefile 中,+= 用于将新的值 追加到现有变量 的末尾。

示例:

CFLAGS = -Wall -g
CFLAGS += -O2all:echo "编译选项: $(CFLAGS)"

解释

  • CFLAGS += -O2 会将 -O2 追加到现有的 CFLAGS 后面。最终,CFLAGS 的值将是 -Wall -g -O2

🧭 六、特殊变量和模式规则

1️⃣ 模式规则(Pattern Rules)

Makefile 允许通过模式规则(Pattern Rules)简化多文件的构建。你可以使用 % 通配符来匹配文件名。

示例:

%.o: %.cgcc -c $< -o $@

解释

  • %.o: %.c 是一个模式规则,表示所有的 .c 文件可以通过该规则生成 .o 文件。
  • $< 是源文件,$@ 是目标文件。

2️⃣ 内建函数

Makefile 中还提供了一些常见的内建函数,方便操作字符串、文件名等。

2.1 $(wildcard pattern):列出符合模式的文件
SRC := $(wildcard *.c)  # 获取所有 .c 文件
2.2 $(patsubst pattern, replacement, text):替换模式
OBJ := $(patsubst %.c, %.o, $(SRC))  # 把 .c 后缀替换为 .o
2.3 $(basename names):去除文件扩展名
BASE := $(basename $(SRC))  # 去掉 .c 后缀,得到文件基名

🧭 七、总结与最佳实践

特性说明示例
变量定义使用 =(延迟求值)、:=(立即求值)和 ?=(条件赋值)来定义变量CC ?= gccCFLAGS := -O2
自动变量$@(目标文件)、$<(第一个依赖文件)等用于简化规则$@, $<, $^, $?
环境变量访问和设置系统级环境变量$(PATH), export MY_VAR = value
追加变量值使用 += 向现有变量添加新值CFLAGS += -O3
模式规则% 定义模式规则,用于简化多文件规则%.o: %.c
内建函数$(wildcard)$(patsubst) 等用于文件操作$(wildcard *.c), $(patsubst %.c, %.o, $(SRC))

  • VPATH : 虚路径
    • 在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当make需要去找寻文件的依赖关系时,你可以在文件前加上路径,但最好的方法是把一个路径告诉make,让make在自动去找。
    • Makefile文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。
    • VPATH = src:…/headers
    • 上面的的定义指定两个目录,“src”和“…/headers”,make会按照这个顺序进行搜索。目录由“冒号”分隔。(当然,当前目录永远是最高优先搜索的地方)
http://www.dtcms.com/a/536841.html

相关文章:

  • Docker 安装和配置 Redis 完整指南
  • 高效对象池设计:提升Unity性能的关键
  • 网站建设需要了解哪些信息常州网站制作公司
  • 如何做正版小说网站工厂电商具体是做什么的
  • 磁盘和注册表清理工具
  • 【windows】证书引起的浏览器请求问题-https红色斜线-解决方法
  • mormot2创建一个httpserver
  • 科技类网站简介怎么做有哪些游戏可以做网站
  • 定制化TTS数据实践:解锁语音大模型的无限潜能
  • 微网站是什么嘉兴高端网站定制
  • 一分钟讲透:c++新特性string_view
  • sns社交网站 建设做网站图片尺寸
  • 营销网站结构网站免费优化平台
  • 免费视频模板网站制作微信网页
  • android实践:loadUrl执行JavaScript异常
  • FFmpeg 基本数据结构 AVCodecParser分析
  • celery知识点总结
  • langchain将用户问题转sql查询探索
  • compareAndSet怎么用
  • Skill Seeker——一站式自动化将文档网站、GitHub 仓库和 PDF 文件转换为可部署 AI 技能的深度解析
  • 浅谈 Agent 开发工具链演进历程
  • 帝国cms小说阅读网站模板果洛电子商务网站建设哪家快
  • 学校网站建设背景科技作品手工
  • SmartPLS下载安装教程(附安装包)SmartPLS 4.1保姆级图文教程
  • ECR扫描管理功能完整实现:提升云原生镜像安全管控效率
  • PySide6 Win10记事本从零到一——第二章 第一个窗口程序
  • 商务英语资源合集
  • Shell 变量
  • 基于vue的在线学习系统
  • 【题解】洛谷 P4291 [HAOI2008] 排名系统 [字符串 + 平衡树]