Comprobar si existe un programa desde un Makefile


¿Cómo puedo comprobar si se puede llamar a un programa desde un Makefile?

(Es decir, el programa debe existir en la ruta de acceso o ser invocable.)

Podría usarse para comprobar para qué compilador está instalado, por ejemplo.

Por ejemplo, algo como esta pregunta, pero sin asumir que el shell subyacente es compatible con POSIX.

Author: Community, 2011-04-11

9 answers

A veces necesita un Makefile para poder ejecutarse en diferentes sistemas operativos de destino y desea que la compilación falle antes si un ejecutable requerido no está en PATH en lugar de ejecutarse durante un tiempo posiblemente largo antes de fallar.

La excelente solución proporcionada por engineerchuan requiere hacer un objetivo . Sin embargo, si tiene muchos ejecutables para probar y su makefile tiene muchos destinos independientes, cada uno de los cuales requiere las pruebas, entonces cada destino requiere la prueba objetivo como dependencia. Eso hace que para una gran cantidad de escritura adicional, así como el tiempo de procesamiento cuando se hace más de un objetivo a la vez.

La solución proporcionada por 0xf puede probar un ejecutable sin hacer un destino. Eso ahorra mucho tiempo de escritura y ejecución cuando hay varios objetivos que se pueden construir por separado o juntos.

Mi mejora a esta última solución es usar el ejecutable which (where en Windows), en lugar de confiar en él siendo una opción --version en cada ejecutable, directamente en la directiva GNU Make ifeq, en lugar de definir una nueva variable, y usar la función GNU Make error para detener la compilación si un ejecutable requerido no está en ${PATH}. Por ejemplo, para probar el ejecutable lzop:

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

Si tiene varios ejecutables para verificar, entonces es posible que desee usar una función foreach con el ejecutable which:

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

Tenga en cuenta el uso del operador de asignación := que se requiere para forzar la evaluación inmediata de la expresión RHS. Si su Makefile cambia el PATH, entonces en lugar de la última línea de arriba necesitará:

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

Esto debería darte una salida similar a:

ads$ make
Makefile:5: *** "No dudu in PATH.  Stop.
 34
Author: Jonathan Ben-Avraham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:29

Mezclé las soluciones de @ kenorb y @0xF y obtuve esto:

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 

Funciona muy bien porque "command-v" no imprime nada si el ejecutable no está disponible, por lo que la variable DOT nunca se define y puede verificarla cuando lo desee en su código. En este ejemplo estoy lanzando un error, pero podrías hacer algo más útil si quisieras.

Si la variable está disponible, "command-v" realiza la operación económica de imprimir la ruta de comando, definiendo la variable DOT.

 34
Author: mentatkgs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-03 14:49:18

¿Es esto lo que hiciste?

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

Crédito a mi compañero de trabajo.

 30
Author: engineerchuan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-30 09:48:13

Use la función shell para llamar a su programa de manera que imprima algo en la salida estándar. Por ejemplo, pase --version.

GNU Make ignora el estado de salida del comando pasado a shell. Para evitar el potencial mensaje "comando no encontrado", redirija el error estándar a /dev/null.

Entonces usted puede comprobar el resultado usando 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

Como bono, la salida (como la versión del programa) podría ser útil en otras partes de su Makefile.

 16
Author: 0xF,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-08 14:04:38

Mi solución implica un pequeño script de ayuda1 que coloca un archivo de bandera si existen todos los comandos necesarios. Esto viene con la ventaja de que la comprobación de los comandos necesarios solo se realiza una vez y no en cada invocación 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 Más sobre la técnica command -v se puede encontrar aquí .

 8
Author: Flow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:02

Limpió algunas de las soluciones existentes aquí...

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 ...) puede excluir si desea que esto sea más silencioso.

Esto fallará rápidamente. No se requiere objetivo.

 6
Author: mpen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-12 21:41:29

Para mí todas las respuestas anteriores están basadas en linux y no funcionan con Windows. Soy nuevo para hacer así que mi enfoque puede no ser ideal. Pero el ejemplo completo que funciona para mí tanto en linux como en Windows es este:

# detect what shell is used
ifeq ($(findstring 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

Opcionalmente cuando necesito detectar más herramientas que puedo usar:

EXECUTABLES = ls dd 
K := $(foreach myTestCommand,$(EXECUTABLES),\
        $(if $(shell ${WHICH} $(myTestCommand) 2>${DEVNUL} ),\
            $(myTestCommand) found,\
            $(error "No $(myTestCommand) in PATH)))
$(info ${K})        
 4
Author: Vit Bernatik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-22 20:31:14

Resuelto compilando un pequeño programa especial en otro destino makefile, cuyo único propósito es comprobar cualquier cosa en tiempo de ejecución que estaba buscando.

Entonces, llamé a este programa en otro destino makefile.

Era algo como esto si recuerdo correctamente:

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

checker: checker.c
    cc -o checker checker.c
 2
Author: Prof. Falken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-04 11:24:45

Puede usar comandos construidos bash como type foo o command -v foo, como se muestra a continuación:

SHELL := /bin/bash
all: check

check:
        @type foo

Donde foo es su programa/comando. Redirige a > /dev/null si lo quieres en silencio.

 2
Author: kenorb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-24 16:02:50