Comment pourrais-je abandonner une exécution de make/makefile basée sur une variable de makefile non définie/valorisée?
Je suis venu avec cela, mais ne fonctionne que si l'appelant n'exécute pas explicitement une cible (c'est-à-dire ne lance que make
seulement).
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)
Je pense que quelque chose comme ça serait une bonne idée, mais je n'ai rien trouvé dans le manuel de make:
ifndef MY_FLAG
.ABORT
endif
TL; DR : utilisez la fonction error
:
ifndef MY_FLAG
$(error MY_FLAG is not set)
endif
Notez que les lignes ne doivent pas être en retrait. Plus précisément, aucun onglet ne doit précéder ces lignes.
Si vous testez plusieurs variables, il est utile de définir une fonction auxiliaire pour cela:
# 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))))
Et voici comment l'utiliser:
$(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)
Cela produirait une erreur comme celle-ci:
Makefile:17: *** Undefined OUT_DIR (build directory). Stop.
Le vrai contrôle est fait ici:
$(if $(value $1),,$(error ...))
Cela reflète le comportement de la conditionnelle ifndef
, de sorte qu'une variable définie avec une valeur vide est également considérée comme "non définie". Mais cela n’est vrai que pour les variables simples et les variables récursives explicitement vides:
# 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)
Comme suggéré par @VictorSergienko dans les commentaires, un comportement légèrement différent peut être souhaité:
$(if $(value $1)
teste si la valeur est non vide. Parfois ça va si la variable est définie avec une valeur vide . J'utiliserais$(if $(filter undefined,$(Origin $1)) ...
Et:
De plus, s'il s'agit d'un répertoire et qu'il doit exister lors de l'exécution de la vérification, j'utiliserais
$(if $(wildcard $1))
. Mais serait une autre fonction.
Il est également possible d’étendre la solution de manière à ne nécessiter une variable que si une certaine cible est invoquée.
$(call check_defined, ...)
de l'intérieur de la recetteIl suffit de déplacer le chèque dans la recette:
foo :
@:$(call check_defined, BAR, baz value)
Le premier signe @
Désactive l'écho de la commande et :
Est la commande réelle, un shell no-op stub .
La fonction check_defined
Peut être améliorée pour générer également le nom de la cible (fourni par la 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 `$@')))
Ainsi, maintenant, une vérification échouée produit une sortie bien formatée:
Makefile:7: *** Undefined BAR (baz value) required by target `foo'. Stop.
check-defined-MY_FLAG
Cible spécialePersonnellement, j'utiliserais la solution simple et directe ci-dessus. Cependant, par exemple, cette réponse suggère d'utiliser un objectif spécial pour effectuer la vérification proprement dite. On pourrait essayer de généraliser cela et définir la cible comme une règle de modèle implicite:
# 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 :
Usage:
foo :|check-defined-BAR
Notez que le check-defined-BAR
Est répertorié comme condition préalable ordre uniquement (|...
).
Avantages:
Les inconvénients:
make -t
(Voir au lieu d’exécuter des recettes ), votre répertoire racine sera pollué par de nombreux fichiers check-defined-...
. Ceci est un triste inconvénient du fait que les règles de modèle ne peuvent pas être déclarées .PHONY
.Je crois que ces limitations peuvent être surmontées en utilisant quelques eval
magie et expansion secondaire bidouilles, bien que je ne sois pas sûr que cela en vaille la peine.
Utilisez la fonction shell test
:
foo:
test $(something)
Usage:
$ make foo
test
Makefile:2: recipe for target 'foo' failed
make: *** [foo] Error 1
$ make foo something=x
test x
Vous pouvez utiliser un IF pour tester:
check:
@[ "${var}" ] || ( echo ">> var is not set"; exit 1 )
Résultat:
$ make check
>> var is not set
Makefile:2: recipe for target 'check' failed
make: *** [check] Error 1
Utilisez le traitement des erreurs du shell pour les variables non définies (notez le 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
Si vous avez besoin d’un message d’erreur personnalisé, ajoutez-le après le ?
:
$ 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