web-dev-qa-db-fra.com

Variable Makefile en tant que condition préalable

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.

123
abernier

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

151
Beta

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.

88
Clayton Stanley

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:

  • c'est une simple ligne
  • c'est compact
  • il est situé près des commandes qui utilisent la variable

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:

  • ne pas utiliser la tabulation dans ce bloc
  • utiliser avec précaution: même la cible clean échouera si ENV n'est pas défini. Sinon, voir la réponse de Hudon qui est plus complexe
40
Daniel Alder

J'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.

5
23jodys

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
5
ssmir

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".

4
Hudon

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
2
Daniel Gallagher

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:

  • Le signe dollar échappé ($$) Est requis pour différer l’extension du shell au lieu de make
  • L'utilisation de 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})
0
Lewis Belcher