Titulación: Grado en Ingeniería Informática

Asignatura: Entornos de Programación - Curso 2012/13

Ejercicio práctico: Herramienta “make


4. Uso de la herramienta “make

Se trata de practicar el uso de la herramienta “make” de UNIX, aplicándola a proyectos sencillos. Para facilitar su empleo se construirá una utilidad que genere automáticamente el fichero makefile a partir de la lista de ficheros fuente del proyecto. Para simplificar, se asumirá que un proyecto corresponde exactamente a un fichero ejecutable, que se obtendrá a partir de una colección de ficheros fuente en C++.

Al finalizar el desarrollo de la práctica se podrán usar órdenes como las siguientes:

    > makegen proyecto

Con esta orden se procesará un fichero proyecto.pro que contendrá la lista de ficheros fuente, uno por línea, y se construirá un fichero Makefile para dicho proyecto, con los objetivos que se indican más adelante.

    > make [objetivos ...]

Con esta orden se generará o actualizará el fichero ejecutable (por defecto), o se realizarán las operaciones correspondientes a los objetivos explícitos que se indiquen. Se prevé disponer de los objetivos clean, dist y distclean, además del objetivo por defecto.

    > makeauto proyecto [objetivos ...]

Esta orden combina las dos anteriores, que se ejecutarán una tras otra.

3.1 Organización del trabajo

Al igual que en las prácticas anteriores, se supone que los ficheros fuente a procesar están todos en un mismo directorio. Por otra parte, los ficheros que constituyen las herramientas makegen y makeauto se situarán en un subdirectorio bin independiente:

...\bin                  →   directorio con el código de las órdenes
    |- makegen.bat       →   orden de generar Makefile
    |- makeauto.bat      →   orden principal
    |- ... .txt          →   ficheros auxiliares, en su caso
    `- ... .awk          →   programas AWK, en su caso

...\ejemploA             →   directorio con los ficheros de código C++
    |- uno.pro           →   descripción del proyecto "uno"
    |- uno.cpp           →   fichero fuente C++
    |- dos.h             →   fichero fuente C++
    |- dos.cpp           →   fichero fuente C++
    |- ...
    |- uno.zip           →   fichero generado automáticamente
    `- Makefile          →   fichero generado automáticamente

...\ejemploB             →   directorio con otros ficheros C++
    |- xxx.pro           →   descripción del proyecto "xxx"
    |- xxx.cpp           →   fichero fuente C++
    |- yyy.h             →   fichero fuente C++
    |- yyy.cpp           →   fichero fuente C++
    |- ...
    |- xxx.zip           →   fichero generado automáticamente
    `- Makefile          →   fichero generado automáticamente

Las órdenes makegen, make y makeauto se invocarán desde el directorio con el código de ejemplo a procesar. Para ello el directorio bin se incluirá en la lista del PATH al comienzo de una sesión de trabajo:

...> path camino-a-bin;%PATH%

A partir de ese momento, puesto que bin está en el PATH, la herramienta se podrá invocar simplemente por su nombre.

...> cd camino-a-ejemplos
...> makegen proyecto

3.2 Ejemplo

El primer paso para realizar la práctica es comprender cómo se aplica la herramienta make a un ejemplo concreto. Para ello utilizaremos el código disponible en dibujar.cpp. El fichero dibujar.pro ilustra el formato del fichero con la descripción del proyecto correspondiente al programa principal. Se trata de conseguir que a partir de ese fichero se genere automáticamente el Makefile correspondiente, incluyendo los objetivos adicionales.

El formato elegido para este Makefile facilita su adaptación a otros proyectos. Al analizarlo se observan diferentes partes, unas fijas o otras variables. Las partes variables pueden construirse a partir de la información del fichero de proyecto con la lista de ficheros fuente, o mediante la llamada a gcc para detectar automáticamente las dependencias.

PROJ = dibujar
SRCS = curvac.cpp dibujar.cpp papel.cpp tortuga.cpp
HDRS = curvac.h papel.h tortuga.h
Generado a partir de dibujar.pro
OBJS = $(SRCS:%.cpp=%.o)
$(PROJ): $(OBJS)
    g++ -o $(PROJ) $(OBJS)
.PHONY: clean dist distclean
clean:
    cmd /c del *.o
dist:
    cmd /c 7z a $(PROJ).zip $(SRCS) $(HDRS) Makefile
distclean:
    cmd /c del $(PROJ).zip
Código fijo, independiente del proyecto
curvac.o: curvac.cpp tortuga.h papel.h curvac.h
dibujar.o: dibujar.cpp tortuga.h papel.h curvac.h
papel.o: papel.cpp papel.h
ppapel.o: ppapel.cpp papel.h
ptortuga.o: ptortuga.cpp tortuga.h papel.h
tortuga.o: tortuga.cpp papel.h tortuga.h
Generado mediante gcc -MM *.cpp

Inicialmente se puede experimentar con el fichero de ejemplo ya disponible. Se copiarán los ficheros fuente de ejemplo junto con el Makefile a un directorio de trabajo, y se realizarán distintas llamadas a “make” para ver el efecto que producen. Modificando algún fichero fuente se puede ver el efecto de la recompilación selectiva. Tras regenerar el objetivo “clean” se comprobará que se recompila todo al regenerar de nuevo el ejecutable.

3.3 Desarrollo de la utilidad makegen

El código a desarrollar es realmente sencillo. El fichero Makefile se va componiendo por partes. En cada paso se añaden nuevas líneas al texto anterior.

  1. Se procesa el fichero proyecto.pro mediante código AWK. Se generan las primeras líneas del Makefile.
  2. Se copia directamente el código fijo de la parte central del Makefile, que se puede tener ya preparada en un fichero de texto.
  3. Se invoca gcc -MM ... y su salida se añade al final del Makefile. Se puede invocar gcc para todos los ficheros fuente (*.cpp), lo cual genera información innecesaria, pero que no estorba. O se puede ser más selectivo y procesar sólo los ficheros fuente del proyecto. En este caso la orden de llamada a gcc se debe componer en un fichero auxiliar durante el proceso del fichero de proyecto en el paso 1.

3.3 Desarrollo de la utilidad makeauto

El código a desarrollar es realmente trivial. Sólo hay que invocar las utilidades makegen y make, una tras otra, con los parámetros que corresponden a cada una.

Apéndice A. Sugerencias para implementación

  1. En un fichero de órdenes .bat se puede hacer referencia a otros ficheros que estén en su mismo directorio mediante la notación %~dp0fichero.

Apéndice B. Lista de comprobaciones

Para comprobar si la utilidad desarrollada funciona correctamente bastará ejecutarla con el código C++ que se suministra como ejemplo u otro similar.

  1. Crear el fichero dibujar.pro en el directorio de los ficheros fuente de ejemplo.
  2. Invocar la orden global:
    > makeauto dibujar
  3. Comprobar que se compilan los ficheros fuente y que se genera el ejecutable. Probarlo para ver que funciona.
  4. Modificar algún fichero fuente e invocar make. Comprobar que sólo se recompila lo necesario.
  5. Ejecutar make dist. Comprobar que se genera un archivo dibujar.zip.
  6. Descomprimir ese archivo en otro directorio de trabajo. Situarse sobre él y ejecutar make. Comprobar que se recompila y genera el ejecutable en ese otro directorio.
  7. Repetir la prueba con otros objetivos y con otros ejemplos de código C++, preparando manualmente los ficheros de proyecto con las listas de ficheros fuente adecuadas.