如果未设置变量,如何中止makefile? [英] How to abort makefile if variable not set?

查看:52
本文介绍了如果未设置变量,如何中止makefile?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何基于未设置/赋值makefile的变量来中止make/makefile执行?

How could I abort a make/makefile execution based on a makefile's variable not being set/valued?

我想到了这一点,但是仅在调用者未明确运行目标(即仅运行 make 的情况下)有效.

I came up with this, but works only if caller doesn't explicitly run a target (i.e. runs make only).

ifeq ($(MY_FLAG),)
abort:   ## This MUST be the first target :( ugly
    @echo Variable MY_FLAG not set && false
endif

all:
    @echo MY_FLAG=$(MY_FLAG)

我认为这样的想法是个好主意,但在make的手册中没找到任何内容:

I think something like this would be a good idea, but didn't find anything in make's manual:

ifndef MY_FLAG
.ABORT
endif

推荐答案

TL; DR :使用错误功能:

ifndef MY_FLAG
$(error MY_FLAG is not set)
endif

请注意,行不能缩进.更准确地说,在这些行之前都不能使用任何制表符.

Note that the lines must not be indented. More precisely, no tabs must precede these lines.

如果要测试许多变量,则值得为其定义辅助功能:

In case you're going to test many variables, it's worth defining an auxiliary function for that:

# Check that given variables are set and all have non-empty values,
# die with an error otherwise.
#
# Params:
#   1. Variable name(s) to test.
#   2. (optional) Error message to print.
check_defined = \
    $(strip $(foreach 1,$1, \
        $(call __check_defined,$1,$(strip $(value 2)))))
__check_defined = \
    $(if $(value $1),, \
      $(error Undefined $1$(if $2, ($2))))

这是如何使用它:

And here is how to use it:

$(call check_defined, MY_FLAG)

$(call check_defined, OUT_DIR, build directory)
$(call check_defined, BIN_DIR, where to put binary artifacts)
$(call check_defined, \
            LIB_INCLUDE_DIR \
            LIB_SOURCE_DIR, \
        library path)


这将输出如下错误:


This would output an error like this:

Makefile:17: *** Undefined OUT_DIR (build directory).  Stop.

注意:

真正的检查在这里完成:

Notes:

The real check is done here:

$(if $(value $1),,$(error ...))

这反映了 ifndef 条件的行为,因此定义为空值的变量也被视为未定义".但这仅适用于简单变量和明确为空的递归变量:

This reflects the behavior of the ifndef conditional, so that a variable defined to an empty value is also considered "undefined". But this is only true for simple variables and explicitly empty recursive variables:

# ifndef and check_defined consider these UNDEFINED:
explicitly_empty =
simple_empty := $(explicitly_empty)

# ifndef and check_defined consider it OK (defined):
recursive_empty = $(explicitly_empty)

如@VictorSergienko在评论中所建议,可能需要略有不同的行为:

As suggested by @VictorSergienko in the comments, a slightly different behavior may be desired:

$(如果$(value $ 1)测试该值是否为非空.如果变量定义为空值,则有时 OK .我会使用 $(如果$(未定义过滤器,$(原始$ 1))...

$(if $(value $1) tests if the value is non-empty. It's sometimes OK if the variable is defined with an empty value. I'd use $(if $(filter undefined,$(origin $1)) ...

并且:

此外,如果它是目录并且必须存在,则在运行检查时,我将使用 $(如果$(通配符$ 1)).但这将是另一个功能.

Moreover, if it's a directory and it must exist when the check is run, I'd use $(if $(wildcard $1)). But would be another function.

针对特定目标的检查

还可以扩展解决方案,以便仅在调用特定目标时才需要变量.

Target-specific check

It is also possible to extend the solution so that one can require a variable only if a certain target is invoked.

只需将支票移到配方中:

Just move the check into the recipe:

foo :
    @:$(call check_defined, BAR, baz value)

前导 @ 符号关闭命令回显,: 是实际命令,一个 shell

The leading @ sign turns off command echoing and : is the actual command, a shell no-op stub.

可以改进 check_defined 函数以输出目标名称(通过 $ @ 变量提供):

The check_defined function can be improved to also output the target name (provided through the $@ variable):

check_defined = \
    $(strip $(foreach 1,$1, \
        $(call __check_defined,$1,$(strip $(value 2)))))
__check_defined = \
    $(if $(value $1),, \
        $(error Undefined $1$(if $2, ($2))$(if $(value @), \
                required by target `$@')))

因此,现在失败的检查会产生格式正确的输出:

So that, now a failed check produces a nicely formatted output:

Makefile:7: *** Undefined BAR (baz value) required by target `foo'.  Stop.

check-defined-MY_FLAG 特殊目标

就我个人而言,我将使用上面简单而直接的解决方案.但是,例如,此答案建议使用特殊目标来执行实际操作查看.可以尝试将其概括化,然后将目标定义为隐式模式规则:

check-defined-MY_FLAG special target

Personally I would use the simple and straightforward solution above. However, for example, this answer suggests using a special target to perform the actual check. One could try to generalize that and define the target as an implicit pattern rule:

# Check that a variable specified through the stem is defined and has
# a non-empty value, die with an error otherwise.
#
#   %: The name of the variable to test.
#   
check-defined-% : __check_defined_FORCE
    @:$(call check_defined, $*, target-specific)

# Since pattern rules can't be listed as prerequisites of .PHONY,
# we use the old-school and hackish FORCE workaround.
# You could go without this, but otherwise a check can be missed
# in case a file named like `check-defined-...` exists in the root 
# directory, e.g. left by an accidental `make -t` invocation.
.PHONY : __check_defined_FORCE
__check_defined_FORCE :

用法:

foo :|check-defined-BAR

请注意, check-defined-BAR 被列为

Notice that the check-defined-BAR is listed as the order-only (|...) prerequisite.

优点:

  • (可以说)更简洁的语法

缺点:

  • One can't specify a custom error message
  • Running make -t (see Instead of Executing Recipes) will pollute your root directory with lots of check-defined-... files. This is a sad drawback of the fact that pattern rules can't be declared .PHONY.

我相信,可以使用一些 eval 魔术和二次扩展骇客,尽管我不确定是否值得.

I believe, these limitations can be overcome using some eval magic and secondary expansion hacks, although I'm not sure it's worth it.

这篇关于如果未设置变量,如何中止makefile?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆