web-dev-qa-db-fra.com

Comment assigner la sortie d'une commande à une variable Makefile

J'ai besoin d'exécuter certaines règles de création de manière conditionnelle, uniquement si le Python installé est supérieur à une certaine version (disons 2.5).

Je pensais pouvoir faire quelque chose comme exécuter:

python -c 'import sys; print int(sys.version_info >= (2,5))'

puis en utilisant la sortie ('1' si ok, '0' sinon) dans une instruction ifeq make.

Dans un script bash simple, il suffit de:

MY_VAR=`python -c 'import sys; print int(sys.version_info >= (2,5))'`

mais ça ne marche pas dans un Makefile.

Aucune suggestion? Je pourrais utiliser toute autre solution de contournement raisonnable pour y parvenir.

186
fortran

Utilisez la commande Construire Shell comme dans MY_VAR=$(Shell echo whatever)

me@Zack:~$make
MY_VAR IS whatever
me@Zack:~$ cat Makefile 
MY_VAR := $(Shell echo whatever)

all:
    @echo MY_VAR IS $(MY_VAR)
295
Arkaitz Jimenez

Envelopper la tâche dans un eval fonctionne pour moi.

# dependency on .PHONY prevents Make from 
# thinking there's `nothing to be done`
set_opts: .PHONY
  $(eval DOCKER_OPTS = -v $(Shell mktemp -d -p /scratch):/output)
24
Dave

Voici un exemple un peu plus compliqué avec la tuyauterie et l'affectation de variables dans la recette:

getpodname:
    # Getting pod name
    @eval $$(minikube docker-env) ;\
    $(eval PODNAME=$(Shell sh -c "kubectl get pods | grep profile-posts-api | grep Running" | awk '{print $$1}'))
    echo $(PODNAME)
13
Francesco Casula

J'écris une réponse pour augmenter la visibilité de la syntaxe réelle qui résout le problème. Malheureusement, ce que quelqu'un pourrait considérer comme trivial peut devenir un mal de tête très important pour quelqu'un qui cherche une réponse simple à une question raisonnable.

Mettez ce qui suit dans le fichier "Makefile".

MY_VAR := $(Shell python -c 'import sys; print int(sys.version_info >= (2,5))')

all:
    @echo MY_VAR IS $(MY_VAR)

Le comportement que vous souhaitez voir est le suivant (en supposant que vous avez installé python récent).

make
MY_VAR IS 1

Si vous copiez et collez le texte ci-dessus dans le Makefile, l'obtiendrez-vous? Probablement pas. Vous obtiendrez probablement une erreur comme celle rapportée ici:

makefile: 4: *** séparateur manquant. Stop

Pourquoi: Parce que même si j'ai personnellement utilisé un véritable onglet, Stack Overflow (tentative d'aide) convertit mon onglet en un certain nombre d'espaces. Vous, citoyen Internet frustré, copiez maintenant ceci, en pensant que vous avez maintenant le même texte que celui que j’ai utilisé. La commande make lit maintenant les espaces et trouve que la commande "tout" est mal formatée. Donc, copiez le texte ci-dessus, collez-le, puis convertissez les espaces avant "@echo" en onglet, et cet exemple devrait enfin fonctionner, espérons-le, pour vous.

13
AlanSE

Avec GNU Make, vous pouvez utiliser Shell et eval pour stocker, exécuter et affecter la sortie à partir d'appels de ligne de commande arbitraires. La différence entre l'exemple ci-dessous et ceux qui utilisent := est l'attribution := se produit une fois (quand il se produit) et pour tous. Les variables développées récursivement et définies avec = sont un peu plus "paresseuses"; les références aux autres variables demeurent jusqu'à ce que la variable elle-même soit référencée et le développement récursif suivant a lieu chaque fois que la variable est référencée , ce qui est souhaitable pour créer " cohérents, appelables, extraits ". Voir le manuel de paramétrage des variables pour plus d'informations.

# Generate a random number.
# This is not run initially.
GENERATE_ID = $(Shell od -vAn -N2 -tu2 < /dev/urandom)

# Generate a random number, and assign it to MY_ID
# This is not run initially.
SET_ID = $(eval MY_ID=$(GENERATE_ID))

# You can use .PHONY to tell make that we aren't building a target output file
.PHONY: mytarget
mytarget:
# This is empty when we begin
    @echo $(MY_ID)
# This recursively expands SET_ID, which calls the Shell command and sets MY_ID
    $(SET_ID)
# This will now be a random number
    @echo $(MY_ID)
# Recursively expand SET_ID again, which calls the Shell command (again) and sets MY_ID (again)
    $(SET_ID)
# This will now be a different random number
    @echo $(MY_ID)
4
Mitchell Tracy

Méfiez-vous des recettes comme celle-ci

target:
    MY_ID=$(GENERATE_ID);
    echo $MY_ID;

Cela fait deux choses qui ne vont pas. La première ligne de la recette est exécutée dans une instance de shell distincte de la deuxième ligne. La variable est perdue entre-temps. La deuxième chose qui ne va pas, c'est que le $ ne soit pas échappé.

target:
    MY_ID=$(GENERATE_ID); \
    echo $$MY_ID;

Les deux problèmes ont été corrigés et la variable est utilisable. La barre oblique inverse combine les deux lignes pour s'exécuter dans un seul shell. Par conséquent, le réglage de la variable et la lecture de la suite de la variable fonctionnent.

Je me rends compte que le message original expliquait comment obtenir les résultats d'une commande Shell dans une variable MAKE, et cette réponse montre comment l'obtenir dans une variable Shell. Mais d'autres lecteurs pourraient en bénéficier.

Une dernière amélioration, si le consommateur s'attend à ce qu'une "variable d'environnement" soit définie, vous devez l'exporter.

my_Shell_script
    echo $MY_ID

aurait besoin de cela dans le makefile

target:
    export MY_ID=$(GENERATE_ID); \
    ./my_Shell_script;

J'espère que ça aide quelqu'un. En général, il faut éviter de faire du vrai travail en dehors des recettes, car si quelqu'un utilise le makefile avec l'option '--dry-run', pour voir seulement ce qu'il va faire, il n'aura aucun effet secondaire indésirable. Chaque appel $(Shell) est évalué au moment de la compilation et un travail réel pourrait être effectué accidentellement. Mieux vaut laisser le vrai travail, comme générer des identifiants, à l'intérieur des recettes lorsque cela est possible.

3
Juraj