【问题标题】:info guarded by conditional in recipe?配方中的条件保护信息?
【发布时间】:2015-09-30 11:23:58
【问题描述】:

我们有一个库项目,它从旧 i386/i686/x86_64 中常见的未对齐数据访问过渡到对齐数据访问。推动力是 GCC 和矢量化,这导致 -O3 崩溃(GCC 甚至将字符串矢量化与 SSE4 原语相比)。

如果有人运行 make 并且特定的定义不存在,那么我们想打印一个 info 消息警告他们。我在制作食谱时遇到了麻烦。

以下配方在运行时会产生错误(它使用 TAB,而不是空格):

.PHONY check_defines
check_defines:
    CONFIG_ALIGNED := $(shell $(EGREP) -i -c "[[:space:]]*#[[:space:]]*define[[:space:]]*NO_UNALIGNED_DATA_ACCESS" config.h)
    CXX_ALIGNED := $(shell echo $(value CXXFLAGS) | $(EGREP) -i -c "-DNO_UNALIGNED_DATA_ACCESS")
    ifeq ($(CONFIG_ALIGNED)$(CXX_ALIGNED),00)
        $(info Warning: NO_UNALIGNED_DATA_ACCESS is not defined)
    endif

错误是:

$ make check_defines
GNUmakefile:383: *** missing separator.  Stop.

第 383 行是CONFIG_ALIGNED := ...。 5.1 Recipe Syntax 上的手册对我没什么帮助。

如何制定规则以在配方中执行测试?

【问题讨论】:

  • 您可能希望在 -D 和 NO_UNALIGNED_DATA_ACCESS 之间允许空格
  • 您在虚假目标声明中缺少冒号。它应该是:.PHONY: check_defines。这确实会触发 missing separator 错误。

标签: gnu-make


【解决方案1】:

Makefile 不像脚本那样被解析并被强制执行。食谱不是子程序。

您链接到的页面明确告诉您配方的主体被视为 shell 命令,而不是声明/表达式:

  • “规则上下文”中的变量定义由制表符缩进为行中的第一个字符,将被视为配方的一部分,而不是 make 变量定义,并传递给 shell。
  • “规则上下文”中的条件表达式(ifdef、ifeq 等,请参见条件语法)以制表符作为行上的第一个字符缩进,将被视为配方的一部分并传递给外壳。

所以你不能声明 make 变量或使用 make 条件。您只能使用 shell 命令。

尝试类似:

CONFIG_ALIGNED := $(shell $(EGREP) -i -c "[[:space:]]*#[[:space:]]*define[[:space:]]*NO_UNALIGNED_DATA_ACCESS" config.h)
CXX_ALIGNED := $(shell echo $(value CXXFLAGS) | $(EGREP) -i -c "-DNO_UNALIGNED_DATA_ACCESS")
ifeq ($(CONFIG_ALIGNED)$(CXX_ALIGNED),00)
CHECK_FAILED := 1
endif

.PHONY check_defines
check_defines:
        if [ $(CHECK_FAILED) = 1 ]; then echo Warning: NO_UNALIGNED_DATA_ACCESS is not defined; exit 1; fi

或者使用shell检查条件:

CONFIG_ALIGNED := $(shell $(EGREP) -i -c "[[:space:]]*#[[:space:]]*define[[:space:]]*NO_UNALIGNED_DATA_ACCESS" config.h)
CXX_ALIGNED := $(shell echo $(value CXXFLAGS) | $(EGREP) -i -c "-DNO_UNALIGNED_DATA_ACCESS")

.PHONY check_defines
check_defines:
        if [ $(CONFIG_ALIGNED)$(CXX_ALIGNED) = 00 ]; then echo Warning: NO_UNALIGNED_DATA_ACCESS is not defined; exit 1; fi

或者全部用shell,声明shell变量而不是make变量:

.PHONY check_defines
check_defines:
        config_aligned=`$(EGREP) -i -c "[[:space:]]*#[[:space:]]*define[[:space:]]*NO_UNALIGNED_DATA_ACCESS" config.h`
        cxx_aligned=`echo $(value CXXFLAGS) | $(EGREP) -i -c "-DNO_UNALIGNED_DATA_ACCESS")`
        if [ $${config_aligned}$${cxx_aligned} = 00 ]; then echo Warning: NO_UNALIGNED_DATA_ACCESS is not defined; exit 1; fi

(如果 CXXFLAGS 包含诸如`pkg-config --cflags foo` 之类的任何外壳输出,则此版本可能无法工作,因为这会混淆外壳)

【讨论】:

  • 谢谢。最后一个看起来很吸引人,因为除非需要,否则它会避免使用 shell。
  • 请看我的其他回答。不幸的是,该手册有点误导。首先,ifxxx ... endif 预处理器样式的块可以在配方中使用,但正如他们所说,它们前面不能有制表符字符,并且在执行配方时不会评估它们,而是在解析文件时。其次,其余的语法都是可用的,比如$(if ...)表达式,甚至$(eval ...)
【解决方案2】:

在配方中,您需要使用任何$(...) 表达式将make 的解析器退出shell 模式。

.PHONY: check_defines
check_defines:
    $(eval CONFIG_ALIGNED := $(shell echo 0))
    $(eval CXX_ALIGNED := $(shell echo 0))
    $(if $(filter 00,$(CONFIG_ALIGNED)$(CXX_ALIGNED)), \
        $(warning Warning: NO_UNALIGNED_DATA_ACCESS is not defined) \
    )

注意,声明的变量不是规则本地的,所以你最好不要声明它们,直接将shell表达式放在if表达式中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-20
    • 1970-01-01
    • 2012-08-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多