Abandonner le makefile si la variable n’est pas définie

Comment pourrais-je interrompre une exécution make / makefile basée sur une variable de makefile non définie / évaluée?

Je suis venu avec ceci, mais fonctionne seulement si l’appelant n’exécute pas explicitement une cible (c.-à-d. Exécute 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) 

Je pense que quelque chose comme ceci serait une bonne idée, mais n’a rien trouvé dans le manuel de fabrication:

 ifndef MY_FLAG .ABORT endif 

TL; DR : Utilisez la fonction d’ 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.


Solution générique

Si vous testez de nombreuses variables, il convient 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 = \ $(ssortingp $(foreach 1,$1, \ $(call __check_defined,$1,$(ssortingp $(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. 

Vérification spécifique à la cible

Il est également possible d’étendre la solution afin que l’on puisse exiger une variable uniquement si une certaine cible est appelée.

$(call check_defined, ...) depuis l’intérieur de la recette

Il suffit de déplacer le chèque dans la recette:

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

Le signe @ initial désactive la commande echoing et : est la commande réelle, un stub de shell sans opération .

Afficher le nom de la cible

La fonction check_defined peut être améliorée pour afficher également le nom de la cible (fourni via la variable $@ ):

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

Ainsi, un contrôle échoué produit désormais une sortie bien formatée:

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

check-defined-MY_FLAG cible spéciale

Personnellement, j’utiliserais la solution simple et directe ci-dessus. Cependant, par exemple, cette réponse suggère d’utiliser une cible spéciale pour effectuer la vérification réelle. 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 prerequirejsites 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, eg 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 à la commande uniquement ( |... ).

Avantages:

  • (sans doute) une syntaxe plus propre

Les inconvénients:

  • On ne peut pas spécifier un message d’erreur personnalisé
  • L’exécution de make -t (voir Au lieu d’exécuter des recettes ) va polluer votre répertoire racine avec beaucoup de fichiers à check-defined-... . Ceci est un sortingste inconvénient du fait que les règles de configuration ne peuvent pas être déclarées .PHONY .

Je crois que ces limitations peuvent être surmontées en utilisant des techniques d’ eval magique et d’expansion secondaire , même si je ne suis pas sûr que cela en vaille la peine.

Utilisez le test fonction shell:

 foo: test $(something) 

Usage:

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

Utilisez la gestion des erreurs 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 

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