makefile缺少分隔符。

17

好的,我卡在这里了,我不知道我做错了什么。在处理一个更复杂的makefile时一切都很顺利,但突然间我遇到了“缺少分隔符”的错误。我能够将它简化为一个非常简单的场景:

test.mk

define push_dir
$(info ${1})
endef

define pop_dir
$(info ${1})
endef

define include_submake
$(call push_dir,${1})
$(call pop_dir,${1})
endef

简单

include test.mk

INITIAL_SUBMAKE:= includeme.mk
$(call include_submake,${INITIAL_SUBMAKE})

process:
    @echo Processed...

而且输出:

C:\project>make -f Simple process
includeme.mk
includeme.mk
Simple:4: *** missing separator.  Stop.

includeme.mk实际上并不存在。我不知道出了什么问题,我已经尝试了很多方法。如果我像这样用info将调用include_submake包围起来:

$(info $(call include_submake,${INITIAL_SUBMAKE}))

缺少分隔符错误消失了。另外,如果我在include_submake中只调用其中一个函数,它就能正常运行。此外,如果我直接调用这些函数而不是通过include_submake调用它们,也能正常工作:

include test.mk

INITIAL_SUBMAKE:= includeme.mk
$(call push_dir,${INITIAL_SUBMAKE})
$(call pop_dir,${INITIAL_SUBMAKE})

process:
    @echo Processed...


C:\project>make -f Simple process
includeme.mk
includeme.mk
Processed...

我觉得我在这里忽略了某些基本的东西。感谢您的帮助。


还不确定复制粘贴出了什么问题,但是对于进程:目标,我在@echo命令前面有一个制表符。 - ThePosey
2个回答

20
“缺少分隔符”错误是由于include_submake返回非空值导致的,在您的情况下,这个值是一个单行换行符。Make仅允许在不被认为是某个规则或其他指令的一部分的表达式中出现空格字符(即空格或制表符)。
使用普通的Make变量赋值重写您的函数,错误就会消失:
push_dir = \
    $(info $1)

pop_dir = \
    $(info $1)

include_submake = \
    $(call push_dir,$1) \
    $(call pop_dir,$1)

更新: define 与普通变量赋值的区别

回答第一个评论中的问题。个人而言,我更喜欢在几种情况下使用define指令。

eval函数一起使用

正如GNU Make手册所建议的那样,define指令与eval函数结合使用非常有用。以下是手册中的示例(强调是我的):

程序 = 服务器 客户端
服务器_OBJS = 服务器.o 服务器_priv.o 服务器访问.o 服务器_LIBS = priv 协议
客户端_OBJS = 客户端.o 客户端_api.o 客户端_mem.o 客户端_LIBS = 协议 #以下内容都是通用的 .PHONY: all all: $(程序) define PROGRAM_template $(1): $$($(1)_OBJS) $$($(1)_LIBS:%=-l%) ALL_OBJS += $$($(1)_OBJS) endef $(foreach prog,$(程序),$(eval $(call PROGRAM_template,$(prog)))) $(程序): $(LINK.o) $^ $(LDLIBS) -o $@
clean: rm -f $(ALL_OBJS) $(程序)

生成器模板

如果你想要从GNU Make中的一些信息生成文件,那么原样变量就非常适合。例如,考虑基于Makefile中的一些信息生成头文件。

# 参数:
#   1. 头标识符。
定义头模板
/* This file is generated by GNU Make $(MAKE_VERSION). */
#ifndef $(inclusion_guard) #define $(inclusion_guard) $(foreach inc,$($1.includes), #include <$(inc).h>)
/* Something else... */
#endif /* $(inclusion_guard) */ 结束定义 # 1. 唯一的头标识符。 inclusion_guard = \ __GEN_$1_H # Shell 转义。 sh_quote = \ '$(subst ','"'"',$1)'
foo.includes := bar baz
HEADERS := foo.h $(HEADERS) : %.h : @printf "%s" $(call sh_quote,$(call header_template,$(*F)))> $@

扩展 Make 语法

在我们的项目中,我们使用自己构建的系统Mybuild,它完全基于GNU Make实现。作为改进Make内置语言贫乏语法的低级技巧之一,我们开发了一个特殊脚本,允许使用扩展语法来定义函数。脚本本身也是用Make编写的,因此它是Make中的元编程。
特别地,可以使用以下功能:
- 定义多行函数而无需使用反斜杠 - 在函数内部使用注释(在普通的Make注释中,只能出现在变量赋值指令之外) - 定义自定义宏,如$(assert ...)$(lambda ...) - 内联简单函数,如$(eq s1,s2)(字符串相等检查)
这是一个使用扩展语法编写函数的示例。请注意,在调用 $(def_all) 后,它将成为有效的 Make 函数,并可以像往常一样调用。
# 反转指定列表。
#   1. 列表
# 返回:
#   元素顺序相反的列表。
define reverse
    # 从空列表开始。
    $(fold ,$1,
# 将每个新元素 ($2) 前置到 # 之前计算结果的前面。 $(lambda $2 $1)) endef $(def_all)

使用这些新功能,我们能够实现一些非常酷的东西(至少对于 Make 来说是这样的 :-)),包括:

  • 具有动态对象分配、类继承、方法调用等的面向对象层
  • LALR 解析器运行时引擎,用于解析器生成 GOLD Parser Builder
  • 建模库,用于支持使用 EMF 生成的模型的运行时

欢迎在您自己的项目中使用代码的任何部分!


那绝对让错误消失了。在使用$(call)时,有没有一个好的方法来推理何时应该使用define而不是=语义?根据你所说的,我可以用$(strip)包装$(call),一切都正常工作。如果您有任何可以详细说明的内容,将不胜感激。不幸的是,我只能给出1个赞。 - ThePosey
哇!谢谢你啊。解释得非常好,而且超出了我的期望。我会给你的其他帖子点赞的。我也会看看你的embox构建系统。我在你的个人资料上看到了这个网站,但还没有机会仔细研究它。 - ThePosey
@ThePosey,不客气,哦,谢谢你的点赞!实际上Embox是一个操作系统内核,而Mybuild是在Embox中使用的构建自动化和配置工具。 - Eldar Abusalimov

3

我遇到了同样的问题,我插入了“制表符”,删除了“制表符”,重新插入以确保无误,但是还是出现了相同的错误信息。

然而,我是在XCodem中进行这些操作的,令我惊讶的是,它插入了空格,而不是“\t”。一旦我使用了不同的编辑器,这些“幻影”错误就消失了。

希望对你有所帮助...


谢谢!在函数$(call)之前有4个空格而不是制表符。 - kdmitry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接