NO2.makefile中变量和不同的赋值方式以及一些常用预定义变量的使用方法

本文详细介绍了Makefile中的变量概念及其命名规则,并深入探讨了不同类型的变量赋值方式,包括简单赋值、递归赋值、条件赋值和追加赋值等。此外,还介绍了几种常用的预定义变量,如自动变量和特殊变量,并提供了实际应用示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文参照自狄泰软件学院:《门徒计划》课程

变量和命名规则:makefile中支持程序语言设计中的变量的概念,但是变量只代表文本数据(字符串),命名规则如下:

变量名可以包含字符数字和下划线,但是不能包含 ":", " # ",  " = ", " ",而且变量名大小写敏感

变量的定义和使用:


常用的变量的赋值方式:

简单赋值(:=)         递归赋值( = )  条件赋值 (?=)    追加赋值(+=)

用法示例:

简单赋值(:=)


递归赋值( = ):


条件赋值 (?=):


 追加赋值(+=):


常用的预定义变量

—自动变量  $@      $^      $<

—特殊变量 $(MAKE)  $(MAKECMDGOALS) $(MAKEFILE_LIST)

                  $(MAKE_VERRION)   $(CURDIR)    $(VARIABLES)

自动变量的解释:

 

自动变量的使用示例:



输出结果为:

$@ => all
$^ => first second third
$< => first

特殊变量的解释:




相关示例代码:

.PHONY : all out first second third test

all out : 
	@echo "$(MAKE)"
	@echo "$(MAKECMDGOALS)"
	@echo "$(MAKEFILE_LIST)"
	
	
first :
	@echo "first"
	
second :
	@echo "second"
	
third :
	@echo "third"
	
test :
	@$(MAKE) first
	@$(MAKE) second
	@$(MAKE) third

make test的输出结果

make all 的输出结果

相关示例代码:

.PHONY : test1 test2

TDelphi := Delphi Tang
D.T.Software := D.T.
	
test1 :
	@echo "$(MAKE_VERSION)"
	@echo "$(CURDIR)"
	@echo "$(.VARIABLES)"
	
test2 :
	@echo "$(RM)"
结果,自己可以编译查看一下


<think>我们正在讨论Makefile中的变量赋值方式及其行为。根据用户提供的引用之前的问题,我们需要详细解释Makefile变量赋值的几种方式及其对应的行为。 在Makefile中,变量赋值有多种方式,每种方式在何时展开、如何展开以及作用范围上有所不同。常见的赋值方式包括: 1. 立即赋值(=):使用等号(=)进行赋值变量值是在引用时展开的,也就是说,变量的值会使用最后一次定义的值。 2. 直接赋值(:=):使用冒号等号(:=)进行赋值变量值在定义时立即展开,后续的赋值会影响该变量。 3. 条件赋值(?=):如果变量之前没有被赋值过,则进行赋值;否则,忽略此次赋值。 4. 追加赋值(+=):在变量原有值的基础上追加新值。 此外,还有多行变量定义(define ... endef)覆盖赋值(override)等。 下面我们将详细解析每种赋值方式的行为,并给出示例。 ### 1. 立即赋值(=) 使用等号(=)进行赋值变量的值是在被引用时才会展开,因此它会使用当前最新的变量值。这意味着,如果在定义变量之后又改变了它所依赖的变量,那么它的值也会随之改变。 ```makefile # 立即赋值示例 VAR1 = foo VAR2 = $(VAR1) # 此时VAR2的值是"foo" VAR1 = bar all: @echo "VAR2 = $(VAR2)" # 输出: VAR2 = bar ``` 在上面的例子中,VAR2在定义时并没有立即展开,而是在被引用(在all目标中)时才展开,此时VAR1的值已经是bar,因此VAR2的值为bar。 ### 2. 直接赋值(:=) 使用冒号等号(:=)进行赋值变量的值在定义时立即展开,后续对依赖变量的修改会影响该变量。 ```makefile # 直接赋值示例 VAR1 := foo VAR2 := $(VAR1) # 此时立即展开,VAR2的值是"foo" VAR1 := bar all: @echo "VAR2 = $(VAR2)" # 输出: VAR2 = foo ``` 在这个例子中,VAR2在定义时立即展开为foo,即使后面VAR1被修改为bar,VAR2的值也会改变。 ### 3. 条件赋值(?=) 使用问号等号(?=)进行赋值,只有在该变量之前没有赋值的情况下才会进行赋值。如果变量已经被赋值(包括空值),则会覆盖。 ```makefile # 条件赋值示例 VAR1 = foo VAR1 ?= bar # 因为VAR1已经被赋值,所以此句无效 all: @echo "VAR1 = $(VAR1)" # 输出: VAR1 = foo ``` 另一个例子: ```makefile # VAR2之前没有被赋值 VAR2 ?= baz all: @echo "VAR2 = $(VAR2)" # 输出: VAR2 = baz ``` ### 4. 追加赋值(+=) 使用加号等号(+=)进行赋值,将新值追加到变量原有值的后面,中间用空格隔开。追加赋值的行为取决于变量之前是如何定义的。 - 如果变量之前是用立即赋值(=)定义的,那么追加的值会在引用时展开。 - 如果变量之前是用直接赋值(:=)定义的,那么追加的值会在追加时立即展开。 ```makefile # 追加赋值示例 VAR1 = foo VAR1 += bar # 此时VAR1的值为"foo bar",但bar部分还未展开(如果有变量引用) VAR2 := foo VAR2 += bar # 立即展开,VAR2的值为"foo bar" VAR3 = abc VAR3 += $(VAR1) # 追加一个变量引用,此时会展开VAR1 VAR1 = def all: @echo "VAR1 = $(VAR1)" # 输出: VAR1 = def bar? 注意:实际上VAR1的值是"foo bar",但后来被重新赋值为def,然后追加了bar?这里需要澄清。 # 实际上,上面的VAR1在第一次赋值后,通过追加赋值变为"foo bar",然后又被重新赋值为"def",所以最后VAR1的值是"def",而追加赋值的那句实际上被覆盖了?所以我们需要重新组织示例。 ``` 为了更清晰,我们重新组织追加赋值的例子: ```makefile # 追加赋值示例1:基于立即赋值 VAR_A = one VAR_A += two # 此时VAR_A的值为"one two" VAR_B = three VAR_A += $(VAR_B) # 追加一个变量引用,此时VAR_B还未展开 VAR_B = four test1: @echo "VAR_A = $(VAR_A)" # 输出: one two four # 追加赋值示例2:基于直接赋值 VAR_C := one VAR_C += two # 立即展开,所以VAR_C为"one two" VAR_D := three VAR_C += $(VAR_D) # 立即展开,此时VAR_D为three,所以VAR_C变为"one two three" VAR_D = four test2: @echo "VAR_C = $(VAR_C)" # 输出: one two three ``` ### 5. 多行变量定义(define) 使用`define`指令可以定义包含多行内容的变量,通常用于定义命令包。多行变量定义以`define`开始,以`endef`结束。 ```makefile # 多行变量定义 define ECHO_MESSAGE @echo "Message start" @echo "Hello, World!" @echo "Message end" endef all: $(ECHO_MESSAGE) ``` ### 6. 覆盖赋值(override) 使用`override`指令可以覆盖来自环境变量或命令行传递的变量值。 ```makefile # 覆盖赋值示例 override CFLAGS = -g -Wall all: @echo "CFLAGS = $(CFLAGS)" ``` 即使命令行中传递了CFLAGS参数,如`make CFLAGS=-O2`,在Makefile中,CFLAGS的值仍然是-g -Wall。 ### 变量赋值的注意事项 - **变量引用**:使用`$(VAR)`或`${VAR}`来引用变量。 - **空格处理**:在Makefile中,等号两边可以有空格,这与Shell不同(引用[2]中提到Shell中等号两边能有空格,而Makefile中允许)[^2]。 - **作用域**:Makefile中的变量是全局的,除非使用目标特定的变量赋值(target-specific variable)或模式特定的变量赋值(pattern-specific variable)来限定作用域。 ### 示例:目标特定的变量赋值 ```makefile # 目标特定变量 VAR = global all: VAR = target-specific all: @echo "In all: VAR = $(VAR)" other: @echo "In other: VAR = $(VAR)" ``` 运行`make all`会输出"In all: VAR = target-specific",而运行`make other`会输出"In other: VAR = global"。 ### 总结 Makefile变量赋值方式多样,理解每种赋值方式的展开时机作用范围对于编写正确的Makefile至关重要: - `=`:延迟展开,引用时使用最新的值。 - `:=`:立即展开,使用定义时的值。 - `?=`:条件赋值,仅当变量未定义时赋值。 - `+=`:追加赋值,行为取决于原有变量的定义方式。 - `define`:定义多行变量。 - `override`:覆盖环境变量或命令行参数。 正确使用这些赋值方式可以避免许多常见的Makefile错误。 ### 相关问题 1. Makefile中如何定义多行字符串变量2. 如何避免Makefile变量赋值的常见错误? 3. Makefile变量作用域有哪些限制? 4. 如何在Makefile中取消一个变量的定义?</think>### Makefile 变量赋值方式及行为解析 在 Makefile 中,变量赋值是构建系统的核心机制,不同赋值方式直接影响变量的展开时机作用范围。以下是主要赋值方式及其行为解析: #### 1. **立即赋值(`=`)** - **行为**:延迟展开(引用时求值) - **特点**: - 变量值在引用时展开 - 会使用最终定义的值(而非定义时的值) - 允许递归引用 - **示例**: ```makefile VAR_A = first VAR_B = $(VAR_A) # 此时VAR_B值为"first" VAR_A = second test: @echo $(VAR_B) # 输出"second"(使用VAR_A最新值) ``` #### 2. **直接赋值(`:=`)** - **行为**:立即展开(定义时求值) - **特点**: - 变量值在定义时固定 - 会受后续同名变量影响 - 避免递归引用导致的死循环 - **示例**: ```makefile VAR_C := initial VAR_D := $(VAR_C) # 立即展开为"initial" VAR_C := changed test: @echo $(VAR_D) # 输出"initial"(值已固化) ``` #### 3. **条件赋值(`?=`)** - **行为**:仅当变量未定义时赋值 - **特点**: - 避免覆盖已定义的变量 - 常用于设置默认值 - **示例**: ```makefile VAR_E ?= default # 定义默认值 VAR_E ?= override # 无效(覆盖) test: @echo $(VAR_E) # 输出"default" ``` #### 4. **追加赋值(`+=`)** - **行为**:值追加(行为受原始定义方式影响) - **特点**: - 原始为`=`定义:延迟追加 - 原始为`:=`定义:立即追加 - **示例**: ```makefile VAR_F = base VAR_F += $(shell date) # 引用时动态追加 VAR_G := static VAR_G += fixed # 立即追加为"static fixed" test: @echo $(VAR_F) # 输出"base <当前时间>" @echo $(VAR_G) # 输出"static fixed" ``` #### 5. **多行赋值(`define`)** - **行为**:定义多行变量常用于命令序列) - **特点**: - 需配合`endef`结束 - 支持`=`或`:=`赋值行为 - **示例**: ```makefile define CMD_SEQ = echo "Step 1" echo "Step 2" endef test: $(CMD_SEQ) # 执行两条命令 ``` #### 6. **环境变量与覆盖** - **行为**: - Makefile 继承 shell 环境变量 - `override` 强制覆盖命令行传入的变量 - **示例**: ```makefile override CFLAGS = -O2 # 忽略命令行传入值 test: @echo $(CFLAGS) # 始终输出"-O2" ``` #### 关键行为对比表 | 赋值方式 | 展开时机 | 是否递归 | 典型应用场景 | |----------|--------------|----------|-----------------------| | `=` | 引用时 | 是 | 动态配置 | | `:=` | 定义时 | 否 | 性能敏感操作 | | `?=` | 定义时 | - | 设置默认值 | | `+=` | 依赖原始定义 | - | 增量添加依赖/编译选项 | | `define` | 依赖定义符号 | - | 复杂命令序列 | #### 注意事项 1. **空格处理**:Makefile 允许等号两边有空格(与 Shell 不同)[^2] ```makefile VAL = text # 合法(Shell 中会报错) ``` 2. **目录获取**:`$(shell pwd)` 在解析时确定值[^1] ```makefile CURRENT_DIR := $(shell pwd) # 执行make时的目录 ``` 3. **作用域**: - 全局变量:默认作用域 - 目标级变量:`target: VAR = value`(仅对该目标有效) ### 总结 理解不同赋值方式的核心在于把握**展开时机**: - 需要动态值(如最新时间戳)用 `=` - 需要固定值(如工具路径)用 `:=` - 避免变量覆盖用 `?=` - 增量修改用 `+=` 正确选择赋值方式可显著提升 Makefile 的可维护性执行效率。 ### 相关问题 1. Makefile 中如何防止变量被命令行参数覆盖? 2. 目标级变量与全局变量有何区别? 3. 如何安全地在 Makefile 中处理带空格的路径? 4. 递归变量赋值可能导致哪些问题?如何避免? [^1]: Makefile中执行pwd赋值变量。在这两种方法中,CURRENT_DIR变量都会被赋值Makefile所在的当前工作目录。然后可以在Makefile的其他部分使用这个变量。请注意,这个值是在Makefile被解析时确定的,所以如果在不同的目录下执行make命令,CURRENT_DIR的值将会是那个目录下的值。 [^2]: Shell 脚本中的等号赋值 在 Shell 脚本 中,赋值语句使用 = 时,左右两边能有空格。如果在等号两边加空格,Shell 会把等号两边的内容当作命令参数来解析,这会导致语法错误。 正确的写法: VAR=value 错误的写法: VAR = value # 这会导致错误 Makefile 中的等号赋值Makefile 中,赋值使用 = 时,可以在等号两边添加空格,这在 Makefile 中是合法的。Makefile 会自动解析并处理等号两边的空格。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值