17

好吧,我坚持这一点,我不知道我做错了什么。在更复杂的 makefile 上工作一切顺利,但突然之间我得到了“缺少分隔符”错误。我能够将其隔离为一个非常简单的场景:

测试.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...

我觉得我在这里忽略了一些基本的东西。谢谢你的帮助。

4

2 回答 2

20

发生错误是因为的missing separator非空返回值include_submake,在您的情况下这是一个换行符。Make 只允许空白字符(即空格或制表符)出现在不被假定为某些规则或其他指令的一部分的表达式中。

使用普通的 Make 变量赋值重写你的函数,错误应该消失:

push_dir = \
    $(info $1)

pop_dir = \
    $(info $1)

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

UPD.: definevs 普通的旧变量赋值

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

eval功能一起使用

正如 GNU Make 手册所建议的那样,指令与functiondefine结合使用非常有用。手册中的示例(重点是我的):eval

程序 = 服务器客户端

server_OBJS = server.o server_priv.o server_access.o
server_LIBS = priv 协议

client_OBJS = client.o client_api.o client_mem.o
client_LIBS = 协议

# 这之后的一切都是通用的

.PHONY:全部
全部:$(程序)

定义 PROGRAM_template
  $(1): $$($(1)_OBJS) $$($(1)_LIBS:%=-l%)
  ALL_OBJS += $$($(1)_OBJS)
endef

$(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog))))

$(程序):
    $(LINK.o) $^ $(LDLIBS) -o $@

干净的:
    rm -f $(ALL_OBJS) $(程序)

生成器模板

当您想从 GNU Make 生成文件时,逐字变量非常适合。例如,考虑根据 Makefile 中的一些信息生成头文件。

#参数:
# 1. 标头标识符。
定义 header_template
/* 此文件由 GNU Make $(MAKE_VERSION) 生成。*/

#ifndef $(inclusion_guard)
#define $(inclusion_guard)

$(foreach 公司,$($1.includes),
#include <$(inc).h>)

/* 别的... */

#endif /* $(inclusion_guard) */

endef

# 1. 唯一的标头标识符。
包含保护= \
    __GEN_$1_H

# 壳逃逸。
sh_quote = \
    '$(subst','"'"',$1)'

foo.includes := bar baz

标题:= foo.h

$(标头) : %.h :
    @printf "%s" $(call sh_quote,$(call header_template,$(*F)))> $@

扩展的 Make 语法

在我们的项目中,我们使用自己的构建系统Mybuild,它完全在 GNU Make 之上实现。作为我们用来改善 Make 内置语言的糟糕语法的低级技巧之一,我们开发了一个特殊的脚本,它允许使用扩展语法来定义函数。脚本本身也是用 Make 编写的,因此它是 Make 中的一种元编程。

特别是,可以使用以下功能:

  • 定义多行函数而不需要使用反斜杠
  • 在函数内部使用注释(在普通的 Make 注释中只能出现在变量赋值指令之外)
  • 定义自定义宏,如$(assert ...)or$(lambda ...)
  • 内联简单函数,如$(eq s1,s2)(字符串相等检查)

这是一个如何使用扩展语法编写函数的示例。请注意,它成为一个有效的 Make 函数,并且可以在调用$(def_all).

# 反转指定的列表。
# 1. 列表
# 返回:
# 其元素以相反顺序排列的列表。
define reverse 
    # 从空列表开始。
    $(折叠,$1,

        # 将每个新元素 ($2) 添加到
        # 先前计算的结果。
        $(lambda $2 $1) )
 endef
$(def_all)

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

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

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

于 2012-06-04T15:38:14.457 回答
1

我遇到了同样的问题。我插入了“标签”,删除了“标签”,重新插入以确保。相同的错误信息。

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

HTH...

于 2014-02-18T03:03:10.790 回答