ESCUELA DE INGENIERÍA DE SISTEMAS

DEPARTAMENTO DE COMPUTACIÓN

PROGRAMACIÓN 2


PRÁCTICA DE LABORATORIO 2

Uso de herramientas básicas: GNU Make y DDD


Parte I: GNU Make


1.1Introducción


La herramienta make tiene como finalidad detectar de forma automática que partes de un programa necesitan ser compilados (o recompilados), y emitir los comandos necesarios para hacerlo.


Para lograr su objetivo make requiere la siguiente información:

  1. conocer la ubicación de los diferentes archivos con los programas fuente.

  2. las relaciones de dependencias entre éstos.

  3. las instrucciones necesarias para obtener los programas en código objetos y los programas ejecutables.


Por defecto esta información es almacenada en un archivo con el nombre de Makefile. Al invocar make se detecta si cualquiera de los programas fuentes necesarios para crear el objetivo (target) existen o han sido modificados desde la última compilación.


Si se posee un archivo Makefile válido, se puede obtener el programa ejecutable por medio del comando:

make


También es posible indicar el objetivo


make objetivo


1.2 El Makefile1

El archivo Makefile posee dos partes: la definición de variables y definición de reglas


Variables

Una variable es un nombre simbólico que será substituido por su valor en el momento de la aplicación de las reglas posteriores. Para definir una variable se utiliza la siguiente sintaxis:

<IDENTIFICADOR>=valor

Un ejemplo:

PRINCIPAL= miprograma.exe

Por convenio las variables tienen identificadores con todas las letras en mayúscula. Para "expandir" una variable, es decir, para obtener su valor se utiliza la siguiente sintaxis:

$(IDENTIFICADOR)

Make define algunas variables que es necesario conocer y que ya contienen valores por defecto. Algunas de ellas son:

AR
Programa de empaquetado. Por defecto es ar.
CC
Compilador de C. Por defecto es cc.
CXX
Compilador de C++. Por defecto es g++.
CPP
Preprocesador de C.
CFLAGS
Opciones de compilación C.
CXXFLAGS
Opciones de compilación C++.
CPPFLAGS
Opciones para el preprocesador.
LDFLAGS
Opciones para el montador (enlazador).
LDLIBS
Bibliotecas a utilizar en el montaje.

El valor de estas variables puede modificarse en el propio archivo makefile, pero también pueden darse valores por línea de comandos:

 $ make CFLAGS=-g
        

Reglas


La sintaxis de una regla es:

        
<objetivo>: <requisitos>
             <orden para generar objetivo a partir de requisitos>

Tanto <objetivo> como <requisitos> suelen ser nombres de archivos. Cuando make evalúa una regla debe poder ejecutar la orden asociada para generar el objetivo a partir de los requisitos. Si falta alguno de los archivos requisito, make buscará una regla que le permita saber cómo generarlo; si no existe tal regla terminará informando del error.

El nombre del objetivo debe comenzar en la primera columna y debe estar seguido del carácter dos puntos (:) y al menos un espacio. A continuación se enumeran los archivos requeridos (llamados dependencias).

En la siguiente línea se describe la orden que ha de generar el objetivo. Dicha línea debe comenzar obligatoriamente con un carácter TABULACIÓN, en caso contrario make informará de un error de sintaxis. Cada regla puede contener varias líneas de órdenes que seguirán el mismo formato. Cada regla debe ir separada de la anterior por medio de una línea en blanco y al final del archivo también debe aparecer una línea vacía.

Veamos un ejemplo de Makefile:

 CC=gcc
 CFLAGS= -g

		
 hola: hola.o auxhola.o
       $(CC) -o hola hola.o auxhola.o

		
 hola.o: hola.c
       $(CC) $(CFLAGS) -c -o hola.o hola.c

		
 auxhola.o: auxhola.c
       $(CC) $(CFLAGS) -c -o auxhola.o auxhola.c

		
 clean:
       $(RM) *.o hola core
        

En el ejemplo se pretende construir la aplicación hola que consta de los módulos hola.o y auxhola.o. Cuando se ejecuta sin parámetros, make siempre empieza evaluando la primera regla que aparece en el fichero, en este caso la regla hola.

Si alguno de los dos archivos con código objeto no está en el directorio, make ejecutará la regla correspondiente que permite generarlo.

Una característica muy importante de make es que antes de ejecutar una regla comprueba que realmente sea necesario. Por medio de las marcas de tiempo que el sistema operativo almacena en los descriptores de fichero, make averigua que órdenes necesita ejecutar. Siempre que el archivos objetivo tenga una fecha más reciente que cualquiera de los ficheros requisito, la regla se evaluará pero no se ejecuta la orden. Esto permite que cuando trabajamos con grandes proyectos formados por muchos módulos ahorremos mucho tiempo ya que make sólo recompila los archivos necesarios para obtener la versión actualizada del ejecutable.

En el ejemplo aparece una regla adicional llamada clean; esta regla elimina todos los archivos objeto y binarios dejando sólo los fuentes. Por convenio se la suele denominar clean aunque no es obligatorio. También suele definirse una regla all que provoca que se evaluen todas las demás reglas de construcción. Para ejecutar una regla en concreto se le debe indicar a make de la manera siguiente:

  make clean
        

Variables automáticas

Se pueden utilizar algunos indicadores especiales que permiten hacer ficheros makefile más simples:



$@

Es el objetivo de la regla en la que aparece.



$^

En la lista de requisitos de la regla en que aparece.



$?

Es la lista de requisitos más recientes que el objetivo.



$<

Es el primer requisito de la regla.



Veamos como aplicarlos al ejemplo anterior:

 CC=gcc
 CFLAGS=-Wall -g
        
 hola: hola.o auxhola.o
       $(CC) -o $@ $^

		
 hola.o: hola.c
       $(CC) $(CFLAGS) -c -o $@ $^

		
 auxhola.o: auxhola.c
       $(CC) $(CFLAGS) -c -o $@ $^

		
 clean:
       $(RM) *.o hola core
      


Parte 2: Data Display Debuger (ddd)


DDD es una interfaz gráfica la los depuradores GDB, DBX, WDB, Ladebug, JDB, XDB, the depurador de Perl, el depurador de bash bashdb, el depurador de GNU Make remake, o el depurador de Python pydb.


La interfaz de ddd, ver Figura 2, posee 3 componentes principales con funcionalidades propias:


La ventana del programa fuente sirve para inspeccionar el código fuente de nuestros programas y controlar su ejecución, en conjunto con sus botones( los que nos aparecen en la parte inferior de la ventana) y los botones de la ventana de operación (Command Tool), ver Figura 1, es la herramienta que se usará para movernos por el código.



Figura 1: Ventana de comandos



Figura 2: Principales ventanas del DDD


La consola del depurador (2) se utiliza para interactuar directamente con el depurador, recuerde que el DDD es sólo una interfaz al depurador que estemos usando.


La ventana de despliegue de datos (3) muestra una representación gráfica de una variable o estructura de cualquier tipo. En el depurador nos aprecerá como una caja con el nombre y el valor si es una variable simple y si es una estructura además nos mostrará todos sus campos y sus valores, cada una de estas cajas se denomina display

Los displays mostrados se unirán con flechas por parentesco, esto es, dentro de la misma estructura se pueden desglosar sus campos en nuevos displays (cajas) con su información.



Parte 3: Trabajo práctico



3.1 Construya los archivos Makefile correspondientes para los ejercicios 2 y 4 de la practica 1, no olvide crear el objetivo clean que permita borrar los archivos en código objeto y el programa ejecutable.

Pruebe que su Makefile es capaz de crear cada uno de los archivos en código objeto, así como el ejecutable.



Modifique su archivo Makefile para que permita empaquetar todos los archivos fuentes de su aplicación por medio del comando tar.



3.2 Cree un nuevo subdirectorio y en este guarde en un archivo llamado numeroPrimo.c el siguiente programa en lenguaje C

#include<stdio.h>

int esPrimo(int);

int main(){

int n;

printf("Evaluar si un numero es primo, para finalizar introduzca un numero menor que 1\n");

do{

printf("Introduzca el numero a evaluar: ");

scanf("%d", &n);

if ( n < 1 )

break;

if ( esPrimo(n) ) {

printf("%d es primo\n", n);

}else {

printf("%d no es primo\n", n);

}

} while(1);


return(0);

}


int esPrimo(int x){

int i;

int r;

for ( i=2; i <= x/2; i++){

r = x % i;

if (r == 0)

return(0);

}

return(1);

}



Para poder crear la tabla de símbolos requerida por el depurador, es necesario en el momento del la compilación agregar la opción -g como se indica a continuación:

gcc -g -c numeroPrimo.c

El programa ejecutable se crea de la forma acostumbrada:

gcc numeroPrimo.o -o numeroPrimo



Cargue el programa ejecutable en el depurador

ddd numeroPrimo

Verifique que en la ventana de código fuente aparece el programa numeroPrimo.c, recuerde que si el programa fuente es modificado se debe compilar, crear el ejecutable y cargar en el depurador para poder ver los cambios en la ventana de código fuente.



Colocar un punto de parada:

Coloque el cursor en la ventana de código fuente, en la linea donde aparece if (n < 1), localice el botón STOP /BREAK en la parte superior de la ventana de despliegue de datos. Observe los cambios en la consola del GDB y en la ventana de código fuente en el depurador.



Comenzar la ejecución:

En el menú principal Program o el la ventana de comandos (ALT-8) active la opción RUN (F2), el programa debe pedir los datos, ver consola del depurador o ventana de ejecución (ALT-9), y luego detener la ejecución en la linea donde se agregó el punto de parada. La próxima línea a ejecutar es indicada de forma gráfica en la ventana de código fuente y en la consola de GDB por medio de un mensaje de texto.

Ejecución linea a linea o paso a paso

Para ejecutar una línea completa use la opción Next (F6) en el menú Program o en la ventana de comandos, si en una línea existe una llamada a un subprograma este se ejecutará de forma automática. Si desea detenerse en las llamadas a a los subprogramas y entrar en estos use la opción Step (F5) en vez de la opción de la opción Next.



Visualización de valores y despliegue de datos.

Para ver el valor de una variable activa, coloque el cursor sobre la variable en la ventana de código fuente. Para observar continuamente los cambios en una variable coloque el cursor sobre la variable activa y con el menu derecho selecciones Display, o Display* para usar el operador de des-referencia en el caso de apuntadores.

Observe el valor de n.

Ejecute linea a linea hasta llegar a la linea que contiene If (esPrimo(n)) , ejecute esta línea paso a paso para observar la función.

Cree un display para x y observe si es el valor es el de la variable n en la función main().

Cree un display para r y para i, ajuste las cajas de display en la ventana de visualización para supervisar constantemente los valores de x, r e i mientras ejecuta linea a linea las instrucciones en esPrimo()

Al finalizar la función verifique si el valor de r fue correctamente transmitido a la función main().



3.2 Cree un nuevo subdirectorio y cree un programa llamado ordenaMayorMenor.c con las siguentes instrucciones:

#include<stdio.h>

void corrigeMayorMenor(int, int);


int main(){

int mayor;

int menor;

printf("Piense en dos numeros enteros diferentes\n");

printf("Introduzca el mayor: ");

scanf("%d", &mayor);

printf("Introduzca el menor: ");

scanf("%d", &menor);

corrigeMayorMenor(mayor,menor);

printf("Mayor: %d, Menor %d\n", mayor, menor);


return(0);

}


void corrigeMayorMenor(int mayor, int menor){

int auxiliar;

if ( mayor < menor ){

auxiliar = mayor;

mayor = menor;

menor = auxiliar;

}

return;

}



El objetivo de la función corrigeMayorMenor() es corregir cualquier equivocación al introducir los datos, compile y ejecute el programa, confirme con varios pares de datos que el programa se comporta de la forma deseada.


Si el programa da información falsa con algunos pares de datos, verifique la función corrigeMayorMenor() ejecutando ésta paso a paso.


Si la función posee errores de lógica modifique las declaraciones necesarias y agregue los operadores de dirección (&) y de desreferencia (*) para corregir el error.


3.4 Haga los cambios necesarios para detectar números iguales, y compruebe su funcionamiento con el depurador.


3.5 En la ventana de comandos del ddd, ver Figura 1, compruebe el funcionamiento de las opciones Cont, Interrup, Nexti, Stepi y Make.


Enlaces:


Linux Man Pages: http://linuxmanpages.com/man1/make.1.php

GNU DDD: http://www.gnu.org/software/ddd/

MiniTutorial DDD (versión 2.1 del ddd): http://www.eui.upm.es/Servicios/CC/Chuletas/DDD/




Realizado por:

Rafael Rivas Estrada, rafael@ula.ve

versión 1.0

Noviembre, 2009


Revisado por:

Hilda Contreras, hyelitza@ula.ve

Gilberto Diaz, gilberto@ula.ve




1Tomado de la página de David Villa en la Universidad de Castilla -La Mancha http://arco.esi.uclm.es/~david.villa/doc/repo/make/make.html