为什么 Make 不允许缩进 "if" 语句?
Why doesn't Make allow indented "if" statements?
当我尝试读取带有嵌套逻辑的 makefile 时,这一直是我的痛处,Make 不允许缩进 if 语句.为什么会这样,是否有解决此限制的好方法,并且仍然具有可读的 makefile?
更新:我现在意识到这个问题是基于一个错误的前提,但我相信把它留在这里可能对任何犯过我同样错误的人有价值.
我不知道为什么您认为不支持缩进条件。当我在以下示例中使用它们时,它们似乎工作正常:
.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:
A recipe is an action that make
carries out. A recipe may have more than one command, either on the same line or each on its own line. Please note: you need to put a tab character at the beginning of every recipe line! This is an obscurity that catches the unwary.
由于 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
解决方案
- 组织 makefile 先有条件,然后有规则,即规则后不再有 TAB 缩进,除了配方,或
- 始终确保将 SPACE 用于条件、变量赋值和规则行。
- 将
.RECIPEPREFIX
设置为 非空白 字符,例如>
并用它来表示配方行。1
除非您有一个显示 TAB 和 SPACEs 之间区别的编辑器,否则备选方案 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)
然后将您的编辑器切换为仅使用 SPACEs。但这让事情变得更糟,即现在 make
无法区分正常缩进和配方缩进。如果您使用上面的示例尝试此操作,您会注意到它现在对于启用缩进规则之一的任何调用都会失败。
感谢其他人的帮助,我现在意识到我的问题是在错误的前提下提出的。 Makefiles 绝对 do 允许缩进 if 语句,或者更准确地说是缩进条件语句。他们不允许的 - 至少开箱即用 - 是 tabbed 条件。这是因为,默认情况下,Make 将 tabs 解释为特别有意义的字符。几乎所有以制表符开头的行都被解释为 recipe 的一部分。因此,任何 not 旨在成为食谱一部分的行 - 例如条件 - 应该 not 以制表符开头。
至于回答我的问题为什么他们选择以这种方式使用制表符的部分,我还没有找到答案。也许设计者打算少用条件语句。
至于解决方法,在这里我将尝试描述几个。
如果您没有显示 whitespace 字符的编辑器,第一个解决方案会非常痛苦,但如果有,最简单的方法可能就是添加一些 空格 来缩进你的非配方代码。这是一个相当骇人听闻的解决方法,而且可能是不明智的。
另一个解决方案(由@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 $@
当我尝试读取带有嵌套逻辑的 makefile 时,这一直是我的痛处,Make 不允许缩进 if 语句.为什么会这样,是否有解决此限制的好方法,并且仍然具有可读的 makefile?
更新:我现在意识到这个问题是基于一个错误的前提,但我相信把它留在这里可能对任何犯过我同样错误的人有价值.
我不知道为什么您认为不支持缩进条件。当我在以下示例中使用它们时,它们似乎工作正常:
.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:
A recipe is an action that
make
carries out. A recipe may have more than one command, either on the same line or each on its own line. Please note: you need to put a tab character at the beginning of every recipe line! This is an obscurity that catches the unwary.
由于 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
解决方案
- 组织 makefile 先有条件,然后有规则,即规则后不再有 TAB 缩进,除了配方,或
- 始终确保将 SPACE 用于条件、变量赋值和规则行。
- 将
.RECIPEPREFIX
设置为 非空白 字符,例如>
并用它来表示配方行。1
除非您有一个显示 TAB 和 SPACEs 之间区别的编辑器,否则备选方案 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)
然后将您的编辑器切换为仅使用 SPACEs。但这让事情变得更糟,即现在 make
无法区分正常缩进和配方缩进。如果您使用上面的示例尝试此操作,您会注意到它现在对于启用缩进规则之一的任何调用都会失败。
感谢其他人的帮助,我现在意识到我的问题是在错误的前提下提出的。 Makefiles 绝对 do 允许缩进 if 语句,或者更准确地说是缩进条件语句。他们不允许的 - 至少开箱即用 - 是 tabbed 条件。这是因为,默认情况下,Make 将 tabs 解释为特别有意义的字符。几乎所有以制表符开头的行都被解释为 recipe 的一部分。因此,任何 not 旨在成为食谱一部分的行 - 例如条件 - 应该 not 以制表符开头。
至于回答我的问题为什么他们选择以这种方式使用制表符的部分,我还没有找到答案。也许设计者打算少用条件语句。
至于解决方法,在这里我将尝试描述几个。
如果您没有显示 whitespace 字符的编辑器,第一个解决方案会非常痛苦,但如果有,最简单的方法可能就是添加一些 空格 来缩进你的非配方代码。这是一个相当骇人听闻的解决方法,而且可能是不明智的。
另一个解决方案(由@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 $@