148 votes

Vérifier si un programme existe à partir d'un Makefile

Comment puis-je vérifier si un programme est appelable depuis un Makefile ?

(C'est-à-dire que le programme doit exister dans le chemin ou être appelable d'une autre manière).

Il pourrait être utilisé pour vérifier quel compilateur est installé, par exemple.

Par exemple, quelque chose comme cette question mais sans supposer que le shell sous-jacent est compatible POSIX.

117voto

Jonathan Ben-Avraham Points 1065

Parfois, il est nécessaire qu'un Makefile puisse fonctionner sur différents systèmes d'exploitation cibles et vous voulez que la construction échoue rapidement si un exécutable requis n'est pas dans le fichier PATH plutôt que de fonctionner pendant un temps éventuellement long avant de tomber en panne.

L'excellente solution fournie par ingénieurchuan nécessite de faire un cible . Cependant, si vous avez beaucoup d'exécutables à tester et que votre Makefile a beaucoup de cibles indépendantes, chacune d'entre elles nécessitant les tests, alors chaque cible nécessite la cible de test comme dépendance. Cela entraîne beaucoup de saisie supplémentaire ainsi que du temps de traitement lorsque vous créez plus d'une cible à la fois.

La solution fournie par 0xf peut tester un exécutable sans faire de cible. Cela permet d'économiser beaucoup de temps de saisie et d'exécution lorsqu'il y a plusieurs cibles qui peuvent être construites séparément ou ensemble.

L'amélioration que j'ai apportée à cette dernière solution consiste à utiliser l'option which exécutable ( where dans Windows), plutôt que de compter sur l'existence d'une --version dans chaque exécutable, directement dans le programme GNU Make ifeq plutôt que de définir une nouvelle variable, et d'utiliser la directive GNU Make error pour arrêter la construction si l'exécutable requis ne se trouve pas dans le répertoire de l'utilisateur. ${PATH} . Par exemple, pour tester le lzop exécutable :

 ifeq (, $(shell which lzop))
 $(error "No lzop in $(PATH), consider doing apt-get install lzop")
 endif

Si vous avez plusieurs exécutables à vérifier, il est préférable d'utiliser un fichier foreach avec la fonction which exécutable :

EXECUTABLES = ls dd dudu lxop
K := $(foreach exec,$(EXECUTABLES),\
        $(if $(shell which $(exec)),some string,$(error "No $(exec) in PATH")))

Notez l'utilisation du := qui est nécessaire afin de forcer l'évaluation immédiate de l'expression RHS. Si votre Makefile change l'opérateur PATH alors à la place de la dernière ligne ci-dessus, vous aurez besoin de :

        $(if $(shell PATH=$(PATH) which $(exec)),some string,$(error "No $(exec) in PATH")))

Cela devrait vous donner un résultat similaire à :

ads$ make
Makefile:5: *** "No dudu in PATH.  Stop.

75voto

mentatkgs Points 122

J'ai mélangé les solutions de @kenorb et @0xF et j'ai obtenu ceci :

DOT := $(shell command -v dot 2> /dev/null)

all:
ifndef DOT
    $(error "dot is not available please install graphviz")
endif
    dot -Tpdf -o pres.pdf pres.dot 

Cela fonctionne parfaitement parce que "command -v" n'affiche rien si l'exécutable n'est pas disponible, donc la variable DOT n'est jamais définie et vous pouvez la vérifier quand vous voulez dans votre code. Dans cet exemple, je lance une erreur, mais vous pouvez faire quelque chose de plus utile si vous le souhaitez.

Si la variable est disponible, "command -v" effectue l'opération peu coûteuse d'impression du chemin de la commande, en définissant la variable DOT.

38voto

engineerchuan Points 439

C'est ce que vous avez fait ?

check: PYTHON-exists
PYTHON-exists: ; @which python > /dev/null
mytarget: check
.PHONY: check PYTHON-exists

le crédit à mon collègue.

23voto

0xF Points 149

Utilisez le shell pour appeler votre programme de manière à ce qu'il imprime quelque chose sur la sortie standard. Par exemple, passez --version .

GNU Make ignore l'état de sortie de la commande passée à shell . Pour éviter le message potentiel "commande non trouvée", redirigez l'erreur standard vers /dev/null .

Vous pouvez ensuite vérifier le résultat en utilisant ifdef , ifndef , $(if) etc.

YOUR_PROGRAM_VERSION := $(shell your_program --version 2>/dev/null)

all:
ifdef YOUR_PROGRAM_VERSION
    @echo "Found version $(YOUR_PROGRAM_VERSION)"
else
    @echo Not found
endif

En bonus, la sortie (comme la version du programme) peut être utile dans d'autres parties de votre Makefile.

15voto

Mark Points 49079

J'ai nettoyé certaines des solutions existantes ici...

REQUIRED_BINS := composer npm node php npm-shrinkwrap
$(foreach bin,$(REQUIRED_BINS),\
    $(if $(shell command -v $(bin) 2> /dev/null),$(info Found `$(bin)`),$(error Please install `$(bin)`)))

El $(info ...) que vous pouvez exclure si vous voulez que ce soit plus silencieux.

Cela permettra faire des erreurs rapides . Aucune cible n'est requise.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X