es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Asignando variables en una receta de Makefile.

En uno de los comandos de mi archivo Makefile, quiero crear un archivo temporal, pasar el nombre de ese archivo a un comando de shell y asignar la salida de ese comando a una variable de make para poder usarla posteriormente. Por más que lo intento, no logro que funcione.

Con el propósito de depurar, he intentado reducir el problema al objetivo más simple que se me ocurrió:

.PHONY: foo
foo:
    $(eval TMPFILE = $(shell mktemp -p ./))
    dd if=/dev/random of=${TMPFILE} bs=1 count=512
    $(eval FOO = $(shell wc -c ${TMPFILE}))
    @echo FOO: ${FOO}

Esto es lo que sucede:

❯ make foo
dd if=/dev/random of=./tmp.K1au4WrZ76 bs=1 count=512
512+0 registros de entrada
512+0 registros de salida
512 bytes copiados, 0,00287818 s, 178 kB/s
FOO: 0 ./tmp.K1au4WrZ76

Así que de alguna manera, wc cree que el archivo está vacío. Pero cuando reviso el TMPFILE, tiene 512 bytes, como se esperaba:

❯ wc -c tmp.K1au4WrZ76
512 tmp.K1au4WrZ76

¿Alguien puede iluminarme sobre lo que está sucediendo aquí y cómo hacerlo correctamente?

Gracias,
Phil

Actualización: Basándome en la respuesta, he creado este objetivo que funciona como se desea:

.PHONEY: foo
.ONESHELL:
foo:
    set -e
    TMPFILE=`mktemp -p ./`
    dd if=/dev/random of=$$TMPFILE bs=1 count=512
    FOO=`wc -c $$TMPFILE`
    @echo FOO: $$FOO

¡Gracias!

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Parece que el problema con el código original es el uso de la variable ${TMPFILE} del Makefile dentro del comando $(shell). Las variables del Makefile se expanden antes de ejecutar cualquier comando de shell, por lo que cuando se ejecuta el comando $(shell), ve una variable vacía (${TMPFILE}) en lugar del nombre real del archivo. Por lo tanto, wc informa 0 bytes para el archivo.

    Para resolver este problema, puedes usar la función $(eval) para asignar la salida de mktemp a una variable del Makefile, y luego pasar esa variable al comando de shell usando el carácter de escape $$. Aquí tienes una versión actualizada de la receta del Makefile:

    .PHONY: foo
    foo:
        $(eval TMPFILE := $(shell mktemp -p ./))
        dd if=/dev/random of=$(TMPFILE) bs=1 count=512
        $(eval FOO := $(shell wc -c $(TMPFILE)))
        @echo FOO: $(FOO)
    

    Ten en cuenta el uso de := en lugar de = para asignar la salida de $(shell) a $(TMPFILE) y $(FOO). Esto asegura que las variables se expandan solo una vez, en el momento de la asignación, y no sean reevaluadas cada vez que se utilizan.

    Alternativamente, puedes usar el objetivo especial .ONESHELL para indicarle a Make que ejecute todos los comandos en una única invocación de shell. De esta manera, puedes evitar el problema de expandir las variables del Makefile dentro de los comandos de shell. Aquí tienes una versión actualizada de la receta usando .ONESHELL:

    .PHONY: foo
    .ONESHELL:
    foo:
        set -e
        TMPFILE=$$(mktemp -p ./)
        dd if=/dev/random of=$$TMPFILE bs=1 count=512
        FOO=$$(wc -c $$TMPFILE)
        @echo FOO: $$FOO
    

    Ten en cuenta el uso de set -e para que el script de shell se detenga inmediatamente si algún comando falla. Además, ten en cuenta el uso de $$ en lugar de $ para escapar las variables en los comandos de shell, ya que Make ignora un único carácter $.

Comments are closed.