为什么不让缩进的“ if”语句?

时间:2019-02-22 04:20:37

标签: gnu-make

当尝试读取带有嵌套逻辑的makefile时,这一直困扰着我, Make 不允许缩进的 if 语句。为什么会这样,并且有一个好的方法来解决此限制,并且仍然具有可读的Makefile?

更新:我现在意识到这个问题是基于错误的前提,但是我相信,将其留在这里对于任何犯过与我相同的错误的人可能都是有价值的。

2 个答案:

答案 0 :(得分:2)

我不知道您为什么会感到不支持缩进条件。在以下示例中使用它们时,它们似乎确实可以正常工作:

.PHONY: all
all:

CONFIGS :=

ifeq ($(CONFIG1),1)
  $(info CONFIG1 selected)

  CONFIGS += config1
  all: config1

  config1:
    @echo $@


  ifeq ($(CONFIG2),1)
    $(info CONFIG2 selected)

    CONFIGS += config2
    all: config2

    config2:
    @echo $@

  else
    $(info CONFIG2 not selected)
  endif
else
  $(info CONFIG1 NOT selected)
endif

all:
    @echo "all: $(CONFIGS)"

注意:在我的示例中,TABS可能无法在复制和粘贴后保存下来。因此,您必须重新输入它们以获取食谱。

试运行:

$ make
CONFIG1 NOT selected
all:

$ make CONFIG1=1
CONFIG1 selected
CONFIG2 not selected
config1
all:  config1

$ make CONFIG1=1 CONFIG2=1
CONFIG1 selected
CONFIG2 selected
config1
config2
all:  config1 config2

但是...

在某些情况下,压痕会导致问题。引用GNU make manual

  

配方是make执行的动作。配方可能在同一行或每行都具有多个命令。 请注意:,您需要在每个配方行的开头添加一个制表符!这是一个模糊的地方,引起了人们的注意。

随着GNU make在一条规则后将所有TAB缩进行纳入该规则的配方,对于make CONFIG1=1,以下操作将失败:

.PHONY: all
all:

CONFIGS :=

config1:
# TAB in the following line
    @echo $@

# the following lines are indented with TABs
    ifeq ($(CONFIG1),1)
    CONFIGS += config1
    test1:
        @echo $@
    endif

ifeq ($(CONFIG1),1)
all: config1
endif

all:
# TAB in the following line
    @echo "all: $(CONFIGS)"
$ make CONFIG1=1
config1
ifeq (1,1)
/bin/sh: -c: line 0: syntax error near unexpected token `1,1'
/bin/sh: -c: line 0: `ifeq (1,1)'
make: *** [Makefile:9: config1] Error 1

解决方案

  1. 首先将makefile组织为具有条件,然后是规则,即除配方外,规则之后即不再有TAB缩进,或者
  2. 始终确保对条件,变量分配和规则行使用SPACE。
  3. .RECIPEPREFIX设置为非空白字符,例如>,并用它来指示配方行。 1

除非您有一个显示TAB和SPACE之间差异的编辑器,否则选择2可能会使您发疯。我建议改为使用替代项1 ...

以下内容适用于make CONFIG2=1

.PHONY: all
all:

CONFIGS :=

config2:
# TAB in the following line
    @echo $@

# the following lines are indented with SPACES
    ifeq ($(CONFIG2),1)
    CONFIGS += config2
    test2:
# 2 TABs in the following line
        @echo $@
    endif

ifeq ($(CONFIG2),1)
all: config2
endif

all:
# TAB in the following line
    @echo "all: $(CONFIGS)"
$ make CONFIG2=1
config2
all:  config2

1 ,您可能会想像这样将.RECIPEPREFIX设置为SPACE:

_empty        :=
_space        := $(_empty) $(_empty)
.RECIPEPREFIX := $(_space)

,然后将编辑器切换为仅使用SPACE。但这使情况变得更糟,也就是说,make现在无法区分普通缩进和配方缩进。如果您在上面的示例中进行尝试,您会注意到,现在对于启用缩进规则之一的任何调用都将失败。

答案 1 :(得分:0)

在别人的帮助下,我现在意识到我的问题是在错误的前提下提出的。 Makefile绝对 do 允许缩进的if语句,或者缩进的条件语句更加精确。他们不允许的条件-至少是开箱即用的-是附条件的标签。这是因为默认情况下, Make tab 解释为特别有意义的字符。几乎所有以制表符开头的行都被解释为recipe的一部分。因此,任何 not 都不打算作为配方一部分的行(如条件语句)应 not 以制表符开头。

至于回答了我的问题的一部分,问他们为什么选择使用制表符,为什么我没有找到答案。也许设计师打算有条件地减少使用条件。

关于变通办法,在这里我将尝试描述一对夫妇。

如果您没有显示空白字符的编辑器,那么第一个解决方案就是一个痛苦的痛苦,但是如果这样做,最简单的方法可能就是添加一些空格缩进您的非配方代码。不过,这是一个非常骇人的解决方法,建议不要这样做。

另一种解决方案(由@Stefan Becker提供)是将special variable.RECIPEPREFIX设置为制表符以外的字符。这是我尝试过的一个示例:

.RECIPEPREFIX := >
# Now, all recipes will begin with the > character rather than a tab.

things = something another_thing something_else nothing
nothing = true

something: another_thing something_else
# See how each line of a recipe now begins with >.
# You can see I also added a tab after the >. 
# These tabs doesn't mean anything to Make; it's just for readability.
>   $(info Making $@.)
>   @touch $@

another_thing:
>   $(info Making $@.)
    # See also how lines like comments can be tabbed, 
    # but no longer add anything meaningful to recipes.
>   @touch $@

something_else:
>   $(info Making $@.)
>   @touch $@
    # And just to prove the situation with conditionals is resolved...
    # See how the @touch command begins with the new RECIPEPREFIX 
    # but the conditionals don't.
    ifeq ($(nothing),true)
>       $(info Also making nothing, because nothing is true.)
>       @touch nothing
    endif

.PHONY: everything_clean
everything_clean:
>   $(info Cleaning up everything.)
>   rm -f $(things)

值得记住的一件事是,配方行必须开始并使用新的RECIPEPREFIX。就是说,这样的 不会起作用:

something: another_thing something_else
# Remember that the RECIPEPREFIX must come first. 
# Indenting your recipe lines first and then using the RECIPEPRIFX will not work.
    >$(info Making $@.)
    >@touch $@