Una regla (rule) aparece en el makefile e indica cuándo y cómo rehacer ciertos archivos, llamados los objetivos (target) de la regla (lo más habitual es uno solo por regla). En ella se enumeran los demás archivos que constituyen los prerrequisitos (prerequisite, antes llamados dependencias) del objetivo, y la receta (recipe, lo que la regla ejecuta) que se usa para crear o actualizar el objetivo.
El orden de las reglas no es significativo, salvo para determinar la meta predeterminada: el objetivo que make considerará si no se especifica otro. La meta predeterminada es el primer objetivo de la primera regla del primer makefile. Hay dos excepciones: un objetivo que empieza por un punto no es la predeterminada a menos que contenga además una o más barras «/»; y un objetivo que define una regla de patrón no influye en absoluto sobre la meta predeterminada. (Véase Definición y redefinición de reglas de patrón.)
Por eso normalmente el makefile se escribe de modo que la primera regla sea la de compilar el programa completo (o todos los programas descritos por el makefile); a este objetivo se le suele dar el nombre «all». Véase Argumentos para especificar las metas.
Veamos primero un ejemplo de regla:
foo.o : foo.c defs.h # módulo para manipular los frobs
cc -c -g foo.c
El objetivo de esta regla es foo.o y sus prerrequisitos son foo.c y defs.h. La receta contiene una sola orden: «cc -c -g foo.c». La receta empieza con un tabulador para identificarla como tal.
Esta regla dice dos cosas:
cc tal como se indica. La receta no menciona explícitamente defs.h, pero se presupone que foo.c lo incluye mediante #include, y por eso se ha añadido defs.h a los prerrequisitos.
En general, una regla tiene este aspecto:
targets : prerequisites
recipe
…
o bien este otro:
targets : prerequisites ; recipe
recipe
…
Los targets (objetivos) son nombres de archivo, separados por espacios. Pueden usarse comodines (véase Uso de comodines en los nombres de archivo) y un nombre de la forma «a(m)» representa el miembro m del archivo de archivo a (véase Miembros de archivo como objetivos). Por lo común hay un único objetivo por regla, pero a veces hay motivos para tener más (véase Múltiples objetivos en una regla).
Las líneas de la recipe (receta) empiezan con un tabulador (o con el primer carácter del valor de la variable .RECIPEPREFIX; véase Otras variables especiales). La primera línea de la receta puede aparecer en la línea siguiente a los prerrequisitos, con un tabulador, o en la misma línea, con un punto y coma. De cualquiera de las dos formas el efecto es el mismo. Hay otras diferencias en la sintaxis de las recetas. Véase Escritura de recetas en las reglas.
Como el signo de dólar se usa para iniciar las referencias a variables de make, si realmente se quiere un signo de dólar en un objetivo o un prerrequisito hay que escribir dos de ellos, «$$» (véase Cómo usar las variables). Y si se ha habilitado la expansión secundaria (véase Expansión secundaria) y se quiere un signo de dólar literal en la lista de prerrequisitos, hay que escribir en realidad cuatro signos de dólar («$$$$»).
Una línea larga puede dividirse insertando una barra invertida seguida de un salto de línea, pero esto no es obligatorio, ya que make no impone ningún límite a la longitud de una línea en el makefile.
Una regla le dice a make dos cosas: cuándo los objetivos están desactualizados y cómo actualizarlos cuando es necesario.
El criterio para estar desactualizado se especifica mediante los prerequisites (prerrequisitos), que son nombres de archivo separados por espacios. (También aquí se admiten comodines y miembros de archivo de archivo (véase Uso de make para actualizar archivos de archivo).) Un objetivo está desactualizado si no existe o si es más antiguo que cualquiera de sus prerrequisitos (por comparación de las horas de última modificación). La idea es que el contenido del archivo de objetivo se calcula a partir de la información de los prerrequisitos, de modo que, si alguno de ellos cambia, el contenido del archivo de objetivo existente ya no es necesariamente válido.
Cómo actualizar se especifica con una recipe (receta). Se trata de una o más líneas que ejecuta el shell (normalmente «sh»), pero con algunas funciones adicionales (véase Escritura de recetas en las reglas).
GNU make entiende dos tipos distintos de prerrequisitos: los prerrequisitos normales, descritos en la sección anterior, y los prerrequisitos de solo orden (order-only). Un prerrequisito normal hace dos afirmaciones. Primero, impone un orden en el que se invocan las recetas: las recetas de todos los prerrequisitos de un objetivo se completan antes de que se inicie la receta del objetivo. Segundo, impone una relación de dependencia: si algún prerrequisito es más reciente que el objetivo, este se considera desactualizado y debe reconstruirse.
Normalmente esto es exactamente lo que se desea: si se actualiza un prerrequisito de un objetivo, el objetivo también debería actualizarse.
A veces, sin embargo, se quiere garantizar que un prerrequisito se construya antes que un objetivo, pero sin forzar la actualización del objetivo cuando el prerrequisito se actualiza. Los prerrequisitos de solo orden sirven para crear este tipo de relación. Los prerrequisitos de solo orden se especifican colocando una barra vertical (|) en la lista de prerrequisitos: los prerrequisitos situados a la izquierda de la barra son normales; los situados a la derecha son de solo orden:
targets : normal-prerequisites | order-only-prerequisites
La sección de prerrequisitos normales puede, por supuesto, estar vacía. Además, se pueden declarar varias líneas de prerrequisitos para el mismo objetivo: se concatenan de forma adecuada (los prerrequisitos normales se añaden a la lista de prerrequisitos normales; los de solo orden, a la lista de prerrequisitos de solo orden). Tenga en cuenta que, si se declara el mismo archivo como prerrequisito normal y de solo orden, el prerrequisito normal tiene prioridad (puesto que su comportamiento es un superconjunto estricto del de un prerrequisito de solo orden).
Los prerrequisitos de solo orden nunca se comprueban al determinar si el objetivo está desactualizado; ni siquiera un prerrequisito de solo orden marcado como ficticio (phony) (véase Objetivos ficticios (phony)) provocará que el objetivo se reconstruya.
Considérese un ejemplo en el que los objetivos deben colocarse en un directorio aparte, y ese directorio podría no existir antes de ejecutar make. En esta situación se quiere que el directorio se cree antes de colocar en él cualquier objetivo pero, como la marca de tiempo de los directorios cambia cada vez que se añade, elimina o renombra un archivo, desde luego no se quiere reconstruir todos los objetivos cada vez que cambia la marca de tiempo del directorio. Una manera de gestionarlo es con prerrequisitos de solo orden: hacer del directorio un prerrequisito de solo orden de todos los objetivos:
OBJDIR := objdir
OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)
$(OBJDIR)/%.o : %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
all: $(OBJS)
$(OBJS): | $(OBJDIR)
$(OBJDIR):
mkdir $(OBJDIR)
Así, la regla que crea el directorio objdir se ejecutará, si es necesario, antes de que se construya cualquier «.o», pero ningún «.o» se construirá por el mero hecho de que haya cambiado la marca de tiempo del directorio objdir.
Un único nombre de archivo puede especificar muchos archivos mediante comodines. Los comodines de make son «*», «?» y «[…]», los mismos que en el shell Bourne. Por ejemplo, *.c especifica una lista de todos los archivos (del directorio de trabajo) cuyos nombres terminan en «.c».
Si una expresión coincide con varios archivos, los resultados se ordenan.2 Sin embargo, varias expresiones no se ordenan de forma global. Por ejemplo, *.c *.h enumera todos los archivos cuyos nombres terminan en «.c» (ordenados), seguidos de todos los archivos cuyos nombres terminan en «.h» (ordenados).
El carácter «~» al comienzo de un nombre de archivo también tiene un significado especial. Si aparece solo, o seguido de una barra, representa su directorio de inicio. Por ejemplo, ~/bin se expande a /home/you/bin. Si al «~» le sigue una palabra, la cadena representa el directorio de inicio del usuario nombrado por esa palabra. Por ejemplo, ~john/bin se expande a /home/john/bin. En sistemas que no tienen un directorio de inicio para cada usuario (como MS-DOS o MS-Windows), esta funcionalidad puede simularse definiendo la variable de entorno HOME.
La expansión de comodines la realiza make automáticamente en los objetivos y en los prerrequisitos. En las recetas, el responsable de la expansión de comodines es el shell. En los demás contextos, la expansión de comodines solo se produce si se solicita explícitamente con la función wildcard.
El significado especial de un comodín puede desactivarse precediéndolo de una barra invertida. Así, foo\*bar se referiría a un archivo concreto cuyo nombre se compone de «foo», un asterisco y «bar».
Los comodines pueden usarse en la receta de una regla, donde el shell se encarga de expandirlos. Por ejemplo, esta es una regla para borrar todos los archivos objeto:
clean:
rm -f *.o
Los comodines también son útiles en los prerrequisitos de una regla. Con la siguiente regla en el makefile, «make print» imprimirá todos los archivos «.c» que hayan cambiado desde la última vez que se imprimieron:
print: *.c
lpr -p $?
touch print
Esta regla usa print como archivo de objetivo vacío; véase Archivos de objetivo vacíos para registrar eventos. (Se usa la variable automática «$?» para imprimir solo los archivos que han cambiado; véase Variables automáticas.)
La expansión de comodines no se produce al definir una variable. Así, si se escribe esto:
objects = *.o
el valor de la variable objects es la cadena literal «*.o». No obstante, si se usa el valor de objects en un objetivo o un prerrequisito, allí sí tendrá lugar la expansión de comodines. Si se usa el valor de objects en una receta, el shell podría realizar la expansión de comodines cuando la receta se ejecute. Para asignar a objects la expansión, úsese en su lugar:
objects := $(wildcard *.o)
Véase La función wildcard.
Veamos ahora un ejemplo de una forma ingenua de usar la expansión de comodines que no hace lo que se pretende. Supóngase que se quiere indicar que el archivo ejecutable foo se construye a partir de todos los archivos objeto del directorio, y se escribe esto:
objects = *.o
foo : $(objects)
cc -o foo $(CFLAGS) $(objects)
El valor de objects es la cadena literal «*.o». La expansión de comodines ocurre en la regla de foo, de modo que cada archivo «.o» existente se convierte en un prerrequisito de foo y se recompilará si es necesario.
Pero ¿qué pasa si se borran todos los archivos «.o»? Cuando un comodín no coincide con ningún archivo, se deja tal cual, así que entonces foo dependerá del archivo de nombre extraño *.o. Como es poco probable que tal archivo exista, make dará un error diciendo que no sabe cómo crear *.o. ¡Esto no es lo que se quería!
En realidad es posible obtener el resultado deseado con la expansión de comodines, pero hacen falta técnicas más elaboradas, como la función wildcard y la sustitución de cadenas. Véase La función wildcard.
Los sistemas operativos de Microsoft (MS-DOS y MS-Windows) usan barras invertidas para separar los directorios en las rutas, así:
c:\foo\bar\baz.c
Esto equivale a la forma estilo Unix c:/foo/bar/baz.c (la parte c: es la llamada letra de unidad). Cuando make se ejecuta en estos sistemas, admite tanto las barras invertidas como las barras estilo Unix en las rutas. Sin embargo, esta admisión no alcanza a la expansión de comodines, donde la barra invertida es un carácter de entrecomillado. Por tanto, en estos casos hay que usar obligatoriamente las barras estilo Unix.
wildcard
La expansión de comodines ocurre automáticamente en las reglas. Pero la expansión de comodines normalmente no tiene lugar al asignar una variable ni dentro de los argumentos de una función. Si se quiere hacer expansión de comodines en esos lugares, hay que usar la función wildcard, así:
$(wildcard pattern…)
Esta cadena, usada en cualquier punto de un makefile, se reemplaza por una lista, separada por espacios, de los nombres de los archivos existentes que coinciden con alguno de los patrones de nombre de archivo indicados. Si ningún nombre de archivo existente coincide con un patrón, ese patrón se omite de la salida de la función wildcard. Obsérvese que esto difiere del comportamiento de los comodines no coincidentes en las reglas, donde se usan literalmente en lugar de ignorarse (véase Peligros del uso de comodines).
Como en la expansión de comodines de las reglas, los resultados de la función wildcard se ordenan. Pero, de nuevo, cada expresión individual se ordena por separado, de modo que «$(wildcard *.c *.h)» se expande a todos los archivos que coinciden con «.c» (ordenados), seguidos de todos los archivos que coinciden con «.h» (ordenados).
Un uso de la función wildcard es obtener una lista de todos los archivos fuente C de un directorio, así:
$(wildcard *.c)
Se puede convertir la lista de archivos fuente C en una lista de archivos objeto reemplazando el sufijo «.c» por «.o» en el resultado, así:
$(patsubst %.c,%.o,$(wildcard *.c))
(Aquí hemos usado otra función, patsubst. Véase Funciones para la sustitución y el análisis de cadenas.)
Así, un makefile para compilar todos los archivos fuente C del directorio y enlazarlos podría escribirse así:
objects := $(patsubst %.c,%.o,$(wildcard *.c))
foo : $(objects)
cc -o foo $(objects)
(Esto aprovecha la regla implícita para compilar programas C, así que no hay necesidad de escribir reglas explícitas para compilar los archivos. Véase Los dos tipos de variables para una explicación de «:=», que es una variante de «=».)
En los sistemas grandes suele ser deseable poner las fuentes en un directorio distinto del de los binarios. Las funciones de búsqueda de directorios (directory search) de make facilitan esto buscando automáticamente en varios directorios para encontrar un prerrequisito. Al redistribuir los archivos entre directorios, no hay que cambiar las reglas individuales, solo las rutas de búsqueda.
VPATH: ruta de búsqueda para todos los prerrequisitos
El valor de la variable de make VPATH especifica una lista de directorios en los que make debe buscar. Lo más habitual es que se espere que esos directorios contengan archivos de prerrequisito que no están en el directorio actual; no obstante, make usa VPATH como lista de búsqueda tanto para los prerrequisitos como para los objetivos de las reglas.
Así, si un archivo que figura como objetivo o prerrequisito no existe en el directorio actual, make busca en los directorios enumerados en VPATH un archivo con ese nombre. Si se encuentra en alguno de ellos, ese archivo puede convertirse en el prerrequisito (véase más abajo). Las reglas pueden entonces especificar los nombres de los archivos en la lista de prerrequisitos como si todos existieran en el directorio actual. Véase Escritura de recetas con búsqueda de directorios.
En la variable VPATH, los nombres de directorio se separan por dos puntos o por espacios en blanco. El orden en que se enumeran los directorios es el orden en que make realiza la búsqueda. (En MS-DOS y MS-Windows se usan puntos y comas como separadores de los nombres de directorio en VPATH, ya que los dos puntos pueden usarse en la propia ruta, tras la letra de unidad.)
Por ejemplo,
VPATH = src:../headers
especifica una ruta que contiene dos directorios, src y ../headers, en los que make busca en ese orden.
Con este valor de VPATH, la siguiente regla,
foo.o : foo.c
se interpreta como si se hubiera escrito así:
foo.o : src/foo.c
suponiendo que el archivo foo.c no existe en el directorio actual pero se encuentra en el directorio src.
vpath
Similar a la variable VPATH, pero más selectiva, es la directiva vpath (obsérvese que va en minúsculas), que permite especificar una ruta de búsqueda para una clase concreta de nombres de archivo: los que coinciden con un patrón determinado. Así se pueden dar ciertos directorios de búsqueda a una clase de nombres de archivo y otros directorios (o ninguno) a otros nombres de archivo.
Hay tres formas de la directiva vpath:
vpath pattern directoriesEspecifica la ruta de búsqueda directories para los nombres de archivo que coinciden con pattern.
La ruta de búsqueda, directories, es una lista de directorios en los que buscar, separados por dos puntos (puntos y comas en MS-DOS y MS-Windows) o por espacios en blanco, igual que la ruta de búsqueda usada en la variable VPATH.
vpath patternBorra la ruta de búsqueda asociada a pattern.
vpath
Borra todas las rutas de búsqueda especificadas previamente con directivas vpath.
Un patrón vpath es una cadena que contiene un carácter «%». La cadena debe coincidir con el nombre de archivo del prerrequisito que se está buscando; el carácter «%» coincide con cualquier secuencia de cero o más caracteres (como en las reglas de patrón; véase Definición y redefinición de reglas de patrón). Por ejemplo, %.h coincide con los archivos que terminan en .h. (Si no hay «%», el patrón debe coincidir exactamente con el prerrequisito, lo cual no suele ser muy útil.)
Los caracteres «%» del patrón de una directiva vpath pueden entrecomillarse con barras invertidas precedentes («\»). Las barras invertidas que de otro modo entrecomillarían caracteres «%» pueden entrecomillarse con más barras invertidas. Las barras invertidas que entrecomillan caracteres «%» u otras barras invertidas se eliminan del patrón antes de compararlo con los nombres de archivo. Las barras invertidas que no corren peligro de entrecomillar caracteres «%» se dejan intactas.
Cuando un prerrequisito no existe en el directorio actual, si el pattern de una directiva vpath coincide con el nombre del archivo de prerrequisito, los directories de esa directiva se buscan igual que (y antes de) los directorios de la variable VPATH.
Por ejemplo,
vpath %.h ../headers
indica a make que busque cualquier prerrequisito cuyo nombre termine en .h en el directorio ../headers si el archivo no se encuentra en el directorio actual.
Si varios patrones vpath coinciden con el nombre del archivo de prerrequisito, make procesa cada directiva vpath coincidente una por una, buscando en todos los directorios mencionados en cada directiva. make gestiona las múltiples directivas vpath en el orden en que aparecen en el makefile; varias directivas con el mismo patrón son independientes entre sí.
Así,
vpath %.c foo vpath % blish vpath %.c bar
buscará un archivo terminado en «.c» en foo, luego en blish y luego en bar, mientras que
vpath %.c foo:bar vpath % blish
buscará un archivo terminado en «.c» en foo, luego en bar y luego en blish.
Cuando se encuentra un prerrequisito mediante la búsqueda de directorios, sea del tipo que sea (general o selectiva), la ruta localizada puede no ser la que make le proporciona realmente en la lista de prerrequisitos. A veces la ruta descubierta mediante la búsqueda de directorios se descarta.
El algoritmo que make usa para decidir si conservar o abandonar una ruta encontrada mediante la búsqueda de directorios es el siguiente:
make no necesita reconstruir el objetivo, se usa la ruta encontrada mediante la búsqueda de directorios.
make debe reconstruir, el objetivo se reconstruye localmente, no en el directorio encontrado mediante la búsqueda de directorios.
Este algoritmo puede parecer complejo, pero en la práctica es a menudo exactamente lo que se quiere.
Otras versiones de make usan un algoritmo más simple: si el archivo no existe y se encuentra mediante la búsqueda de directorios, esa ruta se usa siempre, necesite o no reconstruirse el objetivo. Así, si el objetivo se reconstruye, se crea en la ruta descubierta durante la búsqueda de directorios.
Si, de hecho, este es el comportamiento que se desea para algunos o todos los directorios, se puede usar la variable GPATH para indicárselo a make.
GPATH tiene la misma sintaxis y el mismo formato que VPATH (es decir, una lista de rutas delimitada por espacios o por dos puntos). Si un objetivo desactualizado se encuentra mediante la búsqueda de directorios en un directorio que también aparece en GPATH, esa ruta no se descarta. El objetivo se reconstruye usando la ruta expandida.
Cuando un prerrequisito se encuentra en otro directorio mediante la búsqueda de directorios, eso no puede cambiar la receta de la regla; se ejecutará tal como está escrita. Por tanto, hay que escribir la receta con cuidado para que busque el prerrequisito en el directorio donde make lo encuentra.
Esto se hace con las variables automáticas, como «$^» (véase Variables automáticas). Por ejemplo, el valor de «$^» es una lista de todos los prerrequisitos de la regla, incluidos los nombres de los directorios en los que se encontraron, y el valor de «$@» es el objetivo. Así:
foo.o : foo.c
cc -c $(CFLAGS) $^ -o $@
(La variable CFLAGS existe para poder especificar opciones para la compilación de C mediante las reglas implícitas; aquí la usamos por coherencia, de modo que afecte uniformemente a todas las compilaciones de C; véase Variables usadas por las reglas implícitas.)
A menudo los prerrequisitos incluyen también archivos de cabecera, que no se quieren mencionar en la receta. La variable automática «$<» es justamente el primer prerrequisito:
VPATH = src:../headers
foo.o : foo.c defs.h hack.h
cc -c $(CFLAGS) $< -o $@
La búsqueda en los directorios especificados con VPATH o con vpath también ocurre durante la consideración de las reglas implícitas (véase Uso de las reglas implícitas).
Por ejemplo, cuando un archivo foo.o no tiene una regla explícita, make considera las reglas implícitas, como la regla incorporada para compilar foo.c si ese archivo existe. Si dicho archivo falta en el directorio actual, se buscan los directorios apropiados. Si foo.c existe (o se menciona en el makefile) en alguno de esos directorios, se aplica la regla implícita para la compilación de C.
Las recetas de las reglas implícitas usan normalmente variables automáticas por necesidad; en consecuencia, usarán los nombres de archivo encontrados por la búsqueda de directorios sin esfuerzo adicional.
La búsqueda de directorios se aplica de una forma especial a las bibliotecas usadas con el enlazador. Esta función especial entra en juego cuando se escribe un prerrequisito cuyo nombre tiene la forma «-lname». (Se nota que aquí ocurre algo extraño porque el prerrequisito es normalmente el nombre de un archivo, mientras que el nombre de archivo de una biblioteca suele tener el aspecto libname.a, no «-lname».)
Cuando el nombre de un prerrequisito tiene la forma «-lname», make lo trata de forma especial buscando el archivo libname.so y, si no lo encuentra, el archivo libname.a, en el directorio actual, en los directorios especificados por las rutas de búsqueda vpath coincidentes y la ruta de búsqueda VPATH, y luego en los directorios /lib, /usr/lib y prefix/lib (normalmente /usr/local/lib, pero las versiones de make para MS-DOS/MS-Windows se comportan como si prefix estuviera definido como la raíz del árbol de instalación de DJGPP).
Por ejemplo, si hay una biblioteca /usr/lib/libcurses.a en el sistema (y no hay un archivo /usr/lib/libcurses.so), entonces
foo : foo.c -lcurses
cc $^ -o $@
haría que se ejecutara la orden «cc foo.c /usr/lib/libcurses.a -o foo» cuando foo es más antiguo que foo.c o que /usr/lib/libcurses.a.
Aunque el conjunto predeterminado de archivos que se buscan es libname.so y libname.a, esto es personalizable mediante la variable .LIBPATTERNS. Cada palabra del valor de esta variable es una cadena de patrón. Cuando se ve un prerrequisito como «-lname», make reemplaza el signo de porcentaje de cada patrón de la lista por name y realiza las búsquedas de directorios anteriores usando cada nombre de archivo de biblioteca.
El valor predeterminado de .LIBPATTERNS es «lib%.so lib%.a», que proporciona el comportamiento predeterminado descrito antes.
Se puede desactivar por completo la expansión de bibliotecas de enlace asignando a esta variable un valor vacío.
Un objetivo ficticio (phony target) es uno que no es realmente el nombre de un archivo; más bien es solo un nombre para una receta que se ejecuta cuando se hace una petición explícita. Hay dos razones para usar un objetivo ficticio: evitar un conflicto con un archivo del mismo nombre y mejorar el rendimiento.
Si se escribe una regla cuya receta no crea el archivo de objetivo, la receta se ejecutará cada vez que el objetivo se considere para rehacerlo. He aquí un ejemplo:
clean:
rm *.o temp
Como la orden rm no crea un archivo llamado clean, probablemente nunca exista tal archivo. Por tanto, la orden rm se ejecutará cada vez que se diga «make clean».
En este ejemplo, el objetivo clean no funcionará correctamente si alguna vez se crea un archivo llamado clean en este directorio. Como no tiene prerrequisitos, clean siempre se consideraría actualizado y su receta no se ejecutaría. Para evitar este problema se puede declarar explícitamente el objetivo como ficticio haciéndolo prerrequisito del objetivo especial .PHONY (véase Nombres de objetivos incorporados especiales), así:
.PHONY: clean
clean:
rm *.o temp
Hecho esto, «make clean» ejecutará la receta exista o no un archivo llamado clean.
Los prerrequisitos de .PHONY se interpretan siempre como nombres de objetivo literales, nunca como patrones (aunque contengan caracteres «%»). Para reconstruir siempre una regla de patrón, considérese usar un «objetivo de forzado» (force target) (véase Reglas sin recetas ni prerrequisitos).
Los objetivos ficticios también son útiles junto con las invocaciones recursivas de make (véase Uso recursivo de make). En esta situación, el makefile suele contener una variable que enumera una serie de subdirectorios que hay que construir. Una manera simplista de gestionarlo es definir una única regla con una receta que recorra en bucle los subdirectorios, así:
SUBDIRS = foo bar baz
subdirs:
for dir in $(SUBDIRS); do \
$(MAKE) -C $$dir; \
done
Sin embargo, este método tiene problemas. Primero, cualquier error detectado en un sub-make es ignorado por esta regla, así que seguirá construyendo el resto de los directorios aunque uno falle. Esto puede subsanarse añadiendo órdenes de shell que registren el error y salgan, pero entonces lo hará incluso si make se invoca con la opción -k, lo cual es inoportuno. Segundo, y quizá más importante, no se puede aprovechar al máximo la capacidad de make de construir objetivos en paralelo (véase Ejecución en paralelo), ya que hay una sola regla. Los objetivos de cada makefile individual se construirán en paralelo, pero solo se construirá un subdirectorio a la vez.
Declarando los subdirectorios como objetivos .PHONY (algo que hay que hacer, pues el subdirectorio obviamente existe siempre; de lo contrario no se construiría) se pueden eliminar estos problemas:
SUBDIRS = foo bar baz
.PHONY: subdirs $(SUBDIRS)
subdirs: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $@
foo: baz
Aquí también hemos declarado que el subdirectorio foo no puede construirse hasta que se complete el subdirectorio baz; este tipo de declaración de relación es especialmente importante al intentar construcciones en paralelo.
La búsqueda de reglas implícitas (véase Uso de las reglas implícitas) se omite para los objetivos .PHONY. Por eso declarar un objetivo como .PHONY es bueno para el rendimiento, aunque no preocupe la existencia real del archivo.
Un objetivo ficticio no debería ser prerrequisito de un archivo de objetivo real; si lo es, su receta se ejecutará cada vez que make considere ese archivo. Mientras un objetivo ficticio nunca sea prerrequisito de un objetivo real, la receta del objetivo ficticio se ejecutará solo cuando el objetivo ficticio sea una meta especificada (véase Argumentos para especificar las metas).
No se debe declarar como ficticio un makefile incluido. Los objetivos ficticios no pretenden representar archivos reales y, como el objetivo siempre se considera desactualizado, make siempre lo reconstruirá y luego se reejecutará a sí mismo (véase Cómo se rehacen los makefiles). Para evitarlo, make no se reejecuta a sí mismo si se reconstruye un archivo incluido marcado como ficticio.
Los objetivos ficticios pueden tener prerrequisitos. Cuando un directorio contiene varios programas, lo más cómodo es describir todos los programas en un solo makefile ./Makefile. Como el objetivo rehecho de forma predeterminada será el primero del makefile, es habitual hacer de este un objetivo ficticio llamado «all» y darle, como prerrequisitos, todos los programas individuales. Por ejemplo:
all : prog1 prog2 prog3
.PHONY : all
prog1 : prog1.o utils.o
cc -o prog1 prog1.o utils.o
prog2 : prog2.o
cc -o prog2 prog2.o
prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o
Ahora se puede decir simplemente «make» para rehacer los tres programas, o especificar como argumentos los que se quieran rehacer (como en «make prog1 prog3»). La condición de ficticio no se hereda: los prerrequisitos de un objetivo ficticio no se vuelven ficticios por sí mismos, a menos que se declaren explícitamente así.
Cuando un objetivo ficticio es prerrequisito de otro objetivo ficticio, actúa como una subrutina de este último. Por ejemplo, en el siguiente caso «make cleanall» borrará los archivos objeto, los archivos de diferencias y el archivo program:
.PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff
Si una regla no tiene ni prerrequisitos ni receta, y además el objetivo de la regla es un archivo que no existe, make considera que este objetivo se ha actualizado cada vez que se ejecuta la regla. Esto significa que todos los objetivos que dependen de él ejecutarán siempre su receta.
Un ejemplo lo aclarará:
clean: FORCE
rm $(objects)
FORCE:
Aquí el objetivo «FORCE» cumple la condición especial anterior, de modo que el objetivo clean que depende de él se ve forzado a ejecutar su receta. El nombre «FORCE» en sí no tiene ningún significado especial, pero es uno de los nombres usados habitualmente para este fin.
Como puede verse, usar «FORCE» de esta manera tiene el mismo resultado que usar «.PHONY: clean».
Usar «.PHONY» es más explícito y más eficiente. No obstante, otras versiones de make no admiten «.PHONY». Por eso «FORCE» aparece en muchos makefiles. Véase Objetivos ficticios (phony).
El objetivo vacío (empty target) es una variante del objetivo ficticio que se usa para guardar la receta de una acción que se solicita explícitamente de vez en cuando. A diferencia de los objetivos ficticios, este archivo de objetivo sí puede existir realmente; pero el contenido del archivo no importa y por lo general está vacío.
El propósito del archivo de objetivo vacío es registrar, mediante su hora de última modificación, el momento en que se ejecutó por última vez la receta de la regla. Esto es posible porque una de las órdenes de la receta es una orden touch que actualiza el archivo de objetivo.
El archivo de objetivo vacío debería tener algún prerrequisito (de lo contrario no tiene sentido). Cuando se pide rehacer el objetivo vacío, la receta se ejecuta si alguno de los prerrequisitos es más reciente que el objetivo; en otras palabras, si algún prerrequisito ha cambiado desde la última vez que se rehízo el objetivo. He aquí un ejemplo:
print: foo.c bar.c
lpr -p $?
touch print
Con esta regla, «make print» ejecutará la orden lpr si alguno de los dos archivos fuente ha cambiado desde el último «make print». Se usa la variable automática «$?» para imprimir solo los archivos que han cambiado (véase Variables automáticas).
Ciertos nombres tienen un significado especial cuando aparecen como objetivos.
.PHONY
Los prerrequisitos del objetivo especial .PHONY se consideran objetivos ficticios. Cuando uno de esos objetivos pasa a considerarse, make ejecuta su receta de forma incondicional, con independencia de que exista un archivo con ese nombre o de cuál sea su hora de última modificación. Véase Objetivos ficticios (phony).
.SUFFIXES
Los prerrequisitos del objetivo especial .SUFFIXES son la lista de sufijos que se usan al examinar las reglas de sufijo. Véase Reglas de sufijo anticuadas.
.DEFAULT
La receta especificada para .DEFAULT se usa con todos los objetivos para los que no se encuentra ninguna regla (ni explícita ni implícita). Véase Definición de reglas predeterminadas como último recurso. Si se especifica una receta de .DEFAULT, esa receta se ejecuta en su lugar para todos los archivos que se mencionan como prerrequisitos en las reglas pero no como objetivos. Véase El algoritmo de búsqueda de reglas implícitas.
.PRECIOUS
A los objetivos enumerados como prerrequisitos de .PRECIOUS se les da el siguiente trato especial: si make se mata o se interrumpe durante la ejecución de la receta, esos objetivos no se borran. Véase Interrupción o terminación de make. Además, si el objetivo es un archivo intermedio, que normalmente se borra cuando deja de ser necesario, tampoco se realiza ese borrado. Véase Encadenamiento de reglas implícitas. En este último aspecto, su función se solapa con la del objetivo especial .SECONDARY.
También se pueden enumerar patrones de objetivo de regla implícita (como «%.o») como archivos de prerrequisito del objetivo especial .PRECIOUS. Así se preservan los archivos intermedios que crean las reglas cuyos objetivos tienen nombres que coinciden con ese patrón.
.INTERMEDIATE
Los objetivos de los que depende .INTERMEDIATE se tratan como archivos intermedios. Véase Encadenamiento de reglas implícitas. Un .INTERMEDIATE sin prerrequisitos no tiene efecto.
.NOTINTERMEDIATE
Los prerrequisitos del objetivo especial .NOTINTERMEDIATE nunca se consideran archivos intermedios. Véase Encadenamiento de reglas implícitas. Un .NOTINTERMEDIATE sin prerrequisitos hace que todos los objetivos se traten como no intermedios.
Si el prerrequisito es un patrón de objetivo, los objetivos construidos con esa regla de patrón dejan de considerarse archivos intermedios.
.SECONDARY
Los objetivos de los que depende .SECONDARY se tratan como archivos intermedios, salvo que nunca se borran automáticamente. Véase Encadenamiento de reglas implícitas.
.SECONDARY puede usarse para evitar reconstrucciones redundantes en algunas situaciones poco frecuentes. Por ejemplo:
hello.bin: hello.o bye.o
$(CC) -o $@ $^
%.o: %.c
$(CC) -c -o $@ $<
.SECONDARY: hello.o bye.o
Considérese una situación en la que hello.bin está actualizado respecto a los archivos fuente, pero el archivo objeto hello.o no se encuentra. Sin .SECONDARY, make reconstruiría hello.o aunque los archivos fuente no hayan cambiado y, a continuación, también reconstruiría hello.bin. Declarando hello.o como .SECONDARY, make ya no necesita reconstruirlo, y tampoco necesita reconstruir hello.bin. Por supuesto, si alguno de los archivos fuente se actualiza, todos los archivos objeto se reconstruyen para que la generación de hello.bin tenga éxito.
Un .SECONDARY sin prerrequisitos hace que todos los objetivos se traten como secundarios (es decir, ningún objetivo se borra por haberse considerado un archivo intermedio).
.SECONDEXPANSION
Si .SECONDEXPANSION se menciona como objetivo en algún punto del makefile, todas las listas de prerrequisitos definidas después de su aparición se expanden una segunda vez tras terminar de leer todos los makefiles. Véase Expansión secundaria.
.DELETE_ON_ERROR
Si .DELETE_ON_ERROR se menciona como objetivo en algún punto del makefile, make borra el objetivo de una regla cuando este ha cambiado y su receta termina con un estado de salida distinto de cero, igual que cuando se recibe una señal. Véase Errores en las recetas.
.IGNORE
Si se especifican prerrequisitos para .IGNORE, make ignora los errores al ejecutar las recetas de esos archivos concretos. La receta de .IGNORE (si la hay) se ignora.
Si se menciona como objetivo sin prerrequisitos, .IGNORE indica que se ignoren los errores al ejecutar las recetas de todos los archivos. Este uso de «.IGNORE» se admite solo por compatibilidad histórica. Como afecta a todas las recetas del makefile, no es muy útil; es preferible usar un método más selectivo para ignorar los errores de recetas concretas. Véase Errores en las recetas.
.LOW_RESOLUTION_TIME
Si se especifican prerrequisitos para .LOW_RESOLUTION_TIME, make considera que esos archivos los han creado órdenes que generan marcas de tiempo de baja resolución. La receta de un objetivo .LOW_RESOLUTION_TIME se ignora.
Las marcas de tiempo de archivo de alta resolución que tienen muchos sistemas de archivos modernos reducen la posibilidad de que make juzgue erróneamente que un archivo está actualizado. Por desgracia, algunos hosts no ofrecen un medio para establecer marcas de tiempo de archivo de alta resolución, de modo que órdenes como «cp -p», que establecen explícitamente la marca de tiempo de un archivo, se ven obligadas a descartar su parte por debajo del segundo. Los archivos creados por tales órdenes deberían enumerarse como prerrequisitos de .LOW_RESOLUTION_TIME para que make no juzgue erróneamente que están desactualizados. Por ejemplo:
.LOW_RESOLUTION_TIME: dst
dst: src
cp -p src dst
Como «cp -p» descarta la parte por debajo del segundo de la marca de tiempo de src, dst, aun estando actualizado, suele quedar ligeramente más antiguo que src. Con la línea .LOW_RESOLUTION_TIME, make pasa a considerar dst actualizado cuando la marca de tiempo de dst está al comienzo del mismo segundo que la de src.
Por las limitaciones del formato de archivo de archivo, las marcas de tiempo de los miembros de archivo de archivo son siempre de baja resolución. No es necesario enumerar los miembros de archivo de archivo como prerrequisitos de .LOW_RESOLUTION_TIME, ya que make lo hace automáticamente.
.SILENT
Si se especifican prerrequisitos para .SILENT, make deja de mostrar, antes de ejecutarlas, las recetas que usa para rehacer esos archivos concretos. La receta de .SILENT se ignora.
Si se menciona como objetivo sin prerrequisitos, .SILENT indica que no se muestre nada antes de ejecutar las recetas. También se puede usar un método más selectivo para silenciar solo determinadas líneas de orden de una receta. Véase Eco de las recetas. Si se quieren silenciar todas las recetas de una ejecución concreta de make, úsense las opciones «-s» o «--silent» (véase Resumen de opciones).
.EXPORT_ALL_VARIABLES
Con solo mencionarse como objetivo, esto indica a make que exporte de forma predeterminada todas las variables a los procesos hijos. Es una alternativa al uso de export sin argumentos. Véase Comunicación de variables a un sub-make.
.NOTPARALLEL
Si .NOTPARALLEL se menciona como objetivo sin prerrequisitos, todos los objetivos de esta invocación de make se ejecutan en serie, aunque se haya dado la opción «-j». Las órdenes de make invocadas recursivamente siguen ejecutando sus recetas en paralelo (salvo que su propio makefile también contenga este objetivo).
Si .NOTPARALLEL tiene objetivos como prerrequisitos, todos los prerrequisitos de esos objetivos se ejecutan en serie. Esto añade implícitamente un .WAIT entre cada prerrequisito de los objetivos enumerados. Véase Desactivación de la ejecución en paralelo.
.ONESHELL
Si .ONESHELL se menciona como objetivo, al construir el objetivo todas las líneas de la receta se pasan a una única invocación del shell, en lugar de invocar cada línea por separado. Véase Ejecución de las recetas.
.POSIX
Si .POSIX se menciona como objetivo, el makefile se analiza y ejecuta en modo conforme a POSIX. Esto no significa que solo se acepten makefiles conformes a POSIX: todas las funciones avanzadas de GNU make siguen disponibles. Este objetivo hace que make se comporte como exige POSIX en aquellos puntos en que el comportamiento predeterminado de make difiere de POSIX.
En particular, si se menciona este objetivo, las recetas se invocan como si se hubiera pasado al shell el indicador -e: es decir, la primera orden que falle en una receta hará que esta falle de inmediato.
Los sufijos de las reglas implícitas predefinidas también se consideran objetivos especiales cuando aparecen como objetivos, al igual que la concatenación de dos sufijos, como «.c.o». Estos objetivos son reglas de sufijo, una forma anticuada (aunque todavía muy usada) de definir reglas implícitas. En principio, cualquier nombre de objetivo puede adquirir un significado especial de esta manera, partiéndolo en dos y añadiendo ambos fragmentos a la lista de sufijos. En la práctica, los sufijos suelen empezar por «.», de modo que estos nombres de objetivo especiales también empiezan por «.». Véase Reglas de sufijo anticuadas.
Cuando una regla explícita tiene varios objetivos, estos se tratan de una de dos maneras: como objetivos independientes o como objetivos agrupados. Cuál de los dos tratamientos se aplica lo determina el separador que aparece tras la lista de objetivos.
Una regla que usa el separador de objetivos estándar «:» define objetivos independientes. Equivale a escribir la misma regla una vez por cada objetivo, duplicando los prerrequisitos y la receta. Por lo general, la receta usa variables automáticas como «$@» para indicar qué objetivo se está construyendo.
Las reglas con objetivos independientes son útiles en dos casos:
kbd.o command.o files.o: command.h
da un prerrequisito adicional a cada uno de los tres archivos objeto enumerados. Equivale a escribir:
kbd.o: command.h command.o: command.h files.o: command.h
bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) > $@
equivale a:
bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput
Aquí se supone que el programa hipotético generate produce dos tipos de salida según se le dé «-big» o «-little». Para una explicación de la función subst, véase Funciones para la sustitución y el análisis de cadenas.
Por cierto, igual que la variable «$@» permite variar la receta, a veces se quieren variar los prerrequisitos según el objetivo. Esto no puede hacerse con varios objetivos en una regla ordinaria, pero sí con las reglas de patrón estáticas. Véase Reglas de patrón estáticas.
Si, en vez de objetivos independientes, se tiene una receta que genera varios archivos en una sola invocación, esa relación puede expresarse declarando que la regla usa objetivos agrupados (grouped targets). Una regla con objetivos agrupados usa el separador «&:» (donde «&» se usa para sugerir «todos»).
Cuando make construye uno cualquiera de los objetivos agrupados, entiende que, como resultado de la invocación de la receta, también se actualizan todos los demás objetivos del grupo. Además, make reconoce que, aunque solo una parte de los objetivos agrupados esté desactualizada o falte, ejecutar la receta actualizará todos los objetivos. Por último, si cualquiera de los objetivos agrupados está desactualizado, todos los objetivos agrupados se consideran desactualizados.
A modo de ejemplo, la siguiente regla define objetivos agrupados:
foo bar biz &: baz boz
echo $^ > foo
echo $^ > bar
echo $^ > biz
Durante la ejecución de la receta de objetivos agrupados, la variable automática «$@» se establece al nombre del objetivo concreto del grupo que provocó la activación de la regla. Hay que tener cuidado si se confía en esta variable en la receta de una regla con objetivos agrupados.
A diferencia de los objetivos independientes, una regla con objetivos agrupados debe incluir necesariamente una receta. No obstante, un objetivo que sea miembro de un conjunto de objetivos agrupados puede aparecer también en definiciones de reglas de objetivos independientes que no tengan receta.
A cada objetivo se le asocia una única receta. Si un objetivo agrupado aparece en una regla de objetivos independientes, o en otra regla de objetivos agrupados con receta, se emite una advertencia y la última receta reemplaza a la primera. Además, ese objetivo se elimina del grupo anterior y pasa a aparecer solo en el nuevo grupo.
Si se quiere que un objetivo aparezca en varios grupos, hay que usar el separador de objetivos agrupados de doble dos puntos «&::» al declarar todos los grupos que contienen ese objetivo. Los objetivos agrupados de doble dos puntos se tratan cada uno de forma independiente, y la receta de cada regla de objetivos agrupados de doble dos puntos se ejecuta a lo sumo una vez si al menos uno de sus objetivos necesita actualizarse.
Un archivo puede ser el objetivo de varias reglas. Todos los prerrequisitos mencionados en todas las reglas se reúnen en una única lista de prerrequisitos del objetivo. Si el objetivo es más antiguo que cualquier prerrequisito de cualquiera de las reglas, se ejecuta la receta.
Solo puede haber una receta que se ejecute para un archivo dado. Si dos o más reglas dan recetas para el mismo archivo, make usa la última dada y muestra un mensaje de error. (Como caso especial, si el nombre del archivo empieza por un punto, no se muestra el mensaje de error. Este comportamiento extraño existe solo por compatibilidad con otras implementaciones de make, así que es mejor evitar usarlo.) A veces se quieren activar varias recetas, definidas en distintos puntos del makefile, para el mismo objetivo. Para ello se pueden usar las reglas de doble dos puntos (véase Reglas de doble dos puntos).
Con una regla adicional que solo tenga prerrequisitos se pueden dar a la vez algunos prerrequisitos adicionales a muchos archivos. Por ejemplo, los makefiles suelen tener una variable, como objects, con la lista de todos los archivos de salida del compilador del sistema que se construye. Una forma cómoda de indicar que todos ellos deben recompilarse si cambia config.h es escribir lo siguiente:
objects = foo.o bar.o foo.o : defs.h bar.o : defs.h test.h $(objects) : config.h
Esto puede insertarse o quitarse sin alterar las reglas que realmente especifican cómo crear los archivos objeto. Es una forma cómoda cuando se quieren añadir prerrequisitos adicionales de forma intermitente.
Otra técnica consiste en especificar los prerrequisitos adicionales con una variable que se establezca con un argumento de la línea de órdenes de make (véase Anulación de variables). Por ejemplo,
extradeps= $(objects) : $(extradeps)
hace que la orden «make extradeps=foo.h» considere foo.h como prerrequisito de cada archivo objeto, mientras que un simple «make» no lo hace.
Si ninguna de las reglas explícitas de un objetivo tiene receta, make busca una regla implícita aplicable que pueda servir (véase Uso de las reglas implícitas).
Las reglas de patrón estáticas (static pattern rules) son reglas que especifican varios objetivos y construyen el nombre de los prerrequisitos de cada objetivo a partir del nombre del objetivo. Son más generales que las reglas ordinarias con varios objetivos, porque los objetivos no tienen por qué tener prerrequisitos idénticos. Sus prerrequisitos deben ser análogos, pero no necesariamente idénticos.
La sintaxis de una regla de patrón estática es la siguiente:
targets …: target-pattern: prereq-patterns …
recipe
…
La lista de targets especifica los objetivos a los que se aplica la regla. Los objetivos pueden contener comodines, igual que los objetivos de una regla ordinaria (véase Uso de comodines en los nombres de archivo).
El target-pattern y los prereq-patterns describen cómo calcular los prerrequisitos de cada objetivo. Cada objetivo se confronta con el target-pattern y se extrae una parte del nombre del objetivo, llamada la raíz (stem). Esta raíz se sustituye en cada prereq-pattern para formar los nombres de los prerrequisitos (uno por cada prereq-pattern).
Cada patrón contiene normalmente un único carácter «%». Cuando el target-pattern coincide con un objetivo, el «%» puede coincidir con cualquier parte del nombre del objetivo; a esta parte se la llama la raíz. El resto del patrón debe coincidir exactamente. Por ejemplo, el objetivo foo.o coincide con el patrón «%.o», con «foo» como raíz. Los objetivos foo.c y foo.out no coinciden con este patrón.
El nombre del prerrequisito de cada objetivo se forma reemplazando el «%» de cada patrón de prerrequisito por la raíz. Por ejemplo, si un patrón de prerrequisito es %.c, al sustituir la raíz «foo» se obtiene el nombre de prerrequisito foo.c. También se admite escribir un patrón de prerrequisito que no contenga «%»; en ese caso, este prerrequisito es el mismo para todos los objetivos.
Los caracteres «%» de las reglas de patrón pueden entrecomillarse con barras invertidas precedentes («\»). Las barras invertidas que de otro modo entrecomillarían caracteres «%» pueden entrecomillarse con más barras invertidas. Las barras invertidas que entrecomillan caracteres «%» u otras barras invertidas se eliminan antes de comparar el patrón con los nombres de archivo o de sustituir la raíz. Las barras invertidas que no corren peligro de entrecomillar caracteres «%» se dejan intactas. Por ejemplo, en el patrón the\%weird\\%pattern\\, antes del «%» que tiene efecto está «the%weird\», seguido de «pattern\\». Las dos barras invertidas finales se dejan intactas porque no afectan a ningún carácter «%».
A continuación, un ejemplo que compila cada uno de foo.o y bar.o a partir del archivo .c correspondiente:
objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
Aquí «$<» es la variable automática que contiene el nombre del prerrequisito y «$@» es la variable automática que contiene el nombre del objetivo. Véase Variables automáticas.
Cada objetivo especificado debe coincidir con el patrón de objetivo. Por cada objetivo que no coincide se emite una advertencia. Si se tiene una lista de archivos de los que solo algunos coinciden con el patrón, se puede usar la función filter para quitar los nombres de archivo que no coinciden (véase Funciones para la sustitución y el análisis de cadenas):
files = foo.elc bar.o lose.o
$(filter %.o,$(files)): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc,$(files)): %.elc: %.el
emacs -f batch-byte-compile $<
En este ejemplo, el resultado de «$(filter %.o,$(files))» es bar.o lose.o, y la primera regla de patrón estática actualiza cada uno de esos archivos objeto compilando el archivo fuente C correspondiente. El resultado de «$(filter %.elc,$(files))» es foo.elc, así que ese archivo se crea a partir de foo.el.
Otro ejemplo muestra cómo usar $* en una regla de patrón estática:
bigoutput littleoutput : %output : text.g
generate text.g -$* > $@
Cuando se ejecuta la orden generate, $* se expande a la raíz, «big» o «little».
Las reglas de patrón estáticas tienen mucho en común con las reglas implícitas definidas como reglas de patrón (véase Definición y redefinición de reglas de patrón). Ambas tienen un patrón para el objetivo y patrones para construir los nombres de los prerrequisitos. La diferencia está en cómo decide make cuándo se aplica la regla.
Una regla implícita puede aplicarse a cualquier objetivo que coincida con su patrón, pero solo se aplica cuando el objetivo no tiene receta especificada por otro medio y se pueden encontrar los prerrequisitos. Si parecen aplicables varias reglas implícitas, solo se aplica una; cuál se elige depende del orden de las reglas.
En cambio, una regla de patrón estática se aplica a la lista exacta de objetivos especificada en la regla. No puede aplicarse a ningún otro objetivo y se aplica necesariamente a cada uno de los objetivos especificados. Si se aplican dos reglas en conflicto, ambas con receta, es un error.
Puede decirse que las reglas de patrón estáticas son mejores que las reglas implícitas por las siguientes razones:
make use una regla implícita equivocada. Cuál se elige puede depender del orden en que se realice la búsqueda de reglas implícitas. Con las reglas de patrón estáticas no hay incertidumbre: cada regla se aplica exactamente a los objetivos especificados.
Las reglas de doble dos puntos (double-colon) son reglas explícitas que se escriben con «::» en lugar de «:» tras el nombre del objetivo. Reciben un trato distinto al de las reglas ordinarias cuando el mismo objetivo aparece en varias reglas. Las reglas de patrón con doble dos puntos tienen un significado totalmente distinto (véase Reglas de patrón que coinciden con cualquier cosa).
Cuando un objetivo aparece en varias reglas, todas esas reglas deben ser del mismo tipo: todas ordinarias o todas de doble dos puntos. En el caso de doble dos puntos, cada una es independiente de las demás. La receta de cada regla de doble dos puntos se ejecuta si el objetivo es más antiguo que cualquier prerrequisito de esa regla. Si la regla no tiene prerrequisitos, su receta se ejecuta siempre (aunque el objetivo ya exista). Como consecuencia, de las reglas de doble dos puntos puede no ejecutarse ninguna, ejecutarse solo algunas o ejecutarse todas.
Las reglas de doble dos puntos con el mismo objetivo están, de hecho, completamente separadas entre sí. Cada regla de doble dos puntos se procesa por separado, exactamente igual que se procesan las reglas con objetivos distintos.
Las reglas de doble dos puntos de un objetivo se ejecutan en el orden en que aparecen en el makefile. Con todo, las reglas de doble dos puntos solo tienen verdadero sentido cuando el orden de ejecución de las recetas no importa.
Las reglas de doble dos puntos son algo oscuras y no suelen ser muy útiles. Ofrecen un mecanismo para los casos en que la forma de actualizar un objetivo difiere según cuál de los archivos de prerrequisito haya provocado la actualización, pero esos casos son raros.
Cada regla de doble dos puntos debería especificar una receta. Si no lo hace, se usará una regla implícita aplicable si la hay. Véase Uso de las reglas implícitas.
En el makefile de un programa, muchas de las reglas que hay que escribir suelen limitarse a indicar que un archivo objeto depende de algún archivo de cabecera. Por ejemplo, si main.c usa defs.h mediante #include, se escribiría:
main.o: defs.h
Esta regla es necesaria para informar a make de que hay que rehacer main.o cada vez que cambie defs.h. Se ve que, en un programa grande, habría que escribir decenas de reglas así en el makefile. Y, además, hay que tener siempre sumo cuidado de actualizar el makefile cada vez que se añade o elimina un #include.
Para evitar esta molestia, la mayoría de los compiladores de C modernos miran las líneas #include de los archivos fuente y escriben estas reglas en su lugar. Normalmente lo hacen con la opción «-M» del compilador. Por ejemplo, la orden:
cc -M main.c
genera la salida:
main.o : main.c defs.h
Así, ya no hace falta escribir todas esas reglas a mano: el compilador lo hace en su lugar.
Téngase en cuenta que, como una regla así menciona main.o en el makefile, la búsqueda de reglas implícitas nunca lo considerará un archivo intermedio. Esto significa que make nunca borrará ese archivo tras usarlo. Véase Encadenamiento de reglas implícitas.
Con los programas make antiguos, la forma tradicional de aprovechar esta función del compilador era generar los prerrequisitos cada vez con una orden como «make depend». Esa orden creaba un archivo depend con todos los prerrequisitos generados automáticamente, y el makefile podía leerlo mediante include (véase Inclusión de otros makefiles).
Con GNU make, gracias a la función de rehacer makefiles, esta forma de proceder ha quedado obsoleta: como make regenera siempre el makefile desactualizado, ya no hace falta indicarle explícitamente que regenere los prerrequisitos. Véase Cómo se rehacen los makefiles.
La forma que recomendamos para la generación automática de prerrequisitos es tener un makefile por cada archivo fuente. Para cada archivo fuente name.c hay un makefile name.d que enumera de qué archivos depende el archivo objeto name.o. Así solo hay que reescanear los archivos fuente que han cambiado para rehacer los nuevos prerrequisitos.
La siguiente es una regla de patrón para generar, a partir de un archivo fuente C name.c, un archivo de prerrequisitos (es decir, un makefile) llamado name.d:
%.d: %.c
@set -e; rm -f $@; \
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
Para la definición de reglas de patrón, véase Definición y redefinición de reglas de patrón. El indicador «-e» del shell hace que el shell termine de inmediato si la orden $(CC) (o cualquier otra) falla (termina con un estado distinto de cero).
Con el compilador de C de GNU quizá se prefiera usar el indicador «-MM» en lugar de «-M». Este omite los prerrequisitos relativos a los archivos de cabecera del sistema. Para más detalles, véase Opciones que controlan el preprocesador en Using GNU CC.
El propósito de la orden sed es transformar (por ejemplo) esto:
main.o : main.c defs.h
en lo siguiente:
main.o main.d : main.c defs.h
Con esto, cada archivo «.d» pasa a depender de todos los archivos fuente y de cabecera de los que depende el archivo «.o» correspondiente. make sabe entonces que debe regenerar los prerrequisitos siempre que cambie cualquiera de los archivos fuente o de cabecera.
Una vez definida la regla para rehacer los archivos «.d», se usa la directiva include para leerlos todos. Véase Inclusión de otros makefiles. Por ejemplo:
sources = foo.c bar.c include $(sources:.c=.d)
(Este ejemplo usa una referencia de sustitución para convertir la lista de archivos fuente «foo.c bar.c» en la lista de makefiles de prerrequisitos «foo.d bar.d». Para más información sobre las referencias de sustitución, véase Referencias de sustitución.) Como los archivos «.d» son makefiles como cualquier otro, make los rehará según sea necesario sin que haya que hacer nada. Véase Cómo se rehacen los makefiles.
Téngase en cuenta que, como los archivos «.d» contienen definiciones de objetivos, la directiva include debe colocarse siempre después de la primera meta predeterminada del makefile. De lo contrario, se corre el riesgo de que algún archivo objeto se convierta en la meta predeterminada. Véase Cómo procesa make un makefile.
[N. del T.] Sobre la nota al pie (2) del texto. La nota al pie del original señala que «algunas versiones antiguas de GNU make no ordenaban el resultado de la expansión de comodines». En las versiones actuales de GNU make, el resultado se ordena por cada expresión de comodín.