it-swarm.dev

Encontrar si existe un programa de Makefile

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

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

Se podría usar para verificar qué compilador está instalado, por ejemplo.

P.ej. algo como esta pregunta , pero sin asumir que el Shell subyacente es compatible con POSIX.

76
Prof. Falken

A veces, necesita un Makefile para poder ejecutarse en diferentes sistemas operativos de destino y desea que la compilación falle antes si el 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 target . Sin embargo, si tiene muchos ejecutables para probar y su Makefile tiene muchos objetivos independientes, cada uno de los cuales requiere las pruebas, entonces cada objetivo requiere el objetivo de la prueba como una dependencia. Eso hace que se escriba mucho más, así como el tiempo de procesamiento, cuando se hacen más de un objetivo a la vez.

La solución provista por 0xf puede probar un ejecutable sin hacer un objetivo. Eso ahorra mucho tiempo de escritura y ejecución cuando hay múltiples objetivos que pueden construirse por separado o en conjunto.

Mi mejora a la última solución es utilizar el ejecutable which (where en Windows), en lugar de confiar en que haya una opción --version en cada ejecutable, directamente en la GNU Hacer una directiva ifeq, en lugar de definir una nueva variable, y para usar la función GNU Make error para detener la construcción si el 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 la PATH, entonces en lugar de la última línea anterior necesitará:

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

Esto debería darle salida similar a:

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

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 a la perfección porque "command -v" no imprime nada si el ejecutable no está disponible, por lo que la variable DOT nunca se define y puedes verificarlo siempre que lo desees en tu 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.

37
mentatkgs

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

31
engineerchuan

Utilice 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 posible mensaje de "comando no encontrado", redirija el error estándar a /dev/null.

Luego puede verificar 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 beneficio adicional, la salida (como la versión del programa) puede ser útil en otras partes de su Makefile.

17
0xF

Mi solución involucra un pequeño script auxiliar1 que coloca un archivo de bandera si todos los comandos necesarios existen. Esto viene con la ventaja de que la comprobación de los comandos necesarios solo se realiza una vez y no en todas las invocaciones 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 Puede encontrar más información sobre la técnica command -vaquí .

8
Flow

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

La $(info ...) puede excluirse si desea que esto sea más silencioso.

Esto será fallará rápido. No se requiere objetivo.

7
mpen

Para mí, todas las respuestas anteriores se basan 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 el siguiente:

# 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 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
Vit Bernatik

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

Shell := /bin/bash
all: check

check:
        @type foo

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

3
kenorb

Supongamos que tiene diferentes objetivos y constructores, cada uno requiere otro conjunto de herramientas. Establezca una lista de dichas herramientas y considérelas como objetivo para forzar la verificación de su disponibilidad

Por ejemplo:

make_tools := gcc md5sum gzip

$(make_tools):  
    @which $@ > /dev/null

file.txt.gz: file.txt gzip
    gzip -c file.txt > file.txt.gz 
2
lkanab

Resuelto mediante la compilación de un pequeño programa especial en otro objetivo de makefile, cuyo único propósito es verificar cualquier cosa de tiempo de ejecución que estaba buscando.

Luego, llamé a este programa en otro objetivo de makefile.

Fue algo como esto si recuerdo correctamente:

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

checker: checker.c
    cc -o checker checker.c
1
Prof. Falken

Las soluciones que comprueban la salida de STDERR de --version no funcionan para los programas que imprimen su versión en STDOUT en lugar de STDERR. En lugar de verificar su salida a STDERR o STDOUT, verifique el código de retorno del programa. Si el programa no existe, su código de salida siempre será distinto de cero.

#!/usr/bin/make -f
# https://stackoverflow.com/questions/7123241/makefile-as-an-executable-script-with-Shebang
ECHOCMD:=/bin/echo -e
Shell := /bin/bash

RESULT := $(Shell python --version >/dev/null 2>&1 || (echo "Your command failed with $$?"))

ifeq (,${RESULT})
    EXISTS := true
else
    EXISTS := false
endif

all:
    echo EXISTS: ${EXISTS}
    echo RESULT: ${RESULT}
1
user