如何在Makefile中编写宏插入目标变量中不存在的符号
问题描述
我需要在Makefile中编写一个宏,实现将源变量中的符号/目标文件插入到目标变量中,但仅插入目标变量原本不存在的内容。
期望的调用方式示例:
$(call insert_non_existing_symbols, source1.o $(OBJS_SYMBOLS_1) $(OBJS_SYMBOLS_2) another_source.o, $(DESINATION_VARIABLE), $(DESINATION_VARIABLE))
当前我写的宏定义如下:
################################################################################ # Macro to insert symbols in one variable not existing in another variable. # From the symbols in $(1), check that the symbol is not available in $(2), and # inserts in $(3) the symbols not available insert_non_existing_symbols = \ for i in $(1); do \ exist = 0; \ $(call member,$i,$(2),$(ret_val)) \ ifeq $ret_val,0 \ $(3) += $i; \ endif \ done ################################################################################ # Macro to determine if a symbol $(1) exists in another variable/symbol $(2) # returns $(3) = 1 if it does. member = \ $(3) = 0; \ for i in $(2); do \ ifeq $(1),$i \ $(3)=1; \ endif \ done
现有测试变量:
OBJS_COLL := obj1.o obj2.o DESINATION_VARIABLE := obj3.o obj4.o
调用宏后,期望DESINATION_VARIABLE更新为obj3.o obj4.o obj1.o obj2.o。
实际遇到的问题:
使用:=即时赋值的变量无法获取更新后的值,改用=递归赋值虽然可以生效,但想寻求更合适的解决方案或思路。
问题分析
你当前的宏写法存在几个核心问题:
- Makefile的函数是文本展开式逻辑,不是Shell那样的执行式逻辑。你写的
ifeq、for会被提前展开,无法实现预期的循环判断流程。 - 通过
$(ret_val)传递返回值的方式在Make函数中不可行,Make函数没有独立的变量作用域,这种赋值方式不会按预期工作。 - 直接在函数里使用
+=无法正确修改目标变量,因为函数返回的是文本内容,不会主动执行赋值操作。
解决方案
方案1:用Make内置集合函数实现(推荐)
Make本身提供了filter-out等内置函数,可以直接处理集合差集操作,完全不需要复杂的循环宏:
直接实现(无需自定义宏)
# 计算源变量中不在目标变量里的内容 NEW_SYMBOLS := $(filter-out $(DESINATION_VARIABLE), $(OBJS_COLL)) # 将新内容追加到目标变量 DESINATION_VARIABLE += $(NEW_SYMBOLS)
封装成宏(便于复用)
# 宏定义:$(call insert_non_existing_symbols, 源变量值, 目标变量名) # 注意:传递目标变量的名字而非变量值,才能直接修改变量 insert_non_existing_symbols = \ $(eval NEW_SYMBOLS := $(filter-out $($2), $1)) \ $(eval $2 += $(NEW_SYMBOLS))
调用示例:
OBJS_COLL := obj1.o obj2.o DESINATION_VARIABLE := obj3.o obj4.o # 调用宏:传入源变量值和目标变量名 $(call insert_non_existing_symbols, $(OBJS_COLL), DESINATION_VARIABLE) # 验证结果 test: @echo $(DESINATION_VARIABLE)
执行make test会输出obj3.o obj4.o obj1.o obj2.o,符合预期。
方案2:解决即时赋值变量的更新问题
如果必须使用:=即时赋值,核心是用$(eval)强制Make立即执行赋值语句:
:=是立即展开,赋值时就确定变量值;=是延迟展开,直到变量被使用时才计算。$(eval)会让Make立即执行后续的Makefile语句,不管变量是用:=还是=定义的,都能正确完成更新。
比如上面的宏中使用$(eval)后,即使目标变量是:=定义的,也能正常更新:
DESINATION_VARIABLE := obj3.o obj4.o # 即时赋值 $(call insert_non_existing_symbols, $(OBJS_COLL), DESINATION_VARIABLE) # 此时DESINATION_VARIABLE已被更新为包含新内容的即时值
关键知识点说明
filter-out $(TARGET), $(SOURCE):从SOURCE中过滤掉所有存在于TARGET的元素,直接得到需要新增的内容。$(eval):强制Make立即执行后续语句,是实现函数内修改变量的核心。- 传递变量名而非变量值:要在宏里修改变量,必须传递变量的名字(如
DESINATION_VARIABLE),而非变量的值($(DESINATION_VARIABLE)),这样$(eval $2 += ...)才能正确定位到目标变量。
内容的提问来源于stack exchange,提问作者Ulises V.
相关产品推荐
相关产品推荐

