変数が設定されていない場合はメイクファイルを中止します


151

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

回答:


271

TL; DRerror関数を使用:

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, where to put binary artifacts)
$(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 :
    @:$(call check_defined, BAR, baz value)

先頭の@記号はコマンドのエコーをオフにします。これ:は実際のコマンドであり、シェルの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 特別なターゲット

個人的には、上記のシンプルで簡単なソリューションを使用します。ただし、たとえば、この回答は、実際のチェックを実行するために特別なターゲットを使用することを提案しています。これを一般化して、ターゲットを暗黙のパターンルールとして定義することができます。

# 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注文のみ|...)の前提条件としてリストされて いることに注意してください。

長所:

  • (間違いなく)より明確な構文

短所:

  • カスタムエラーメッセージを指定することはできません
  • 実行するとmake -t(「レシピを実行する代わりに」を参照)、多数のcheck-defined-...ファイルでルートディレクトリが汚染されます。これは、パターンルールを宣言できない.PHONYという悲しい欠点です。

これらの制限は、いくつかのeval魔法の拡張ハックを使用して克服できると思いますが、それだけの価値があるかどうかはわかりません。


正確には?Macを使用したことはありませんが、デフォルトでMakeの別の実装がインストールされていると思います(たとえば、GNU MakeではなくBSD Make)。make --version最初のステップとして確認することをお勧めします。
Eldar Abusalimov 2015

1
これはmake 3.81では機能していないようです。変数が定義されている(エコーされる可能性がある)場合でも、常にエラーが発生します。
OrangeDog 2016年

ああ、あなたはそれをリンクされた複製のように正確に構造化する必要があります。
OrangeDog 2016年

1
@bibstha頭に浮かぶオプションを追加しました。更新された回答を読んでください。
Eldar Abusalimov 2016

2
ifndefをインデントする必要がないことを説明するために、noobiesの説明を追加します(私と同じです)。
ヘリオス2017年

40

シェル関数を使用しますtest

foo:
    test $(something)

使用法:

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

3
これは私が同じ問題に直面したときに使用したものです-ありがとう、メッサ!2つのわずかな変更を加えました。1)checkforsomethingのみが含まれるターゲットを作成し、testそれにfoo依存するようにしました。2)チェックを@if test -z "$(something)"; then echo "helpful error here"; exit 1; fi代わりに変更しました。これにより、有用なエラーを追加できるようになり、新しいターゲットの名前を、問題の原因をもう少し示すことができました。
ブライアンジェラルド

これで私は得るMakefile:5: *** missing separator. Stop.
シルゴン

7
簡潔にtest -n "$(something) || (echo "message" ; exit 1)するために、私は明示的に回避するために使用しましたif
user295691 2018

@silgonは、タブではなくスペースを使用してインデントしている可能性があります。
eweb

9

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と同じ答えです。ただし、これはよりコンパクトで、エラーメッセージの生成が含まれます。
user295691

6

設定されていない変数のシェルエラー処理を使用します(doubleに注意してください$)。

$ 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

2

簡潔さと簡潔さのために:

$ 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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.