it-swarm.dev

Cómo asignar la salida de un comando a una variable de Makefile

Necesito ejecutar algunas reglas de creación condicionalmente, solo si el Python instalado es mayor que una versión determinada (por ejemplo, 2.5).

Pensé que podía hacer algo como ejecutar:

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

y luego usar la salida ('1' si está bien, '0' de lo contrario) en una ifeq make.

En un simple script de bash shell es solo:

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

pero eso no funciona en un Makefile.

¿Alguna sugerencia? Podría usar cualquier otra solución sensata para lograr esto.

177
fortran

Use el make Shell builtin como en 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)
284
Arkaitz Jimenez

Envolver la tarea en una eval está funcionando para mí.

# 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

Aquí hay un ejemplo un poco más complicado con la asignación de tuberías y variables dentro de la receta:

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)
12
Francesco Casula

Estoy escribiendo una respuesta para aumentar la visibilidad de la sintaxis real que resuelve el problema. Desafortunadamente, lo que alguien podría ver como trivial puede convertirse en un dolor de cabeza muy importante para alguien que busca una respuesta simple a una pregunta razonable.

Ponga lo siguiente en el archivo "Makefile".

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

all:
    @echo MY_VAR IS $(MY_VAR)

El comportamiento que le gustaría ver es el siguiente (asumiendo que tiene un Python instalado recientemente).

make
MY_VAR IS 1

Si copia y pega el texto anterior en el Makefile, ¿obtendrá esto? Probablemente no. Probablemente obtendrá un error como el que se informa aquí:

makefile: 4: *** falta el separador. Detener

Por qué: porque aunque personalmente utilicé una pestaña genuina, Stack Overflow (intentando ser útil) convierte mi pestaña en varios espacios. Usted, frustrado ciudadano de Internet, ahora copie esto, pensando que ahora tiene el mismo texto que usé. El comando make, ahora lee los espacios y encuentra que el comando "all" tiene un formato incorrecto. Así que copie el texto anterior, péguelo y luego convierta el espacio en blanco antes de "@echo" en una pestaña, y este ejemplo debería, finalmente, trabajar para usted.

10
AlanSE

Con GNU Make, puede usar Shell y eval para almacenar, ejecutar y asignar resultados de invocaciones de línea de comando arbitrarias. La diferencia entre el siguiente ejemplo y los que usan := es que la asignación := ocurre una vez (cuando se encuentra) y para todos. Las variables expandidas recursivamente establecidas con = son un poco más "perezosas"; las referencias a otras variables permanecen hasta que se hace referencia a la variable en sí misma, y ​​ la expansión recursiva subsiguiente tiene lugar cada vez que se hace referencia a la variable , lo cual es deseable para hacer "fragmentos de código coherentes, llamables". Consulte el manual de configuración de variables para obtener más información.

# 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)
3
Mitchell Tracy

Cuidado con recetas como esta

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

Hace dos cosas mal. La primera línea de la receta se ejecuta en una instancia de Shell separada de la segunda línea. La variable se pierde mientras tanto. La segunda cosa incorrecta es que el $ no se escapa.

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

Ambos problemas se han solucionado y la variable es utilizable. La barra diagonal inversa combina ambas líneas para ejecutarse en un solo Shell, de ahí que funcione la configuración de la variable y la lectura de las variables posteriores.

Me doy cuenta de que la publicación original decía cómo obtener los resultados de un comando de Shell en una variable MAKE, y esta respuesta muestra cómo convertirlo en una variable de Shell. Pero otros lectores pueden beneficiarse.

Una mejora final, si el consumidor espera que se establezca una "variable de entorno", entonces tiene que exportarla.

my_Shell_script
    echo $MY_ID

necesitaría esto en el makefile

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

Espero que ayude a alguien. En general, se debe evitar realizar un trabajo real fuera de las recetas, ya que si alguien usa el archivo MAKE con la opción '--dry-run', solo para VER LO que hará, no tendrá ningún efecto secundario indeseable. Cada llamada $(Shell) se evalúa en el momento de la compilación y se podría realizar accidentalmente un trabajo real. Es mejor dejar el trabajo real, como generar identificaciones, al interior de las recetas cuando sea posible.

0
Juraj