ChatGPT解决这个技术问题 Extra ChatGPT

如果未设置变量,如何中止makefile?

如何根据未设置/赋值的makefile变量中止make/makefile执行?

我想出了这个,但只有在调用者没有明确运行目标时才有效(即仅运行 make)。

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的手册中没有找到任何东西:

ifndef MY_FLAG
.ABORT
endif

E
Eldar Abusalimov

TL;DR:使用 error function

ifndef MY_FLAG
$(error MY_FLAG is not set)
endif

请注意,这些行不能缩进。更准确地说,这些行之前不能有制表符。

通用解决方案

如果您要测试许多变量,则值得为此定义一个辅助函数:

# 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))))

以下是如何使用它:

$(call check_defined, MY_FLAG) $(call check_defined, OUT_DIR, build directory) $(call check_defined, BIN_DIR, 存放二进制工件的位置) $(call check_defined, \LIB_INCLUDE_DIR \LIB_SOURCE_DIR, \ library path)

这将输出如下错误:

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

笔记:

真正的检查在这里完成:

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

这反映了 ifndef 条件的行为,因此定义为空值的变量也被视为“未定义”。但这仅适用于简单变量和显式空递归变量:

# 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 在评论中所建议的那样,可能需要稍微不同的行为:

$(if $(value $1) 测试该值是否非空。如果变量定义为空值有时是可以的。我会使用 $(if $(filter undefined,$(origin $1)) .. .

和:

此外,如果它是一个目录并且在运行检查时它必须存在,我会使用 $(if $(wildcard $1)).但将是另一个功能。

特定目标检查

还可以扩展解决方案,以便仅在调用某个目标时才需要变量。

$(call check_defined, ...) 从配方内部

只需将支票移入配方:

foo : @:$(调用 check_defined, BAR, baz 值)

前导 @ 符号关闭命令回显,: 是实际命令,即 shell no-op stub

显示目标名称

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

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 `$@')))

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

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

check-defined-MY_FLAG 特殊目标

我个人会使用上面简单直接的解决方案。但是,例如,this answer 建议使用特殊目标来执行实际检查。可以尝试将其概括并将目标定义为隐式模式规则:

# 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 :|检查-定义-BAR

请注意,check-defined-BAR 被列为 order-only (|...) 先决条件。

优点:

(可以说)更简洁的语法

缺点:

无法指定自定义错误消息

运行 make -t (请参阅而不是执行食谱)将污染您的根目录,其中包含大量检查定义的-...文件。这是不能将模式规则声明为 .PHONY 的事实的一个可悲缺点。

我相信,可以使用一些 eval 魔法和 secondary expansion hack 来克服这些限制,尽管我不确定这是否值得。


究竟是什么?从未使用过 Mac,尽管我猜它默认安装了另一个 Make 实现(例如 BSD Make 而不是 GNU Make)。我建议您首先检查 make --version
这似乎不适用于 make 3.81。即使定义了变量(并且可以回显),它也总是出错。
啊,你需要像链接的副本一样构造它。
@bibstha我添加了想到的选项,请阅读更新的答案。
我会为新手(像我一样)添加一个澄清,即 ifndef 不需要缩进:) 我在其他地方发现了这个提示,突然间我所有的错误都变得有意义了。
M
Messa

使用外壳函数 test

foo:
    test $(something)

用法:

$ make foo
test 
Makefile:2: recipe for target 'foo' failed
make: *** [foo] Error 1
$ make foo something=x
test x

这就是我在遇到相同问题时使用的方法 - 谢谢,Messa!我做了两处细微的修改:1) 我创建了一个只有 testcheckforsomething 目标,并使 foo 依赖于它,2) 我将检查改为 @if test -z "$(something)"; then echo "helpful error here"; exit 1; fi。这让我能够添加一个有用的错误,并让我能够让新目标的名称更能说明问题所在。
有了这个我得到Makefile:5: *** missing separator. Stop.
为了简洁起见,我使用 test -n "$(something) || (echo "message" ; exit 1) 来避免显式 if
@silgon您可能使用空格而不是制表符缩进。
r
raittes

您可以使用 IF 来测试:

check:
        @[ "${var}" ] || ( echo ">> var is not set"; exit 1 )

结果:

$ make check
>> var is not set
Makefile:2: recipe for target 'check' failed
make: *** [check] Error 1

[ 是命令 test 的别名,所以这与上面的@Messa 答案相同。但是,这更紧凑,并且包括错误消息生成。
在大多数情况下,这似乎是最干净的答案。
k
kesselborn

对未设置的变量使用 shell 错误处理(注意双 $):

$ cat Makefile
foo:
        echo "something is set to $${something:?}"

$ make foo
echo "something is set to ${something:?}"
/bin/sh: something: parameter null or not set
make: *** [foo] Error 127


$ make foo something=x
echo "something is set to ${something:?}"
something is set to x

如果您需要自定义错误消息,请将其添加到 ? 之后:

$ cat Makefile
hello:
        echo "hello $${name:?please tell me who you are via \$$name}"

$ make hello
echo "hello ${name:?please tell me who you are via \$name}"
/bin/sh: name: please tell me who you are via $name
make: *** [hello] Error 127

$ make hello name=jesus
echo "hello ${name:?please tell me who you are via \$name}"
hello jesus

b
bsimpson53

为简单起见:

$ cat Makefile
check-%:
        @: $(if $(value $*),,$(error $* is undefined))

bar:| check-foo
        echo "foo is $$foo"

输出:

$ make bar
Makefile:2: *** foo is undefined. Stop.
$ make bar foo="something"
echo "foo is $$foo"
foo is something

可以被 $ touch check-foo; make bar 愚弄...在其他答案中需要 check_defined_FORCE 技巧
H
HolyBlackCat

另外的选择:

MY_FLAG = $(error Please set this flag)

尝试在任何地方使用此变量都会导致错误,除非它被命令行覆盖。

要同时接受环境变量,请使用 ?=

MY_FLAG ?= $(error Please set this flag)