Alejandría - La Biblioteca¿Quieres un blog como este?. Date de alta aquí.
Volver a la portada
Portada
Lista completa del contenido
Búsqueda
Enviar un e-mail al buzón de sugerencias.
Consúltenos
 Portada 
 Filosofía / Ensayos 
 Aventuras 
 Ciencia Ficción 
 Técnicos 
 Infantil 
 Esotérico 
 Poesía 
 » Técnicos 

Entendiendo make y los archivos Makefiles

Se describe el funcionamiento básico del programa make y las estructura de un archivo Makefile típico.

Introducción
Según se indica en la manual de make, el propósito de esta utilidad es determinar automáticamente que piezas de un programa necesitan ser recompiladas y de acuerdo a un conjunto de reglas lleva a cabo las tareas necesarias para alcanzar el objetivo definido, que normalmente es compilar un programa.
Para proyectos con varios cientos de líneas de código, permite agilizar el proceso de construcción de los programas, y en en general, facilita el trabajo de compilación para uno o más archivos.
De esta forma y con los archivos adecuados, make compila todos los programas fuentes. Si alguno de ellos sufre alguna modificación, sólo será recompilado aquel que fue modificado, más todos los programas que dependan de él. Por supuesto, es necesario indicarle a make la dependencia de programas, lo cual se realiza en el archivo Makefile.

El formato del archivo Makefile
Un archivo Makefile es un archivo de texto en el cual se distinguen cuatro tipos básicos de declaraciones:
  • ·   Comentarios.
  • ·   Variables.
  • ·   Reglas explícitas.
  • ·   Reglas implícitas.
Comentarios
Al igual que en los programas, contribuyen a un mejor entendimiento de las reglas definidas en el archivo. Los comentarios se inician con el caracter #, y se ignora todo lo que continúe después de ella, hasta el final de línea.
# Este es un comentario
Variables
Se definen utilizando el siguiente formato:
nombre = dato
De esta forma, se simplifica el uso de los archivos Makefile. Para obtener el valor se emplea la variable encerrada entre paréntesis y con el caracter $ al inicio, en este caso todas las instancias de $(nombre) serán reemplazadas por dato. Por ejemplo, la siguiente definición .
SRC = main.c
origina la siguiente línea:
gcc $(SRC)
y será interpretada como:
gcc main.c
Sin embargo, pueden contener más de un elemento dato. Por ejemplo:
objects = programa_1.o programa_2.o programa_3.o \
         programa_4.o programa_5.o
programa: $(objects) gcc -o programa $(objects)
Hay que notar que make hace distinción entre mayúsculas y minúsculas.
Reglas explícitas
Estas le indican a make qué archivos dependen de otros archivos, así como los comandos requeridos para compilar un archivo en particular. Su formato es:
archivoDestino: archivosOrigen
comandos  # Existe una caracter TAB (tabulador) antes de cada comando.
 Esta regla indica que, para crear archivoDestino, make debe ejecutar comandos sobre los archivos archivosOrigen. Por ejemplo:
main: main.c funciones.h
     gcc -o main main.c funciones.h
significa que, para crear el archivo de destino main, deben existir los archivos main.c y funciones.h y que, para crearlo, debe ejecutar el comando:
gcc -o main main.c funciones.h
Reglas implícitas
Son similares a las reglas explícitas, pero no indican los comandos a ejecutar, sino que make utiliza los sufijos (extensiones de los archivos) para determinar que comandos ejecutar. Por ejemplo:
funciones.o: funciones.c funciones.h
origina la siguiente línea:
$(CC) $(CFLAGS) -c funciones.c funciones.h
Existe un conjunto de variables que se emplean para las reglas implícitas, y existen dos categorías: aquellas que son nombres de programas (como CC) y aquellas que tienen los argumentos para los programas (como CFLAGS). Estas variables son provistas y contienen valores predeterminados, sin embargo, pueden ser modificadas, como se muestra a continuación:
CC = gcc  CFLAGS = -Wall -O2
En el primer caso, se ha indicado que el compilador que se empleará es gcc y sus parámetros son -Wall -O2.

Un ejemplo de un archivo Makefile
A continuación se muestra un ejemplo de un archivo Makefile <ejemplo/Makefile> completo, donde se incluyen todas los tipos de declaraciones. En este ejemplo, se utiliza el programa make para compilar los programas funciones.c y main.c para crear un ejecutable llamado main. Tanto funciones.c como main.c utilizan el archivo funciones.h.
Ejemplo 1. Makefile
# La siguiente no es necesariamente requerida, se agrega para
# mostrar como funciona.
.SUFFIXES: .o .c
.c.o:
           $(CC) -c $(CFLAGS) $<
# Macros
CC = gcc
CFLAGS = -g -Wall -O2
SRC = main.c funciones.c funciones.h
OBJ = main.o funciones.o
# Reglas explícitas
all: $(OBJ)
     $(CC) $(CFLAGS) -o main $(OBJ)
clean:  $(RM) $(OBJ) main
# Reglas implícitas
funciones.o: funciones.c funciones.h
main.o: main.c funciones.h
En este archivo Makefile se han definido dos reglas explícitas, que indican como contruir all y clean. Para llevar a cabo las acciones, basta ejecutar
$ make
lo cual ejecutará la primera regla que encuentra, es decir, all, la cual compilará los programas definidos en la variable $(OBJECT). Si se desea que se ejecuten las tareas de la regla clean, se debe ejecutar:
$ make clean
El archivo funciones.h <ejemplo/funciones.h> contiene el prototipo de las funciones empleada en el programa main.c y que se encuentra implementada en funciones.c. De esta manera, es posible separar en distintos pogramas y funciones cada pieza del programa final, lo cual ayuda con la mantención.
Ejemplo 2. funciones.h
void swap (int *, int *);
Para efectos de ejemplificar, el archivo funciones.c <ejemplo/funciones.c> sólo contiene una función, llamada swap, la cual intercambia dos valores recibidos por referencia.
Ejemplo 3. funciones.c
void swap (int *a, int *b){
  int tmp;
  tmp = (*a);
  (*a) = (*b);
  (*b) = tmp;
 };
Finalmente, el programa principal, main.c <ejemplo/main.c>, el cual lee dos valores desde la entrada estándar, los intercambia llamando a la función swap y entrega el resultado en la salida estándar.
Ejemplo 4. main.c
#include <stdio.h>
#include "funciones.h"
int main (int argc, char **argv){
  int x, y;
  printf ("x: ");
  scanf ("%d", &x);
  printf ("y: ");
  scanf ("%d", &y);
  swap (&x, &y);
  printf ("x: %d, y: %d\n", x, y);
  exit (1);
}

Definiendo nuevas reglas
make tiene definido un conjunto de reglas básicas para convertir archivos, típicamente los archivos cuyas extensiones pertenecen a los lenguajes más conocidos, como C, C++, Fortran, entre otros.
También es posible crear reglas propias para formatos de archivos que no necesariamente han de crear un programa ejecutable. Por ejemplo, se puede mantener un conjunto de documentos, cuyo fuente se encuentran en formato .lyx y que se desea convertir a otros formatos, por ejemplo, PDF, TeX, PS, etc, cuyos sufijos son desconocidos para make , y no sabria que programas ejecutar para producir los archivos esperados.
A continuación se describe como añadir nuevas definiciones con GNU make, la cual puede diferir con versiones antiguas de make. Por compatibilidad, más adelante se explica como definirlo de la antigua forma, que GNU make también puede interpretar.
La forma de definir una regla que permita convertir un archivo PostScript en formato PDF utilizando el programa ps2pdf sería de la siguiente manera:
%.pdf: %.ps                ps2pdf $<
Se ha indicado que los archivos cuya extensión son .pdf dependen de los archivos .ps, y que se generan utilizando el programa indicado en la línea siguiente (ps2pdf). El parámetro de entrada para el programa, será el nombre del archivo con extensión .ps.
Sólo falta indicar la regla que indique el o los archivos a convertir. Por ejemplo:
all: documento1.pdf documento2.pdf
De esta forma, el objetivo de make será construir all, para lo cual debe contruir documento1.pdf y documento2.pdf. Para lograr lo último, make buscará los archivos documento1.ps y documento2.ps, lo cual se traducirá en los siguientes comandos:
ps2pdf documento1.ps  ps2pdf documento2.ps

Mejorando los Makefiles con variables automáticas
Existen algunas variables automáticas que permiten escribir los archivos Makefiles de una forma genérica, de esta forma, si se requiere modificar el nombre de un archivo o regla, sólo sea necesario realizar los cambios en un solo lugar, o en la menor cantidad posible y así evitar errores.
Las variables automáticas más empleadas son:
  • ·   $<. El nombre del primer prerequisito.
  • ·   $*. En la definición de una regla implícita, tiene el valor correspondiente al texto que reemplaza el símbolo %.
  • ·   $?. El nombre de todos los prerequisitos.
  • ·   $@. El nombre del archivo del objetivo de la regla.
%.pdf: %.ps
       ps2pdf $<
%.zip: %.pdf
       echo $*.zip $<
PDF = documento1.pdf documento2.pdf
ZIP = documento1.zip documento2.zip
pdf: $(PDF)
paquete: $(PDF)
         tar zcvf $@.tar.gz $?
zip: $(ZIP)
clean:
       rm -f *.pdf *.tar
En el ejemplo, se han definido dos reglas implícitas. La primera indica como convertir un archivo PostScript a PDF y la segunda, como comprimir un archivo PDF en formato ZIP. También se han definido cuatro reglas, dos de ellas son implícitas (pdf y zip), donde sólo se han indicado sus prerequisitos y las otras dos (paquete y clean) son explícitas.
Cuando se ejecute la regla paquete, make analizará las dependencias, es decir, verificará si existen los correspondientes archivos PDF, sino, los construye, y luego para a ejecutar el comando indicado en la regla. La variable $? será expandida a "documento1.pdf documento2.pdf" y la variable $@ será expandida a "paquete". De esta forma, el comando a ejecutar será:
tar zcvf paquete.tar.gz documento1.pdf documento2.pdf
En el caso de la regla zip, al resolver las dependencias se ejecutará:
zip documento1.zip documento1.pdf  zip documento2.zip documento2.pdf

Es decir, el patrón buscado es "documento1" y "documento2", el cual corresponde a la expresión %. Dicha operación se realizar por cada archivo .pdf.

Germán Poo

Descargas (downloads):

Los archivos Makefiles
ArchivoMakefile.doc

© 2008Powered by NIDAVAL