La receta (recipe, el contenido que ejecuta una regla) de una regla consta de una o más líneas de órdenes de shell que se ejecutan, de una en una, en el orden en que aparecen. Normalmente, el resultado de ejecutar estas órdenes es que el objetivo de la regla queda actualizado.
Los usuarios emplean muchos programas de shell distintos, pero las recetas de los makefiles siempre se interpretan mediante /bin/sh, a menos que el makefile indique otra cosa. Consulte Ejecución de recetas.
Los makefiles tienen una propiedad poco habitual: en un mismo archivo conviven en realidad dos sintaxis distintas. La mayor parte del makefile usa la sintaxis de make (consulte Cómo escribir makefiles). Sin embargo, las recetas están pensadas para que las interprete el shell, así que se escriben con la sintaxis del shell. El programa make no intenta entender la sintaxis del shell: solo realiza unas pocas transformaciones muy concretas sobre el contenido de la receta antes de entregárselo al shell.
Cada línea de la receta debe comenzar con un tabulador (o con el carácter indicado en la variable .RECIPEPREFIX; consulte Otras variables especiales), salvo que la primera línea de la receta puede ir unida a la línea de objetivo-y-prerrequisitos (prerequisite) mediante un punto y coma. Cualquier línea del makefile que empiece con un tabulador y aparezca en un «contexto de regla» (es decir, después de que haya comenzado una regla y hasta que aparezca otra regla o una definición de variable) se considera parte de la receta de esa regla. Entre las líneas de receta pueden aparecer líneas en blanco o líneas que solo contienen comentarios; estas se ignoran.
Algunas consecuencias de estas reglas son:
make; se pasa al shell tal cual. Que el shell lo trate como comentario o no depende de su shell.
make, y se pasa al shell.
ifdef, ifeq, etc.; consulte Sintaxis de los condicionales) en un «contexto de regla» que esté indentada con un tabulador como primer carácter de la línea se considera parte de una receta y se pasa al shell.
Uno de los pocos casos en los que make sí interpreta las recetas es al comprobar si hay una barra invertida justo antes del salto de línea. Como en la sintaxis normal de un makefile, una única línea lógica de receta puede dividirse en varias líneas físicas dentro del makefile colocando una barra invertida antes de cada salto de línea. Una secuencia de líneas así se considera una sola línea de receta, y se invoca una única instancia del shell para ejecutarla.
Sin embargo, a diferencia de cómo se tratan en otros lugares de un makefile (consulte Dividir líneas largas), los pares barra invertida/salto de línea no se eliminan de la receta. Tanto la barra invertida como el salto de línea se conservan y se pasan al shell. Cómo se interprete la barra invertida/salto de línea depende de su shell. Si el primer carácter de la línea siguiente al par barra invertida/salto de línea es el carácter de prefijo de receta (un tabulador de manera predeterminada; consulte Otras variables especiales), entonces ese carácter (y solo ese carácter) se elimina. Nunca se añade espacio en blanco a la receta.
Por ejemplo, la receta del objetivo all en este makefile:
all :
@echo no\
space
@echo no\
space
@echo one \
space
@echo one\
space
consta de cuatro órdenes de shell distintos cuya salida es:
nospace nospace one space one space
Como ejemplo algo más complejo, observe este makefile:
all : ; @echo 'hello \
world' ; echo "hello \
world"
invocará un único shell con la orden:
echo 'hello \
world' ; echo "hello \
world"
que, según las reglas de entrecomillado del shell, producirá la siguiente salida:
hello \ world hello world
Observe cómo el par barra invertida/salto de línea se eliminó dentro de la cadena entrecomillada con comillas dobles ("…"), pero no dentro de la cadena entrecomillada con comillas simples ('…'). Así es como el shell predeterminado (/bin/sh) trata los pares barra invertida/salto de línea. Si especifica un shell distinto en sus makefiles, podría tratarlos de otra manera.
A veces quiere dividir una línea larga dentro de comillas simples, pero no quiere que el par barra invertida/salto de línea aparezca en el contenido entrecomillado. Esto ocurre a menudo al pasar guiones (scripts) a lenguajes como Perl, donde una barra invertida sobrante dentro del guion puede cambiar su significado o incluso provocar un error de sintaxis. Una forma sencilla de gestionar esto es colocar la cadena entrecomillada, o incluso la orden entera, en una variable de make y luego usar la variable en la receta. En este caso se aplican las reglas de entrecomillado de saltos de línea de los makefiles, y la barra invertida/salto de línea se elimina. Si reescribimos el ejemplo anterior con este método:
HELLO = 'hello \ world' all : ; @echo $(HELLO)
obtendremos una salida como esta:
hello world
Si lo prefiere, también puede usar variables específicas de objetivo (consulte Valores de variable específicos de objetivo) para obtener una correspondencia más estrecha entre la variable y la receta que la usa.
La otra forma en que make procesa las recetas es expandiendo cualquier referencia a variable que contengan (consulte Conceptos básicos de las referencias a variables). Esto ocurre después de que make haya terminado de leer todos los makefiles y de determinar que el objetivo está desactualizado; por tanto, las recetas de los objetivos que no se reconstruyen nunca se expanden.
Las referencias a variables y a funciones en las recetas tienen exactamente la misma sintaxis y semántica que las referencias en cualquier otra parte del makefile. También tienen las mismas reglas de entrecomillado: si quiere que aparezca un signo de dólar en su receta, debe duplicarlo («$$»). En shells como el predeterminado, que usan el signo de dólar para introducir variables, es importante tener claro en su mente si la variable que quiere referenciar es una variable de make (use un solo signo de dólar) o una variable del shell (use dos signos de dólar). Por ejemplo:
LIST = one two three
all:
for i in $(LIST); do \
echo $$i; \
done
hace que se pase la siguiente orden al shell:
for i in one two three; do \
echo $i; \
done
lo que genera el resultado esperado:
one two three
Normalmente, make imprime cada línea de la receta antes de ejecutarla. A esto lo llamamos eco, porque da la impresión de que es usted mismo quien teclea las líneas.
Cuando una línea comienza con «@», se suprime el eco de esa línea. El «@» se descarta antes de pasar la línea al shell. Esto se usa normalmente para una orden cuyo único efecto es imprimir algo, como una orden echo que informe del progreso a través del makefile:
@echo About to make distribution files
Cuando se da a make la opción «-n» o «--just-print», solo hace eco de la mayoría de las recetas, sin ejecutarlas (consulte Resumen de las opciones). En ese caso se imprimen incluso las líneas de receta que comienzan con «@». Esta opción es útil para averiguar qué recetas considera necesarias make sin llegar a ejecutarlas.
La opción «-s» o «--silent» de make impide todo eco, como si todas las recetas comenzaran con «@». Una regla en el makefile para el objetivo especial .SILENT sin prerrequisitos tiene el mismo efecto (consulte Nombres de objetivos integrados especiales).
Cuando llega el momento de ejecutar las recetas para actualizar un objetivo, estas se ejecutan invocando un nuevo subshell por cada línea de la receta, salvo que esté en vigor el objetivo especial .ONESHELL (consulte Cómo usar un único shell). (En la práctica, make puede tomar atajos que no afecten a los resultados.)
Tenga en cuenta: esto implica que establecer variables del shell e invocar órdenes del shell como cd, que fijan un contexto local a cada proceso, no afectará a las líneas siguientes de la receta. (N. del T.: cd y similares son independientes en cada subshell, por lo que no se heredan en la línea siguiente.) Si quiere usar cd para que afecte a la sentencia siguiente, ponga ambas sentencias en una misma línea de receta. Así make invocará un único shell para ejecutar la línea entera, y el shell ejecutará las sentencias en secuencia. Por ejemplo:
foo : bar/lose
cd $(<D) && gobble $(<F) > ../$@
Aquí usamos el operador AND del shell (&&) para que, si la orden cd falla, el guion falle sin intentar invocar la orden gobble en el directorio equivocado, lo que podría causar problemas (en este caso, al menos seguro que provocaría que ../foo quedara truncado).
A veces preferirá que todas las líneas de la receta se pasen a una única invocación del shell. En general, hay dos situaciones en las que esto resulta útil: la primera, puede mejorar el rendimiento en makefiles cuyas recetas constan de muchas líneas de órdenes, al evitar procesos adicionales. La segunda, puede que quiera incluir saltos de línea en su orden de receta (por ejemplo, quizá esté usando un intérprete muy distinto como su SHELL). Si el objetivo especial .ONESHELL aparece en cualquier lugar del makefile, entonces todas las líneas de receta de cada objetivo se entregarán a una única invocación del shell. Los saltos de línea entre líneas de receta se conservan. Por ejemplo:
.ONESHELL:
foo : bar/lose
cd $(<D)
gobble $(<F) > ../$@
ahora funcionaría como se espera aunque las órdenes estén en líneas de receta distintas.
Si se indica .ONESHELL, entonces solo se comprueba la primera línea de la receta en busca de los caracteres de prefijo especiales («@», «-» y «+»). Las líneas posteriores incluirán los caracteres especiales en la línea de receta cuando se invoque el SHELL. Si quiere que su receta comience con uno de estos caracteres especiales, tendrá que arreglárselas para que no sean los primeros caracteres de la primera línea, quizá añadiendo un comentario o algo similar. Por ejemplo, esto sería un error de sintaxis en Perl, porque make elimina el primer «@»:
.ONESHELL:
SHELL = /usr/bin/perl
.SHELLFLAGS = -e
show :
@f = qw(a b c);
print "@f\n";
Sin embargo, cualquiera de estas alternativas funcionaría correctamente:
.ONESHELL:
SHELL = /usr/bin/perl
.SHELLFLAGS = -e
show :
# Asegúrese de que "@" no sea el primer carácter de la primera línea
@f = qw(a b c);
print "@f\n";
o
.ONESHELL:
SHELL = /usr/bin/perl
.SHELLFLAGS = -e
show :
my @f = qw(a b c);
print "@f\n";
Como característica especial, si se determina que SHELL es un shell de estilo POSIX, los caracteres de prefijo especiales de las líneas de receta «internas» (de la segunda en adelante) se eliminan antes de procesar la receta. Esta característica pretende permitir que los makefiles existentes añadan el objetivo especial .ONESHELL y sigan funcionando correctamente sin grandes modificaciones. Dado que los caracteres de prefijo especiales no son válidos al principio de una línea en un guion de shell POSIX, no se pierde ninguna funcionalidad. Por ejemplo, esto funciona como se espera:
.ONESHELL:
foo : bar/lose
@cd $(@D)
@gobble $(@F) > ../$@
Aun con esta característica especial, los makefiles con .ONESHELL se comportarán de forma distinta de maneras que podrían resultar perceptibles. Por ejemplo, normalmente, si falla cualquier línea de la receta, eso provoca que la regla falle y no se procesan más líneas de receta. Con .ONESHELL, make no se dará cuenta de un fallo en ninguna línea de receta que no sea la última. Puede modificar .SHELLFLAGS para añadir la opción -e al shell, lo que hará que cualquier fallo en cualquier punto de la línea de órdenes provoque el fallo del shell, pero esto mismo podría hacer que su receta se comporte de otra manera. En última instancia, puede que necesite reforzar sus líneas de receta para que funcionen con .ONESHELL.
El programa que se usa como shell se toma de la variable SHELL. Si esta variable no está establecida en su makefile, se usa el programa /bin/sh como shell. Los argumentos que se pasan al shell se toman de la variable .SHELLFLAGS. El valor predeterminado de .SHELLFLAGS es -c normalmente, o -ec en el modo conforme a POSIX.
A diferencia de la mayoría de las variables, la variable SHELL nunca se establece a partir del entorno. Esto se debe a que la variable de entorno SHELL se usa para especificar la preferencia personal de programa de shell para el uso interactivo. Sería muy malo que decisiones personales como esta afectaran al funcionamiento de los makefiles. Consulte Variables del entorno.
Además, cuando establece SHELL en su makefile, ese valor no se exporta en el entorno a las líneas de receta que make invoca. En su lugar, se exporta el valor heredado del entorno del usuario, si lo hay. Puede anular este comportamiento exportando explícitamente SHELL (consulte Comunicar variables a un sub-make), forzando a que se pase en el entorno a las líneas de receta.
Sin embargo, en MS-DOS y MS-Windows sí se usa el valor de SHELL del entorno, ya que en esos sistemas la mayoría de los usuarios no establecen esta variable y, por tanto, lo más probable es que se haya configurado específicamente para que la use make. En MS-DOS, si el valor de SHELL no es adecuado para make, puede establecer la variable MAKESHELL con el shell que make deba usar; si está establecida, se usará como shell en lugar del valor de SHELL.
Elegir un shell en MS-DOS y MS-Windows es mucho más complejo que en otros sistemas.
En MS-DOS, si SHELL no está establecida, se usa en su lugar el valor de la variable COMSPEC (que siempre está establecida).
El procesamiento de las líneas que establecen la variable SHELL en los makefiles es distinto en MS-DOS. El shell de serie, command.com, es ridículamente limitado en su funcionalidad y muchos usuarios de make tienden a instalar un shell de reemplazo. Por ello, en MS-DOS, make examina el valor de SHELL y cambia su comportamiento según si apunta a un shell de estilo Unix o de estilo DOS. Esto permite una funcionalidad razonable aunque SHELL apunte a command.com.
Si SHELL apunta a un shell de estilo Unix, make en MS-DOS comprueba además si ese shell puede encontrarse realmente; si no, ignora la línea que establece SHELL. En MS-DOS, GNU make busca el shell en los siguientes lugares:
SHELL. Por ejemplo, si el makefile especifica «SHELL = /bin/sh», make buscará en el directorio /bin de la unidad actual.
PATH, en orden.
En cada directorio que examina, make buscará primero el archivo específico (sh en el ejemplo anterior). Si no lo encuentra, buscará también en ese directorio ese archivo con alguna de las extensiones conocidas que identifican archivos ejecutables. Por ejemplo, .exe, .com, .bat, .btm, .sh y algunas otras.
Si alguno de estos intentos tiene éxito, el valor de SHELL se establecerá con la ruta completa del shell tal como se encontró. Sin embargo, si no se encuentra ninguno, el valor de SHELL no se cambiará y, por tanto, la línea que lo establece se ignorará en la práctica. Esto es así para que make solo admita características propias de un shell de estilo Unix si tal shell está realmente instalado en el sistema donde se ejecuta make.
Tenga en cuenta que esta búsqueda extendida del shell se limita a los casos en que SHELL se establece desde el makefile; si se establece en el entorno o en la línea de órdenes, se espera que usted la fije con la ruta completa del shell, exactamente como ocurre en Unix.
El efecto del procesamiento específico de DOS anterior es que un makefile que contiene «SHELL = /bin/sh» (como hacen muchos makefiles de Unix) funcionará en MS-DOS sin cambios si, por ejemplo, tiene sh.exe instalado en algún directorio de su PATH.
GNU make sabe cómo ejecutar varias recetas a la vez. Normalmente, make ejecuta solo una receta a la vez, esperando a que termine antes de ejecutar la siguiente. Sin embargo, la opción «-j» o «--jobs» le indica a make que ejecute muchas recetas simultáneamente. Puede inhibir el paralelismo para algunos o todos los objetivos desde dentro del makefile (consulte Cómo desactivar la ejecución en paralelo).
En MS-DOS, la opción «-j» no tiene efecto, ya que ese sistema no admite multiprocesamiento.
Si a la opción «-j» le sigue un entero, ese es el número de recetas que se ejecutan a la vez; a esto se le llama número de ranuras de tareas (job slots). Si no hay nada parecido a un entero tras la opción «-j», no hay límite en el número de ranuras de tareas. El número predeterminado de ranuras de tareas es uno, lo que significa ejecución en serie (una cosa a la vez).
Gestionar las invocaciones recursivas de make plantea cuestiones para la ejecución en paralelo. Para más información al respecto, consulte Comunicar opciones a un sub-make.
Si una receta falla (la mata una señal o termina con un estado distinto de cero) y los errores no se ignoran para esa receta (consulte Errores en las recetas), las líneas de receta restantes para rehacer el mismo objetivo no se ejecutarán. Si una receta falla y no se dio la opción «-k» o «--keep-going» (consulte Resumen de las opciones), make aborta la ejecución. Si make termina por cualquier motivo (incluida una señal) con procesos hijos en ejecución, espera a que terminen antes de salir realmente.
Cuando el sistema está muy cargado, probablemente querrá ejecutar menos tareas que cuando está poco cargado. Puede usar la opción «-l» para indicar a make que limite el número de tareas que se ejecutan a la vez, en función del promedio de carga. A la opción «-l» o «--max-load» le sigue un número de coma flotante. Por ejemplo,
-l 2.5
no dejará que make inicie más de una tarea si el promedio de carga supera 2.5. La opción «-l» sin un número a continuación elimina el límite de carga, si se había establecido uno con una opción «-l» anterior.
Más concretamente, cuando make va a iniciar una tarea y ya tiene al menos una en ejecución, comprueba el promedio de carga actual; si no es inferior al límite dado con «-l», make espera hasta que el promedio de carga baje de ese límite o hasta que terminen todas las demás tareas.
De manera predeterminada, no hay límite de carga.
Si un makefile define de forma completa y precisa las relaciones de dependencia entre todos sus objetivos, entonces make construirá las metas correctamente, esté activada o no la ejecución en paralelo. Esta es la forma ideal de escribir makefiles.
Sin embargo, a veces algunos o todos los objetivos de un makefile no pueden ejecutarse en paralelo y no resulta factible añadir los prerrequisitos necesarios para informar a make. En ese caso, el makefile puede usar diversos métodos para desactivar la ejecución en paralelo.
Si el objetivo especial .NOTPARALLEL sin prerrequisitos se especifica en cualquier lugar, entonces toda la instancia de make se ejecutará en serie, sin importar la configuración de paralelismo. Por ejemplo:
all: one two three one two three: ; @sleep 1; echo $@ .NOTPARALLEL:
Sin importar cómo se invoque make, los objetivos one, two y three se ejecutarán en serie.
Si el objetivo especial .NOTPARALLEL tiene prerrequisitos, entonces cada uno de esos prerrequisitos se considera un objetivo y todos los prerrequisitos de esos objetivos se ejecutan en serie. Tenga en cuenta que los prerrequisitos solo se ejecutan en serie al construir este objetivo: si algún otro objetivo enumera los mismos prerrequisitos y no está en .NOTPARALLEL, entonces esos prerrequisitos podrían ejecutarse en paralelo. Por ejemplo:
all: base notparallel base: one two three notparallel: one two three one two three: ; @sleep 1; echo $@ .NOTPARALLEL: notparallel
Aquí, «make -j base» ejecutará los objetivos one, two y three en paralelo, mientras que «make -j notparallel» los ejecutará en serie. Si ejecuta «make -j all», entonces sí se ejecutarán en paralelo, ya que base los enumera como prerrequisitos y no está serializado.
El objetivo .NOTPARALLEL no debería tener órdenes.
Por último, puede controlar la serialización de prerrequisitos concretos de manera detallada usando el objetivo especial .WAIT. Cuando este objetivo aparece en una lista de prerrequisitos y la ejecución en paralelo está activada, make no construirá ninguno de los prerrequisitos a la derecha de .WAIT hasta que se hayan completado todos los prerrequisitos a la izquierda de .WAIT. Por ejemplo:
all: one two .WAIT three one two three: ; @sleep 1; echo $@
Si la ejecución en paralelo está activada, make intentará construir one y two en paralelo, pero no intentará construir three hasta que ambos estén completos.
Al igual que con los objetivos proporcionados a .NOTPARALLEL, .WAIT solo surte efecto al construir el objetivo en cuya lista de prerrequisitos aparece. Si los mismos prerrequisitos están presentes en otros objetivos, sin .WAIT, entonces aún podrían ejecutarse en paralelo. Por ello, ni .NOTPARALLEL con objetivos ni .WAIT son tan fiables para controlar la ejecución en paralelo como definir una relación de prerrequisitos. No obstante, son fáciles de usar y pueden ser suficientes en situaciones menos complejas.
El prerrequisito .WAIT no estará presente en ninguna de las variables automáticas de la regla.
Por portabilidad, puede crear un objetivo .WAIT real en su makefile, pero no es necesario para usar esta característica. Si se crea un objetivo .WAIT, no debería tener prerrequisitos ni órdenes.
La característica .WAIT también está implementada en otras versiones de make y está especificada en el estándar POSIX de make.
Al ejecutar varias recetas en paralelo, la salida de cada receta aparece en cuanto se genera, con el resultado de que los mensajes de distintas recetas pueden entremezclarse, llegando a aparecer a veces incluso en la misma línea. Esto puede hacer que la salida sea muy difícil de leer.
Para evitarlo, puede usar la opción «--output-sync» («-O»). Esta opción indica a make que guarde la salida de las órdenes que invoca y la imprima toda junta una vez que las órdenes hayan terminado. Además, si hay varias invocaciones recursivas de make ejecutándose en paralelo, se comunicarán entre sí para que solo una de ellas genere salida a la vez.
Si la impresión del directorio de trabajo está activada (consulte La opción «--print-directory»), los mensajes de entrada/salida se imprimen alrededor de cada agrupación de salida. Si prefiere no ver estos mensajes, añada la opción «--no-print-directory» a MAKEFLAGS.
Hay cuatro niveles de granularidad al sincronizar la salida, que se especifican dando un argumento a la opción (por ejemplo, «-Oline» o «--output-sync=recurse»).
noneEste es el valor predeterminado: toda la salida se envía directamente a medida que se genera y no se realiza ninguna sincronización.
lineLa salida de cada línea individual de la receta se agrupa y se imprime en cuanto esa línea se completa. Si una receta consta de varias líneas, estas pueden entremezclarse con líneas de otras recetas.
targetLa salida de la receta completa de cada objetivo se agrupa y se imprime una vez que el objetivo se completa. Este es el valor predeterminado si se da la opción --output-sync o -O sin argumento.
recurseLa salida de cada invocación recursiva de make se agrupa y se imprime una vez que la invocación recursiva se completa.
Sea cual sea el modo elegido, el tiempo total de construcción será el mismo. La única diferencia está en cómo aparece la salida.
Los modos «target» y «recurse» recopilan ambos la salida de la receta completa de un objetivo y la muestran sin interrupciones cuando la receta se completa. La diferencia entre ellos está en cómo se tratan las recetas que contienen invocaciones recursivas de make (consulte Uso recursivo de make). Para todas las recetas que no tienen líneas recursivas, los modos «target» y «recurse» se comportan de forma idéntica.
Si se elige el modo «recurse», las recetas que contienen invocaciones recursivas de make se tratan igual que los demás objetivos: la salida de la receta, incluida la salida del make recursivo, se guarda y se imprime después de que la receta completa termine. Esto garantiza que la salida de todos los objetivos construidos por una instancia recursiva de make dada quede agrupada, lo que puede facilitar la comprensión de la salida. Sin embargo, también lleva a largos periodos durante la construcción en los que no se ve ninguna salida, seguidos de grandes ráfagas de salida. Si no está observando la construcción a medida que avanza, sino que consulta un registro de la construcción a posteriori, esta puede ser la mejor opción para usted.
Si está observando la salida, los largos silencios durante la construcción pueden resultar frustrantes. El modo de sincronización de salida «target» detecta cuándo se va a invocar a make de forma recursiva, usando los métodos estándar, y no sincroniza la salida de esas líneas. El make recursivo realizará la sincronización para sus objetivos y la salida de cada uno se mostrará de inmediato cuando se complete. Tenga en cuenta que la salida de las líneas recursivas de la receta no se sincroniza (por ejemplo, si la línea recursiva imprime un mensaje antes de ejecutar make, ese mensaje no se sincronizará).
El modo «line» puede ser útil para interfaces (front-ends) que observan la salida de make para seguir cuándo se inician y se completan las recetas.
Algunos programas invocados por make pueden comportarse de manera distinta si determinan que están escribiendo su salida en un terminal frente a un archivo (a menudo descrito como modo «interactivo» frente a «no interactivo»). Por ejemplo, muchos programas que pueden mostrar salida coloreada no lo harán si determinan que no están escribiendo en un terminal. Si su makefile invoca un programa así, usar las opciones de sincronización de salida hará que el programa crea que se está ejecutando en modo «no interactivo» aunque la salida acabe llegando al terminal.
Dos procesos no pueden recibir entrada del mismo dispositivo al mismo tiempo. Para asegurarse de que solo una receta intente recibir entrada del terminal a la vez, make invalidará los flujos de entrada estándar de todas las recetas en ejecución salvo una. Si otra receta intenta leer de la entrada estándar, normalmente incurrirá en un error fatal (una señal «Broken pipe»).
Es impredecible qué receta tendrá un flujo de entrada estándar válido (que vendrá del terminal, o de donde haya redirigido la entrada estándar de make). La primera receta ejecutada siempre lo obtendrá primero, y la primera receta iniciada después de que esa termine lo obtendrá a continuación, y así sucesivamente.
Cambiaremos cómo funciona este aspecto de make si encontramos una alternativa mejor. Mientras tanto, no debería depender de que ninguna receta use la entrada estándar si está usando la característica de ejecución en paralelo; pero si no está usando esta característica, entonces la entrada estándar funciona con normalidad en todas las recetas.
Cada vez que una invocación del shell retorna, make examina su estado de salida. Si el shell se completó correctamente (el estado de salida es cero), la siguiente línea de la receta se ejecuta en un nuevo shell; una vez terminada la última línea, la regla queda completa.
Si hay un error (el estado de salida es distinto de cero), make abandona la regla actual y, en algunos casos, todas las reglas.
A veces, el fallo de cierta línea de receta no indica un problema. Por ejemplo, puede usar la orden mkdir para asegurarse de que un directorio existe. Si el directorio ya existe, mkdir informará de un error, pero probablemente usted quiera que make continúe de todos modos.
Para ignorar los errores de una línea de receta, escriba un «-» al principio del texto de la línea (después del tabulador inicial). El «-» se descarta antes de pasar la línea al shell para su ejecución.
Por ejemplo,
clean:
-rm -f *.o
Esto hace que make continúe aunque rm no consiga eliminar un archivo.
Cuando ejecuta make con la opción «-i» o «--ignore-errors», los errores se ignoran en todas las recetas de todas las reglas. Una regla en el makefile para el objetivo especial .IGNORE tiene el mismo efecto, si no hay prerrequisitos. Esto es menos flexible, pero a veces resulta útil.
Cuando los errores se van a ignorar, ya sea por un «-» o por la opción «-i», make trata un retorno con error igual que un éxito, salvo que imprime un mensaje que le indica el código de estado con el que salió el shell y dice que el error se ha ignorado.
Cuando se produce un error que no se le ha indicado a make que ignore, esto implica que el objetivo actual no puede rehacerse correctamente, y tampoco ningún otro que dependa de él, directa o indirectamente. No se ejecutarán más recetas para estos objetivos, ya que no se han alcanzado sus condiciones previas.
Normalmente, make se rinde de inmediato en esta circunstancia y devuelve un estado distinto de cero. Sin embargo, si se especifica la opción «-k» o «--keep-going», make sigue considerando los demás prerrequisitos de los objetivos pendientes, rehaciéndolos si es necesario, antes de rendirse y devolver un estado distinto de cero. Por ejemplo, tras un error al compilar un archivo objeto, «make -k» seguirá compilando otros archivos objeto aunque ya sepa que será imposible enlazarlos. Consulte Resumen de las opciones.
El comportamiento habitual supone que su propósito es poner al día los objetivos especificados; una vez que make descubre que esto es imposible, más vale que informe del fallo de inmediato. La opción «-k» dice que el propósito real es probar tantos de los cambios hechos en el programa como sea posible, quizá para encontrar varios problemas independientes y poder corregirlos todos antes del siguiente intento de compilación. Por esto la orden compile de Emacs pasa la opción «-k» de manera predeterminada.
Normalmente, cuando una línea de receta falla, si ha cambiado el archivo objetivo lo más mínimo, el archivo está corrupto y no puede usarse —o, al menos, no está completamente actualizado—. Sin embargo, la marca de tiempo del archivo indica que ahora está actualizado, así que la próxima vez que se ejecute make, no intentará actualizar ese archivo. La situación es justo la misma que cuando el shell es matado por una señal; consulte Interrumpir o matar make. Así que, en general, lo correcto es eliminar el archivo objetivo si la receta falla después de haber empezado a cambiar el archivo. make hará esto si .DELETE_ON_ERROR aparece como objetivo. Esto es casi siempre lo que usted quiere que make haga, pero no es la práctica histórica; así que, por compatibilidad, debe solicitarlo explícitamente.
Si make recibe una señal fatal mientras se está ejecutando un shell, puede eliminar el archivo objetivo que la receta debía actualizar. Esto se hace si la hora de última modificación del archivo objetivo ha cambiado desde que make la comprobó por primera vez.
El propósito de eliminar el objetivo es asegurarse de que se rehaga desde cero la próxima vez que se ejecute make. ¿Por qué? Supongamos que pulsa Ctrl-c mientras se ejecuta un compilador, justo cuando ha empezado a escribir el archivo objeto foo.o. El Ctrl-c mata al compilador, lo que da como resultado un archivo incompleto cuya hora de última modificación es más reciente que la del archivo fuente foo.c. Pero make también recibe la señal de Ctrl-c y elimina este archivo incompleto. Si make no hiciera esto, la siguiente invocación de make pensaría que foo.o no necesitaba actualización —con el resultado de que el enlazador intentaría enlazar un archivo objeto incompleto y produciría extraños mensajes de error—.
Para evitar esta eliminación de archivos objetivo, haga que el objetivo especial .PRECIOUS dependa de ese archivo. Antes de rehacer un objetivo, make comprueba si aparece entre los prerrequisitos de .PRECIOUS y, con ello, decide si debe eliminar el objetivo cuando se produzca una señal. Algunas razones para querer esto son que el objetivo se actualice de alguna forma atómica (indivisible), o que exista solo para registrar una hora de modificación (su contenido no importa), o que deba existir siempre para evitar otros tipos de problemas.
make hace todo lo posible por limpiar, pero hay situaciones en las que la limpieza es imposible. Por ejemplo, make puede ser matado por una señal que no puede capturar. O bien uno de los programas que make invoca puede ser matado o estrellarse, dejando un archivo objetivo corrupto pero con una marca de tiempo que indica que está actualizado. En este caso, make no se da cuenta de que ese fallo requiere limpiar el objetivo. O bien el propio make puede toparse con un fallo y estrellarse.
Por estas razones, lo mejor es escribir recetas defensivas (defensive recipe). Una receta defensiva no deja un objetivo corrupto aunque falle. Lo más habitual es crear un archivo temporal en lugar de actualizar el objetivo directamente, y luego renombrar el archivo temporal con el nombre final del objetivo. Algunos compiladores ya funcionan así, por lo que en ese caso no es necesario escribir una receta defensiva.
El uso recursivo de make consiste en usar make como orden dentro de un makefile. Esta técnica resulta útil cuando quiere tener makefiles distintos para los diversos subsistemas que componen un sistema mayor. Por ejemplo, supongamos que tiene un subdirectorio subdir con su propio makefile, y que quiere ejecutar make sobre ese subdirectorio desde el makefile del directorio que lo contiene. Puede lograrlo escribiendo lo siguiente:
subsystem:
cd subdir && $(MAKE)
O, de forma equivalente, también podría escribirlo así (consulte Resumen de las opciones):
subsystem:
$(MAKE) -C subdir
Puede copiar este ejemplo tal cual para escribir una orden make recursiva, pero hay muchas cosas que conviene saber sobre cómo y por qué funcionan, y sobre cómo se relaciona el sub-make con el make de nivel superior. También puede resultar útil declarar como «.PHONY» el objetivo que invoca la orden make recursiva (para más detalles sobre cuándo es útil, consulte Objetivos ficticios).
Por comodidad, GNU make establece la variable CURDIR con la ruta del directorio de trabajo actual al arrancar (después de procesar la opción -C). Este valor no vuelve a ser tocado por make. En particular, tenga en cuenta que incluir (include) archivos de otros directorios no cambia el valor de CURDIR. Este valor tiene la misma precedencia que si se estableciera en el makefile (de manera predeterminada, la variable de entorno CURDIR no anula este valor). Tenga en cuenta que establecer esta variable no tiene efecto en el funcionamiento de make (por ejemplo, make no cambia su directorio de trabajo).
En las órdenes make recursivas, debería usar siempre la variable MAKE, no el nombre de orden explícito «make». Escriba así:
subsystem:
cd subdir && $(MAKE)
El valor de esta variable es el nombre de archivo con el que se invocó make. Si ese nombre de archivo fuera /bin/make, la receta ejecutada sería «cd subdir && /bin/make». Si usa una versión especial de make para ejecutar el makefile de nivel superior, esa misma versión especial se ejecutará en las invocaciones recursivas.
Como característica especial, usar la variable MAKE en la receta de una regla altera los efectos de las opciones «-t» («--touch»), «-n» («--just-print») y «-q» («--question»). Usar la variable MAKE tiene el mismo efecto que poner un carácter «+» al principio de la línea de receta. Consulte En lugar de ejecutar las recetas. Esta característica especial solo se activa cuando la variable MAKE aparece directamente en la receta. No se aplica cuando la variable MAKE se referencia a través de la expansión de otra variable. En ese caso, debe usar el componente «+» para obtener estos efectos especiales.
Consideremos la orden «make -t» en el ejemplo anterior. (La opción «-t» marca los objetivos como actualizados sin ejecutar realmente ninguna receta; consulte En lugar de ejecutar las recetas.) Siguiendo la definición habitual de «-t», en este ejemplo la orden «make -t» se limitaría a crear un archivo llamado subsystem y no haría nada más. Lo que realmente quiere que haga es ejecutar «cd subdir && make -t», pero eso requeriría ejecutar la receta, y «-t» dice que no se ejecuten recetas.
Esta característica especial hace lo que usted desea. Siempre que una línea de receta de una regla contenga la variable MAKE, las opciones «-t», «-n» y «-q» no se aplican a esa línea. Las líneas de receta que contienen MAKE se ejecutan con normalidad a pesar de la presencia de opciones que impiden ejecutar la mayoría de las recetas. Y el mecanismo habitual de MAKEFLAGS pasa las opciones al sub-make (consulte Comunicar opciones a un sub-make), de modo que su petición de tocar los archivos, o de mostrar las recetas, se propaga al subsistema.
Los valores de las variables del make de nivel superior pueden pasarse al sub-make a través del entorno, si se solicita explícitamente. Estas variables se definen en el sub-make como valores predeterminados, pero no anulan las variables definidas en el makefile que usa el sub-make, salvo que use la opción «-e» (consulte Resumen de las opciones).
Para pasar variables hacia abajo, es decir, para exportarlas (export), make añade la variable y su valor al entorno con el que ejecuta cada línea de receta. El sub-make, a su vez, usa ese entorno para inicializar su tabla de valores de variables. Consulte Variables del entorno.
Salvo que se solicite explícitamente, make exporta una variable solo si esta ya estaba definida en el entorno desde el principio, o si se estableció en la línea de órdenes y su nombre consta únicamente de letras, dígitos y guiones bajos.
El valor de la variable de make SHELL no se exporta. En su lugar, se pasa al sub-make el valor de la variable SHELL presente en el entorno de arranque. Puede hacer que make exporte el valor de SHELL usando la directiva export que se describe más adelante. Consulte Cómo elegir el shell.
La variable especial MAKEFLAGS se exporta siempre (salvo que la haga unexport). MAKEFILES se exporta si se le establece cualquier valor.
make pasa automáticamente hacia abajo los valores de variable definidos en la línea de órdenes. Lo hace poniéndolos en la variable MAKEFLAGS. Consulte Comunicar opciones a un sub-make.
Si una variable es de las creadas por make de manera predeterminada (consulte Variables usadas por las reglas implícitas), normalmente no se pasa hacia abajo. El sub-make las define por su cuenta.
Si quiere exportar variables concretas a un sub-make, use la directiva export de la siguiente forma:
export variable …
A la inversa, si quiere impedir que una variable se exporte, use la directiva unexport de la siguiente forma:
unexport variable …
En ambas formas, los argumentos de export y unexport se expanden. Por tanto, puede especificar variables o funciones que se expandan al nombre (o la lista de nombres) de las variables que deben (un)exportarse.
Por comodidad, también puede definir y exportar una variable a la vez:
export variable = value
lo que da el mismo resultado que:
variable = value export variable
Y
export variable := value
da el mismo resultado que:
variable := value export variable
Asimismo,
export variable += value
es exactamente igual que:
variable += value export variable
Consulte Cómo añadir texto a las variables.
Quizá haya notado que las directivas export y unexport funcionan en make exactamente igual que en el shell sh.
Si quiere exportar todas las variables de manera predeterminada, puede usar export por sí solo:
export
Esto le dice a make que las variables que no se mencionan explícitamente en una directiva export o unexport también deben exportarse. Cualquier variable indicada en una directiva unexport seguirá sin exportarse.
El comportamiento que provoca usar export por sí solo era el predeterminado en versiones antiguas de GNU make. Si su makefile depende de este comportamiento y quiere mantener la compatibilidad con versiones antiguas de make, puede añadir el objetivo especial .EXPORT_ALL_VARIABLES al makefile en lugar de usar la directiva export. Las versiones antiguas de make lo ignorarán, mientras que la directiva export provocaría un error de sintaxis en ellas.
Al exportar variables de manera predeterminada mediante export por sí solo o .EXPORT_ALL_VARIABLES, solo se exportan las variables cuyo nombre consta únicamente de caracteres alfanuméricos y guiones bajos. Para exportar otras variables, debe enumerarlas por su nombre en una directiva export.
Para añadir el valor de una variable al entorno, hay que expandir ese valor. Si la expansión de la variable tiene efectos secundarios (como con funciones del tipo info o eval), esos efectos secundarios se manifestarán cada vez que se invoque una orden. Puede evitarlo dando a esas variables un nombre de los que no se exportan de manera predeterminada. No obstante, una solución mejor es no usar en absoluto esta característica de «exportar de manera predeterminada» y, en su lugar, exportar explícitamente las variables en cuestión enumerándolas por su nombre.
Usar unexport por sí solo le dice a make que no exporte variables de manera predeterminada. Como este es el comportamiento predeterminado, solo lo necesitará si export se usó antes por sí solo (quizá en un makefile incluido). No puede usar export y unexport por sí solos para exportar variables en algunas recetas y no en otras. La última directiva export o unexport que aparezca por sí sola determina el comportamiento de toda la ejecución de make.
Como característica especial, la variable MAKELEVEL cambia al pasar de un nivel a otro hacia abajo. El valor de esta variable es una cadena que representa la profundidad del nivel en notación decimal. El valor es «0» en el make de nivel superior, «1» en un sub-make, «2» en un sub-sub-make, y así sucesivamente. Este incremento ocurre cuando make prepara el entorno para una receta.
El uso principal de MAKELEVEL es probarlo dentro de directivas condicionales (consulte Partes condicionales de los makefiles). Así puede escribir un makefile que se comporte de manera distinta según se ejecute de forma recursiva o lo ejecute usted directamente.
Puede usar la variable MAKEFILES para que todas las órdenes de sub-make usen makefiles adicionales. El valor de MAKEFILES es una lista de nombres de archivo separados por espacios. Si esta variable se define en un makefile de un nivel exterior, se pasa hacia abajo a través del entorno. Entonces funciona como una lista de makefiles adicionales que el sub-make lee antes que los makefiles habituales o especificados. Consulte La variable MAKEFILES.
Indicadores como «-s» y «-k» se pasan automáticamente al sub-make a través de la variable MAKEFLAGS. make prepara automáticamente esta variable para que contenga los caracteres de los indicadores que recibió. Así, si ejecuta «make -ks», MAKEFLAGS obtiene el valor «ks».
En consecuencia, cada sub-make recibe en su entorno el valor de MAKEFLAGS. En respuesta, el sub-make extrae los indicadores de ese valor y los procesa como si se hubieran dado como argumentos. Consulte Resumen de las opciones. Esto significa que, a diferencia de otras variables de entorno, el MAKEFLAGS especificado en el entorno tiene prioridad sobre el MAKEFLAGS especificado en el makefile.
El valor de MAKEFLAGS es un conjunto (posiblemente vacío) de caracteres que representan opciones de una sola letra que no toman argumentos, seguido de un espacio y de las opciones que toman argumentos o las opciones de nombre largo. Si una opción tiene versión de una letra y versión de nombre largo, siempre se prefiere la versión de una letra. Si no hay ninguna opción de una sola letra en la línea de órdenes, el valor de MAKEFLAGS empieza con un espacio.
Del mismo modo, las variables definidas en la línea de órdenes también se pasan al sub-make a través de MAKEFLAGS. make trata cualquier palabra del valor de MAKEFLAGS que contenga «=» como si fuera una definición de variable que apareciera en la línea de órdenes. Consulte Anular variables.
Las opciones «-C», «-f», «-o» y «-W» no se ponen en MAKEFLAGS. Estas opciones no se pasan hacia abajo.
La opción «-j» es un caso especial (consulte Ejecución en paralelo). Si la establece con algún valor numérico «N» y su sistema operativo lo admite (la mayoría de los sistemas UNIX lo hacen, pero los demás normalmente no), el make padre y todos los sub-makes se comunican para que, entre todos, solo se ejecuten «N» tareas a la vez. Tenga en cuenta que las tareas marcadas como recursivas (consulte En lugar de ejecutar las recetas) no cuentan para el total de tareas (¡de lo contrario, podría haber «N» sub-makes en ejecución y no quedar ninguna ranura para el trabajo real!).
Si su sistema operativo no admite la comunicación anterior, «-j» no se añade a MAKEFLAGS, de modo que los sub-makes se ejecutan en modo no paralelo. Si la opción «-j» se pasara a los sub-makes, ejecutaría muchas más tareas en paralelo de las que solicitó. Si da «-j» sin un argumento numérico, significa ejecutar tantas tareas en paralelo como sea posible, y eso sí se pasa hacia abajo, ya que muchos infinitos no son distintos de un solo infinito.
Si no quiere pasar hacia abajo otros indicadores, debe cambiar el valor de MAKEFLAGS, por ejemplo así:
subsystem:
cd subdir && $(MAKE) MAKEFLAGS=
Las definiciones de variable de la línea de órdenes aparecen en realidad en la variable MAKEOVERRIDES, y MAKEFLAGS contiene una referencia a esta variable. Si quiere pasar los indicadores hacia abajo con normalidad, pero no las definiciones de variable de la línea de órdenes, puede restablecer MAKEOVERRIDES a vacío así:
MAKEOVERRIDES =
Esto no es algo que se haga normalmente. Pero algunos sistemas tienen un límite fijo y pequeño en el tamaño del entorno, y poner tanta información en el valor de MAKEFLAGS puede superarlo. Si ve el mensaje de error «Arg list too long», quizá sea por esto. (Para cumplir estrictamente con POSIX.2, si el objetivo especial «.POSIX» aparece en el makefile, cambiar MAKEOVERRIDES no afecta a MAKEFLAGS. Probablemente no necesite preocuparse por esto.)
Por compatibilidad histórica, también existe una variable muy parecida, MFLAGS. Tiene el mismo valor que MAKEFLAGS, salvo que no incluye las definiciones de variable de la línea de órdenes y que, mientras no esté vacía, siempre empieza con un guion (MAKEFLAGS empieza con un guion solo cuando comienza con una opción sin versión de una letra, como «--warn-undefined-variables»). Tradicionalmente, MFLAGS se usaba de forma explícita en la orden make recursiva, así:
subsystem:
cd subdir && $(MAKE) $(MFLAGS)
Pero ahora que existe MAKEFLAGS, este uso ya no es necesario. Si quiere que su makefile sea compatible con programas make antiguos, use esta técnica; también funcionará bien con versiones más recientes de make.
La variable MAKEFLAGS también resulta útil si quiere tener establecidas ciertas opciones, como «-k» (consulte Resumen de las opciones), cada vez que ejecute make. Basta con poner el valor de MAKEFLAGS en el entorno. También puede establecer MAKEFLAGS dentro de un makefile para especificar indicadores adicionales que deban estar activos en ese makefile. (Tenga en cuenta que MFLAGS no sirve para este propósito; esa variable se establece solo por compatibilidad y make no interpreta de ningún modo el valor que usted establezca.)
Cuando make interpreta el valor de MAKEFLAGS (ya sea del entorno o de un makefile), primero antepone un guion si el valor no empieza ya por uno. Luego divide el valor en palabras separadas por espacios y analiza esas palabras como si fueran opciones dadas en la línea de órdenes (salvo que «-C», «-f», «-h», «-o» y «-W» y sus versiones de nombre largo se ignoran; y una opción no válida tampoco es un error).
Si pone MAKEFLAGS en el entorno, tenga cuidado de no incluir nunca opciones que cambien drásticamente el comportamiento de make y arruinen el propósito de su makefile y del propio make. Por ejemplo, poner las opciones «-t», «-n» o «-q» en cualquiera de estas variables podría tener resultados desastrosos y, como mínimo, tendrá efectos sorprendentes y, probablemente, molestos.
Si también quiere usar otras implementaciones de make distintas de GNU make y, por tanto, no quiere añadir indicadores específicos de GNU make a la variable MAKEFLAGS, puede añadirlos en su lugar a la variable GNUMAKEFLAGS. Esta variable se analiza justo antes de MAKEFLAGS, de la misma manera que MAKEFLAGS. Al construir el MAKEFLAGS que se pasa a un make recursivo, make incluye todos los indicadores, incluidos los tomados de GNUMAKEFLAGS. Por ello, tras analizar GNUMAKEFLAGS, GNU make establece esta variable a la cadena vacía para evitar que los indicadores se dupliquen durante la recursión.
Lo mejor es usar en GNUMAKEFLAGS solo indicadores que no cambien en lo esencial el comportamiento del makefile. Si su makefile requiere GNU Make de todos modos, use simplemente MAKEFLAGS. Indicadores como «--no-print-directory» o «--output-sync» podrían ser adecuados para GNUMAKEFLAGS.
Si usa varios niveles de invocaciones recursivas de make, la opción «-w» o «--print-directory» hace que la salida sea mucho más fácil de entender, ya que muestra cada directorio cuando make empieza a procesarlo y cuando termina. Por ejemplo, si ejecuta «make -w» en el directorio /u/gnu/make, make imprimirá una línea de la forma:
make: Entering directory `/u/gnu/make'.
antes de hacer nada más, e imprimirá una línea de la forma:
make: Leaving directory `/u/gnu/make'.
cuando el procesamiento se complete. Normalmente no necesita especificar esta opción, porque «make» lo hace por usted automáticamente: «-w» se activa automáticamente al usar la opción «-C» y en los sub-makes. make no activará «-w» automáticamente si también usa «-s», que indica silencio, o si usa «--no-print-directory», que lo desactiva explícitamente.
Cuando la misma secuencia de órdenes resulta útil para crear varios objetivos, puede definirla como una secuencia predefinida (canned sequence) con la directiva define y referenciar esa secuencia predefinida desde las recetas de esos objetivos. La secuencia predefinida es en realidad una variable, así que su nombre no debe colisionar con otros nombres de variable.
Aquí tiene un ejemplo de cómo definir una receta predefinida:
define run-yacc = yacc $(firstword $^) mv y.tab.c $@ endef
Aquí, run-yacc es el nombre de la variable que se define; endef marca el final de la definición, y las líneas intermedias son las órdenes. La directiva define no expande las referencias a variables ni las llamadas a funciones dentro de la secuencia predefinida; el carácter «$», los paréntesis, los nombres de variable, etc., pasan a formar parte del valor de la variable que se está definiendo. Para una descripción completa de define, consulte Cómo definir variables multilínea.
La primera orden de este ejemplo ejecuta Yacc sobre el primer prerrequisito de cualquier regla que use esta secuencia predefinida. El archivo de salida de Yacc siempre se llama y.tab.c. La segunda orden mueve esa salida al nombre de archivo del objetivo de la regla.
Para usar la secuencia predefinida, asigne la variable a la receta de una regla. Puede asignarla como cualquier otra variable (consulte Conceptos básicos de las referencias a variables). Como las variables definidas con define son variables de expansión recursiva, todas las referencias a variables que escriba dentro del define se expanden aquí y ahora. Por ejemplo:
foo.c : foo.y
$(run-yacc)
Cuando aparece «$^» en el valor de run-yacc, se sustituye por «foo.y», y «$@» se sustituye por «foo.c».
Este es un ejemplo realista, pero este en concreto no es realmente necesario, porque make tiene una regla implícita que deduce estas órdenes a partir de los nombres de archivo implicados (consulte Cómo usar las reglas implícitas).
En la ejecución de las recetas, cada línea de la secuencia predefinida se trata como si esa línea apareciera por sí sola en la regla, precedida por un tabulador. En particular, make invoca un subshell distinto para cada línea. Puede usar los caracteres de prefijo especiales que actúan sobre una línea de órdenes («@», «-» y «+») en cada línea de una secuencia predefinida. Consulte Cómo escribir recetas en reglas. Por ejemplo, usando esta secuencia predefinida:
define frobnicate = @echo "frobnicating target $@" frob-step-1 $< -o $@-step-1 frob-step-2 $@-step-1 -o $@ endef
make no hará eco de la primera línea, la orden echo. Pero sí hará eco de las dos líneas de receta siguientes.
Por otro lado, los caracteres de prefijo puestos en la línea de receta que referencia la secuencia predefinida se aplican a todas las líneas de la secuencia. Así, la siguiente regla:
frob.out: frob.in
@$(frobnicate)
no hace eco de ninguna línea de receta. (Para una descripción completa de «@», consulte Eco de las recetas.)
A veces resulta útil definir recetas que no hacen nada. Esto se consigue simplemente dando una receta que consta solo de espacio en blanco. Por ejemplo:
target: ;
Esto define una receta vacía para target. También podría usar una línea que empiece con el carácter de prefijo de receta para definir una receta vacía, pero eso resultaría confuso, porque esa línea parecería vacía.
Quizá se pregunte por qué querría definir una receta que no hace nada. Una de las razones por las que esto resulta útil es para evitar que un objetivo reciba recetas implícitas (de una regla implícita o del objetivo especial .DEFAULT; consulte Cómo usar las reglas implícitas y Cómo definir reglas predeterminadas de último recurso).
Las recetas vacías también sirven para evitar errores en objetivos que se crean como efecto secundario de otra receta. Si el objetivo no existe, una receta vacía hace que make no se queje de no saber cómo crear ese objetivo, y lo considera desactualizado.
Quizá le tiente definir una receta vacía para un objetivo que no es un archivo real, sino que existe solo para que se rehagan sus prerrequisitos. Sin embargo, esta no es la mejor forma de hacerlo, porque los prerrequisitos podrían no rehacerse correctamente si el archivo objetivo existe de verdad. Para una mejor manera de hacerlo, consulte Objetivos ficticios.