Idioma: 日本語 | Español | Français | Português | 中文 | English
Anterior | Siguiente | Índice | Original en inglés (gnu.org)

2 Introducción a los makefiles

Para indicar a make qué quiere que haga, necesita un archivo llamado makefile. En la mayoría de los casos, el makefile le dice a make cómo compilar y enlazar un programa.

En este capítulo expondremos un makefile sencillo que describe cómo compilar y enlazar un editor de texto compuesto por ocho archivos fuente en C y tres archivos de cabecera. El makefile también puede indicar a make cómo ejecutar otras órdenes diversas cuando se le solicita de forma explícita (por ejemplo, eliminar ciertos archivos como tarea de limpieza). Para ver un ejemplo de makefile más complejo, consulte Ejemplo de Makefile complejo.

Cuando make recompila el editor, cada archivo fuente en C que haya cambiado debe recompilarse. Si ha cambiado un archivo de cabecera, hay que recompilar, por seguridad, todos los archivos fuente en C que lo incluyen con #include. Cada compilación produce un archivo objeto correspondiente al archivo fuente. Por último, si se ha recompilado algún archivo fuente, todos los archivos objeto —tanto los recién creados como los conservados de compilaciones anteriores— deben enlazarse para producir el nuevo editor ejecutable.

2.1 Qué aspecto tiene una regla

Un makefile sencillo consiste en un conjunto de «reglas» con la siguiente forma:

target … : prerequisitesrecipe
        …
        …

Un objetivo (target) suele ser el nombre de un archivo que genera un programa; ejemplos de objetivos son los archivos ejecutables o los archivos objeto. Un objetivo también puede ser el nombre de una acción a realizar, como clean (consulte Objetivos ficticios (phony)).

Un prerrequisito (prerequisite, antes llamado dependencia) es un archivo que se usa como entrada para crear el objetivo. Es frecuente que un objetivo dependa de varios archivos.

Una receta (recipe, el contenido que ejecuta la regla) es una acción que make lleva a cabo. Una receta puede tener más de una orden, ya sea en la misma línea o cada una en la suya. [Importante] ¡debe colocar un carácter de tabulación al principio de cada línea de receta! Es una sutileza que pilla desprevenidos a los incautos. Si prefiere prefijar sus recetas con un carácter distinto de la tabulación, puede asignar otro carácter a la variable .RECIPEPREFIX (consulte Otras variables especiales).

Por lo general, una receta forma parte de una regla con prerrequisitos y sirve para crear un archivo objetivo cuando cambia alguno de esos prerrequisitos. No obstante, la regla que especifica una receta para el objetivo no tiene por qué tener prerrequisitos. Por ejemplo, la regla que contiene la orden de borrado asociada al objetivo clean no tiene prerrequisitos.

Así pues, una regla explica cómo y cuándo rehacer ciertos archivos, que son los objetivos de esa regla concreta. make ejecuta la receta sobre los prerrequisitos para crear o actualizar el objetivo. Una regla también puede explicar cómo y cuándo llevar a cabo una acción. Consulte Cómo escribir reglas.

Un makefile puede contener otro texto además de reglas, pero un makefile sencillo solo necesita reglas. Las reglas pueden parecer algo más complicadas que las de esta plantilla, pero todas se ajustan, más o menos, a este patrón.

2.2 Un Makefile sencillo

He aquí un makefile directo que describe cómo un archivo ejecutable llamado edit depende de ocho archivos objeto que, a su vez, dependen de ocho archivos fuente en C y tres archivos de cabecera.

En este ejemplo, todos los archivos en C incluyen defs.h con #include, pero solo los que definen órdenes de edición incluyen command.h, y solo los archivos de bajo nivel que modifican el búfer del editor incluyen buffer.h.

edit : main.o kbd.o command.o display.o \
       insert.o search.o files.o utils.o
        cc -o edit main.o kbd.o command.o display.o \
                   insert.o search.o files.o utils.o

main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
insert.o : insert.c defs.h buffer.h
        cc -c insert.c
search.o : search.c defs.h buffer.h
        cc -c search.c
files.o : files.c defs.h buffer.h command.h
        cc -c files.c
utils.o : utils.c defs.h
        cc -c utils.c
clean :
        rm edit main.o kbd.o command.o display.o \
           insert.o search.o files.o utils.o

Aquí dividimos cada línea larga en dos usando barra invertida y salto de línea; equivale a escribir una sola línea larga, pero resulta más legible. Consulte Dividir líneas largas.

Para usar este makefile y crear el archivo ejecutable llamado edit, teclee:

make

Para usar este makefile y eliminar del directorio el archivo ejecutable y todos los archivos objeto, teclee:

make clean

En el makefile del ejemplo, los objetivos incluyen el archivo ejecutable edit y los archivos objeto main.o, kbd.o, etc. Los prerrequisitos son archivos como main.c y defs.h. De hecho, cada archivo .o es a la vez un objetivo y un prerrequisito. Las recetas incluyen cc -c main.c y cc -c kbd.c.

Cuando un objetivo es un archivo, debe recompilarse o reenlazarse si cambia alguno de sus prerrequisitos. Además, los prerrequisitos que se generan automáticamente deben actualizarse antes. En este ejemplo, edit depende de cada uno de los ocho archivos objeto, y el archivo objeto main.o depende del archivo fuente main.c y del archivo de cabecera defs.h.

Tras cada línea que contiene un objetivo y sus prerrequisitos puede ir una receta. Estas recetas indican cómo actualizar el archivo objetivo. Para distinguir las recetas de las demás líneas del makefile, al principio de cada línea de la receta debe ir un carácter de tabulación (o el carácter que especifique la variable .RECIPEPREFIX; consulte Otras variables especiales). (Tenga presente que make no sabe nada sobre cómo funcionan las recetas. Es responsabilidad suya proporcionar recetas que actualicen correctamente el archivo objetivo. Lo único que hace make es ejecutar la receta que usted ha especificado cuando es necesario actualizar el archivo objetivo.)

El objetivo clean no es un archivo, sino simplemente el nombre de una acción. Como normalmente no se desea llevar a cabo las acciones de esta regla, clean no es prerrequisito de ninguna otra regla. En consecuencia, make nunca hace nada con ella salvo que se lo indique de forma explícita. Observe que esta regla no solo no es prerrequisito de nada, sino que tampoco tiene prerrequisitos propios, de modo que su único propósito es ejecutar la receta especificada. Los objetivos que no se refieren a archivos, sino que son meras acciones, se denominan objetivos ficticios (phony). Consulte Objetivos ficticios (phony) para más información sobre este tipo de objetivos, y Errores en las recetas para ver cómo hacer que make ignore los errores de rm o de cualquier otra orden.

2.3 Cómo procesa make un Makefile

De forma predeterminada, make empieza por el primer objetivo (no por los objetivos cuyo nombre comienza con ., salvo que también contengan uno o más /). A esto se le llama meta predeterminada. (Las metas son los objetivos que make se esfuerza por actualizar en última instancia.) Puede modificar este comportamiento mediante la línea de órdenes (consulte Argumentos para especificar las metas) o con la variable especial .DEFAULT_GOAL (consulte Otras variables especiales).

En el ejemplo sencillo de la sección anterior, la meta predeterminada es actualizar el programa ejecutable edit; por eso pusimos esa regla la primera.

Así pues, cuando se da la orden:

make

make lee el makefile del directorio actual y empieza procesando la primera regla. En el ejemplo, esa regla sirve para reenlazar edit; pero antes de que make pueda procesarla por completo, debe procesar las reglas de los archivos de los que depende edit, que en este caso son los archivos objeto. Cada uno de estos archivos se procesa según su propia regla. Esas reglas indican que cada archivo .o se actualice compilando su archivo fuente. La recompilación debe realizarse si el archivo fuente, o cualquiera de los archivos de cabecera indicados como prerrequisitos, es más reciente que el archivo objeto, o si el archivo objeto no existe.

Las demás reglas se procesan porque sus objetivos aparecen como prerrequisitos de la meta. Si la meta (o cualquier cosa de la que dependa, y así sucesivamente) no depende de alguna otra regla, esa regla no se procesa, salvo que se lo indique explícitamente a make (con una orden como make clean).

Antes de recompilar un archivo objeto, make considera actualizar sus prerrequisitos, es decir, el archivo fuente y los archivos de cabecera. Este makefile no especifica nada que deba hacerse con ellos —los archivos .c y .h no son objetivos de ninguna regla—, de modo que make no hace nada con esos archivos. Sin embargo, make sí actualizaría en este paso, según sus propias reglas, los programas en C generados automáticamente, como los que producen Bison o Yacc.

Tras recompilar los archivos objeto que lo necesiten, make decide si reenlazar edit. Esto debe hacerse si el archivo edit no existe, o si alguno de los archivos objeto es más reciente que él. Si un archivo objeto se acaba de recompilar, ahora es más reciente que edit, de modo que edit se reenlaza.

Por tanto, si modificamos el archivo insert.c y ejecutamos make, make compilará ese archivo para actualizar insert.o y luego enlazará edit. Si modificamos el archivo command.h y ejecutamos make, make recompilará los archivos objeto kbd.o, command.o y files.o, y luego enlazará el archivo edit.

2.4 Las variables simplifican los Makefiles

En el ejemplo anterior tuvimos que enumerar todos los archivos objeto dos veces dentro de la regla de edit (se reproduce aquí):

edit : main.o kbd.o command.o display.o \
              insert.o search.o files.o utils.o
        cc -o edit main.o kbd.o command.o display.o \
                   insert.o search.o files.o utils.o

Esta duplicación es propensa a errores; si se añade al sistema un nuevo archivo objeto, podríamos agregarlo a una lista y olvidarnos de la otra. Podemos eliminar este riesgo y simplificar el makefile usando una variable. Las variables permiten definir una cadena de texto una sola vez y sustituirla después en múltiples lugares (consulte Cómo usar las variables).

Es práctica habitual que todo makefile tenga una variable llamada objects, OBJECTS, objs, OBJS, obj u OBJ que contenga la lista de todos los nombres de archivos objeto. Definiríamos una variable así, objects, con una línea como esta en el makefile:

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

Entonces, en cada lugar donde queramos poner una lista de los nombres de archivos objeto, podemos sustituir el valor de la variable escribiendo $(objects) (consulte Cómo usar las variables).

Así queda el makefile sencillo completo cuando se usa una variable para los archivos objeto:

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

edit : $(objects)
        cc -o edit $(objects)
main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
insert.o : insert.c defs.h buffer.h
        cc -c insert.c
search.o : search.c defs.h buffer.h
        cc -c search.c
files.o : files.c defs.h buffer.h command.h
        cc -c files.c
utils.o : utils.c defs.h
        cc -c utils.c
clean :
        rm edit $(objects)

2.5 Dejar que make deduzca las recetas

No es necesario detallar las recetas para compilar cada archivo fuente en C, porque make puede deducirlas: dispone de una regla implícita para actualizar un archivo .o a partir de un archivo .c con el nombre correspondiente, mediante una orden cc -c. Por ejemplo, usará la receta cc -c main.c -o main.o para compilar main.c en main.o. Por tanto, podemos omitir las recetas de las reglas de los archivos objeto. Consulte Uso de reglas implícitas.

Cuando un archivo .c se usa automáticamente de este modo, también se añade automáticamente a la lista de prerrequisitos. Por tanto, si omitimos la receta, podemos omitir también el archivo .c de los prerrequisitos.

He aquí el ejemplo completo, con ambos cambios y con la variable objects sugerida antes:

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

edit : $(objects)
        cc -o edit $(objects)

main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h

.PHONY : clean
clean :
        rm edit $(objects)

Así es como escribiríamos el makefile en la práctica real. (Las complicaciones asociadas a clean se describen en otro lugar. Consulte Objetivos ficticios (phony) y Errores en las recetas.)

Como las reglas implícitas son tan cómodas, resultan importantes. Las verá usarse con frecuencia.

2.6 Otro estilo de Makefile

Cuando los objetos de un makefile se crean únicamente mediante reglas implícitas, es posible otro estilo de makefile. En este estilo, se agrupan las entradas por sus prerrequisitos en lugar de por sus objetivos. Así es como queda:

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

edit : $(objects)
        cc -o edit $(objects)

$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h

Aquí defs.h se indica como prerrequisito de todos los archivos objeto; command.h y buffer.h son prerrequisitos de los archivos objeto concretos que se enumeran para cada uno.

Que esto sea mejor o no es cuestión de gustos: es más compacto, pero a algunas personas no les agrada porque les resulta más claro reunir en un solo lugar toda la información sobre cada objetivo.

2.7 Reglas para limpiar el directorio

Compilar un programa no es lo único para lo que puede querer escribir reglas. Es habitual que los makefiles indiquen cómo hacer algunas otras cosas además de compilar un programa: por ejemplo, cómo eliminar todos los archivos objeto y ejecutables para dejar el directorio clean (limpio).

He aquí cómo podríamos escribir una regla de make para limpiar nuestro editor de ejemplo:

clean:
        rm edit $(objects)

En la práctica, quizá queramos escribir la regla de una forma algo más elaborada para hacer frente a situaciones imprevistas. Lo haríamos así:

.PHONY : clean
clean :
        -rm edit $(objects)

Así se evita que make se confunda si existe un archivo real llamado clean, y se logra que continúe a pesar de los errores de rm. (Consulte Objetivos ficticios (phony) y Errores en las recetas.)

Una regla como esta no debería colocarse al principio del makefile, ¡porque no queremos que se ejecute de forma predeterminada! Por eso, en el makefile del ejemplo queremos que la regla de edit, que recompila el editor, siga siendo la meta predeterminada.

Como clean no es prerrequisito de edit, esta regla no se ejecutará en absoluto si damos la orden make sin argumentos. Para que la regla se ejecute, hay que teclear make clean. Consulte Cómo ejecutar make.


Anterior | Siguiente | Índice | Original en inglés (gnu.org)