Vérifier si un programme existe depuis un Makefile

Comment puis-je vérifier si un programme peut être appelé depuis un Makefile?

(C’est-à-dire que le programme doit exister dans le chemin ou peut être appelé.)

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 avec POSIX.

Parfois, vous avez besoin d’un fichier Makefile pour pouvoir s’exécuter sur différents systèmes d’exploitation cibles et vous voulez que la construction échoue rapidement si un exécutable requirejs n’est pas dans PATH plutôt que d’être exécuté trop longtemps avant de tomber en panne.

L’excellente solution fournie par engineerchuan nécessite de créer une cible . Cependant, si de nombreux exécutables doivent être testés et que votre fichier makefile possède de nombreuses cibles indépendantes, chacune nécessitant des tests, chaque cible nécessite la cible de test en tant que dépendance. Cela fait beaucoup de temps de saisie et de traitement lorsque vous créez plusieurs cibles à la fois.

La solution fournie par 0xf peut tester un exécutable sans créer de cible. Cela économise beaucoup de temps de frappe et d’exécution lorsque plusieurs cibles peuvent être créées séparément ou ensemble.

Mon amélioration par rapport à la dernière solution est d’utiliser l’exécutable which (sous Windows), plutôt que de compter sur une option --version dans chaque exécutable, directement dans la directive GNU Make ifeq , plutôt que de définir une nouvelle variable. et d’utiliser la fonction d’ error GNU Make pour arrêter la génération si un exécutable requirejs n’est pas dans ${PATH} . Par exemple, pour tester l’exécutable lzop :

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

Si vous avez plusieurs exécutables à vérifier, vous pouvez utiliser une fonction foreach avec le fichier exécutable suivant:

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

Notez l’utilisation de l’opérateur d’affectation := requirejs pour forcer l’évaluation immédiate de l’expression RHS. Si votre Makefile change le PATH , alors vous aurez besoin au lieu de la dernière ligne ci-dessus:

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

Cela devrait vous donner un résultat similaire à:

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

J’ai mélangé les solutions de @kenorb et @ 0xF et 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 très bien parce que “command -v” n’imprime rien si l’exécutable n’est pas disponible, donc la variable DOT n’est jamais définie et vous pouvez simplement 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” exécute l’opération peu coûteuse d’impression du chemin de commande, définissant la variable DOT.

C’est ce que tu as fait?

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

crédit à mon collègue.

Utilisez la fonction shell pour appeler votre programme de manière à imprimer quelque chose sur la sortie standard. Par exemple, passer --version .

GNU Make ignore le statut de sortie de la commande transmise au shell . Pour éviter le message potentiel “commande introuvable”, redirigez l’erreur standard vers /dev/null .

Ensuite, vous pouvez 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 (telle que la version du programme) peut être utile dans d’autres parties de votre Makefile.

Ma solution implique un petit script d’aide 1 qui place un fichier d’indicateur si toutes les commandes requirejses existent. Cela présente l’avantage que la vérification des commandes requirejses n’est effectuée qu’une seule fois et pas à chaque appel de make .

check_cmds.sh

 #!/bin/bash NEEDED_COMMANDS="jlex byaccj ant javac" for cmd in ${NEEDED_COMMANDS} ; do if ! command -v ${cmd} &> /dev/null ; then echo Please install ${cmd}! exit 1 fi done touch .cmd_ok 

Makefile

 .cmd_ok: ./check_cmds.sh build: .cmd_ok target1 target2 

1 Vous trouverez plus d’informations sur la command -v technique ici .

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

Le $(info ...) vous pouvez exclure si vous voulez que cela soit plus silencieux.

Cela va échouer rapidement . Aucune cible requirejse.

Pour moi, toutes les réponses ci-dessus sont basées sur Linux et ne fonctionnent pas avec Windows. Je suis nouveau pour que mon approche ne soit pas idéale. Mais l’exemple complet qui fonctionne pour moi sur Linux et Windows est le suivant:

 # detect what shell is used ifeq ($(findssortingng cmd.exe,$(SHELL)),cmd.exe) $(info "shell Windows cmd.exe") DEVNUL := NUL WHICH := where else $(info "shell Bash") DEVNUL := /dev/null WHICH := which endif # detect platform independently if gcc is installed ifeq ($(shell ${WHICH} gcc 2>${DEVNUL}),) $(error "gcc is not in your system PATH") else $(info "gcc found") endif 

facultativement quand j’ai besoin de détecter plus d’outils que je peux utiliser:

 EXECUTABLES = ls dd K := $(foreach myTestCommand,$(EXECUTABLES),\ $(if $(shell ${WHICH} $(myTestCommand) 2>${DEVNUL} ),\ $(myTestCommand) found,\ $(error "No $(myTestCommand) in PATH))) $(info ${K}) 

Résolu en compilant un petit programme spécial dans une autre cible du makefile, dont le seul but est de vérifier les éléments d’exécution que je cherchais.

Ensuite, j’ai appelé ce programme dans une autre cible de makefile.

C’était quelque chose comme ça si je me souviens bien:

 real: checker real.c cc -o real real.c `./checker` checker: checker.c cc -o checker checker.c 

Vous pouvez utiliser les commandes bash telles que type foo ou command -v foo , comme ci-dessous:

 SHELL := /bin/bash all: check check: @type foo 

foo est votre programme / commande. Redirection vers > /dev/null si vous le souhaitez.