Dans un Makefile, une recette deploy
nécessite de définir une variable d'environnement ENV
pour s'exécuter correctement, alors que d'autres ne s'en soucient pas, par exemple:
ENV =
.PHONY: deploy hello
deploy:
rsync . $(ENV).example.com:/var/www/myapp/
hello:
echo "I don't care about ENV, just saying hello!"
Comment puis-je m'assurer que cette variable est définie, par exemple: existe-t-il un moyen de déclarer cette variable de makefile comme condition préalable à la recette de déploiement, comme par exemple:
deploy: make-sure-ENV-variable-is-set
?
Merci.
Cela provoquera une erreur fatale si ENV
n'est pas défini et si quelque chose en a besoin (de toute façon dans GNUMake).
. PHONY: déploie check-env Deploy: vérifie-env ... Autre-chose-que -needs-env: check-env ... check-env: ifndef ENV $ (erreur ENV est indéfinie) fin si
(Notez que ifndef et endif ne sont pas indentés - ils contrôlent ce que make "voit" , prenant effet avant l’exécution du Makefile. "$ (Erreur" est indenté avec un onglet de sorte qu’il ne soit exécuté que dans le dossier contexte de la règle.)
Vous pouvez créer une cible de garde implicite, qui vérifie que la variable dans la tige est définie, comme ceci:
guard-%:
@ if [ "${${*}}" = "" ]; then \
echo "Environment variable $* not set"; \
exit 1; \
fi
Vous ajoutez ensuite une cible guard-ENVVAR où vous voulez affirmer qu'une variable est définie, comme ceci:
change-hostname: guard-HOSTNAME
./changeHostname.sh ${HOSTNAME}
Si vous appelez 'make change-hostname', sans ajouter HOSTNAME = nom_hôte à l'appel, vous obtiendrez une erreur et la construction échouera.
variante en ligne
Dans mes makefiles, j'utilise normalement une expression comme:
deploy:
test -n "$(ENV)" # $$ENV
rsync . $(ENV).example.com:/var/www/myapp/
Les raisons:
N'oubliez pas le commentaire important pour le débogage:
test -n ""
Makefile:3: recipe for target 'deploy' failed
make: *** [deploy] Error 1
... vous oblige à rechercher le Makefile pendant que ...
test -n "" # $ENV
Makefile:3: recipe for target 'deploy' failed
make: *** [deploy] Error 1
... explique directement ce qui ne va pas
variante globale (à compléter, mais non demandé)
En plus de votre Makefile, vous pouvez aussi écrire:
ifeq ($(ENV),)
$(error ENV is not set)
endif
Avertissements:
clean
échouera si ENV n'est pas défini. Sinon, voir la réponse de Hudon qui est plus complexeJ'ai trouvé que la meilleure réponse ne pouvait pas être utilisée, sauf pour les autres cibles PHONY. Si utilisé comme dépendance pour une cible qui est un fichier réel, utilisez check-env
forcera la cible de ce fichier à être reconstruite.
Les autres réponses sont globales (par exemple, la variable est requise pour toutes cibles dans le Makefile) ou utilisent le shell, par exemple. si ENV manquait, make se terminerait quelle que soit la cible.
Une solution que j'ai trouvée aux deux problèmes est
ndef = $(if $(value $(1)),,$(error $(1) not set))
.PHONY: deploy
deploy:
$(call ndef,ENV)
echo "deploying $(ENV)"
.PHONY: build
build:
echo "building"
La sortie ressemble à
$ make build
echo "building"
building
$ make deploy
Makefile:5: *** ENV not set. Stop.
$ make deploy ENV="env"
echo "deploying env"
deploying env
$
value
présente des mises en garde effrayantes, mais pour cette utilisation simple, je pense que c'est le meilleur choix.
Comme je le vois, la commande elle-même a besoin de la variable ENV afin que vous puissiez la vérifier dans la commande elle-même:
.PHONY: deploy check-env
deploy: check-env
rsync . $(ENV).example.com:/var/www/myapp/
check-env:
if test "$(ENV)" = "" ; then \
echo "ENV not set"; \
exit 1; \
fi
Un problème possible avec les réponses données jusqu'à présent est que l'ordre de dépendance dans make n'est pas défini. Par exemple, en cours d'exécution:
make -j target
quand target
a quelques dépendances, cela ne garantit pas qu'elles fonctionneront dans un ordre donné.
La solution pour cela (pour garantir que ENV sera vérifiée avant que les recettes ne soient choisies) est de vérifier ENV lors du premier passage de make, en dehors de toute recette:
## Are any of the user's goals dependent on ENV?
ifneq ($(filter deploy other-thing-that-needs-ENV,$(MAKECMDGOALS)),$())
ifndef ENV
$(error ENV not defined)
endif
endif
.PHONY: deploy
deploy: foo bar
...
other-thing-that-needs-ENV: bar baz bono
...
Vous pouvez en savoir plus sur les différentes fonctions/variables utilisées ici et $()
n'est qu'un moyen d'indiquer explicitement que nous comparons à "rien".
Vous pouvez utiliser ifdef
au lieu d'une autre cible.
.PHONY: deploy
deploy:
ifdef ENV
rsync . $(ENV).example.com:/var/www/myapp/
else
@echo 1>&2 "ENV must be set"
false # Cause deploy to fail
endif
Je sais que c'est vieux, mais j'ai pensé raconter ma propre expérience pour les futurs visiteurs, car c'est un peu plus simple IMHO.
Typiquement, make
utilisera sh
comme shell par défaut ( défini via la variable spéciale Shell
=) ). Dans sh
et ses dérivés, il est trivial de quitter avec un message d'erreur lors de l'extraction d'une variable d'environnement si elle n'est pas définie ou nulle en faisant: ${VAR?Variable VAR was not set or null}
.
En prolongeant cela, nous pouvons écrire une cible make réutilisable qui peut être utilisée pour faire échouer d'autres cibles si aucune variable d'environnement n'a été définie:
.check-env-vars:
@test $${ENV?Please set environment variable ENV}
deploy: .check-env-vars
rsync . $(ENV).example.com:/var/www/myapp/
hello:
echo "I don't care about ENV, just saying hello!"
Choses à noter:
$$
) Est requis pour différer l’extension du shell au lieu de make
test
ne sert qu'à empêcher le shell d'essayer d'exécuter le contenu de VAR
(il ne sert à rien d'autre).check-env-vars
Peut être étendu de manière triviale pour rechercher davantage de variables d’environnement, chacune d’elles n’ajoutant qu’une seule ligne (par exemple, @test $${NEWENV?Please set environment variable NEWENV}
)