Titulación: Ingeniería Informática plan 96

Asignatura: Entornos de Programación - Curso 2011/12

Ejercicio práctico: Programación en lenguaje AWK


3. Construir un diagrama modular navegable

El objetivo de esta práctica es desarrollar una utilidad rudimentaria que permita construir el diagrama modular de una aplicación escrita en lenguaje C/C++, y visualizar dicho diagrama en un navegador web con posibilidad de navegar por el diagrama saltando a las páginas del código fuente.

Se considerará que cada fichero fuente en C o C++ es un módulo. Las relaciones de uso entre módulos corresponderán a las directivas #include. Como alternativa se puede desarrollar la práctica para código fuente en Ada, usando como módulos los paquetes y los subprogramas aislados, y como relaciones de uso las de las sentencias with.

El funcionamiento global de la utilidad a desarrollar responderá al siguiente esquema:

funcionamiento global

El programa gen-dot.awk procesa de una vez el conjunto de los ficheros fuente y genera la descripción del diagrama modular usando el lenguaje DOT, que puede ser procesado mediante la utilidad Graphviz para componer el dibujo.

El programa gen-code.awk servirá para procesar un fichero fuente cada vez, y generar la correspondiente página web conteniendo el código fuente como texto preformado.

Finalmente, el guión gen-web.bat se encarga de automatizar todo el proceso. Mandará ejecutar los programas AWK indicados para realizar esas partes del trabajo, y generará directamente la página web de índice conteniendo el diagrama navegable con enlaces a las páginas de código individuales.

Por lo tanto, la práctica puede desarrollarse por partes, descomponiendo el problema en los subproblemas indicados, que se resuelven por separado, aprovechando en uno lo ya resuelto en otro. A continuación se plantea este desarrollo por partes:

3.1 Organización del trabajo

Se supone que los ficheros con el código a procesar podrán estar en directorios diferentes. Se procesará uno de estos directorio cada vez. Las páginas web generadas se situarán en un subdirectorio de nombre web en ese mismo directorio. También se situarán en ese directorio web los ficheros temporales que se generen, en su caso.

Por otra parte, los guiones de órdenes para generar el diagrama y la web se mantendrán en un directorio separado. Es decir, no se deben mezclar los ficheros que constituyen la utilidad desarrollada con los ficheros del usuario a los que se aplicarán.

La estructura del conjunto será como la del siguiente ejemplo:

...\bin                 →  directorio con el código desarrollado
    |- gen-web.bat      →  orden de realizar todo el proceso
    |- gen-dot.awk      →  orden de construir el diagrama
    |- gen-code.awk     →  orden de construir una página web de código
    `- ...              →  otros ficheros auxiliares, si interesa

...\ejemplos-1          →  directorio con ficheros de prueba
    |- xxx.cpp          →  fichero de prueba
    |- xxx.h            →  fichero de prueba
    |- yyy.cpp          →  fichero de prueba
    |- ...
    `-\web
       |- index.html    →  página principal
       |- diagrama.dot  →  descripción del diagrama
       |- diagrama.png  →  imagen del diagrama
       |- xxx.cpp.html  →  página de código fuente
       `- ...

...\ejemplos-2          →  directorio con otros ficheros de prueba
    |- alfa.cpp         →  programa de prueba
    |- alfa.h           →  programa de prueba
    |- ...
    `-\web
       |- index.html    →  página principal
       `- ...

El directorio bin se incluirá en la lista del PATH al comienzo de una sesión de trabajo:

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

La creación de la web con el diagrama navegable deberá ordenarse desde cualquier directorio, con total independencia de la ubicación del directorio bin. Puesto que bin está en el PATH las órdenes se podrán invocar simplemente por su nombre.

...> cd camino-a-ejemplos
...> gen-web directorio

3.2 La utilidad Graphviz

Esta utilidad permite construir automáticamente el dibujo de un grafo a partir de la descripción de sus nodos y arcos. La descripción puede ser simplemente una enumeración de los arcos, que implícitamente nombra también cada nodo. Ejemplo:

Descripción del grafo (ejemplo.dot) Dibujo generado (ejemplo.png)
digraph {
   A -> B;
   A -> C;
   B -> C;
}

ejemplo.png

Para obtener el dibujo hay que ejecutar el programa dot, que es parte de la utilidad Graphviz. El formato de la orden es:

> dot -Tformato -ofichero-salida fichero-entrada

Con el ejemplo anterior, si los nombres de los ficheros son los que se indican, la orden sería:

> dot -Tpng -oejemplo.png ejemplo.dot

El dibujo obtenido se puede visualizar como parte de una página web, usando la marca

... <img src="fichero-salida"> ...

Para que el diagrama sirva como elemento de navegación se puede asociar a cada nodo un enlace a otra página web. Para ello hay que generar un mapa web que asocie los enlaces a las áreas del dibujo que correspopndan. Afortunadamente la utilidad dot de Graphviz puede generar el mapa automáticamente usando el formato de salida especial -Tcmap.

Para ver cómo se usa esta facilidad, modificaremos ligeramente el ejemplo anterior. Ahora hay que enumerar explíctamente los nodos, y para simplificar el mapa se ha cambiado la forma por defecto de los nodos (elipse) a rectángulos:

digraph {
   node [shape=box];
   A [URL="alfa.html"];
   B [URL="beta.html"];
   C [URL="gamma.html"];
   A -> B;
   A -> C;
   B -> C;
}

Ahora la orden

> dot -Tcmap ejemplo.dot

producirá las siguientes líneas de texto por la salida estándar (ya que no se ha especificado fichero de salida)

<area shape="rect" ... href="alfa.html" ... coords="41,5,113,53">
<area shape="rect" ... href="beta.html" ... coords="5,101,77,149">
<area shape="rect" ... href="gamma.html" ... coords="41,197,113,245">

Por último conviene saber que el lenguaje DOT permite asociar propiedades de estilo visual a los elementos del grafo. La siguiente tabla recoge unas pocas de estas propiedades.

Propiedad Descripción Aplicable a Valores posibles
(valor por defecto en negrita)
shape Forma del nodo nodo ellipse, box, circle, record, Mrecord, point, none, ...
label Texto o rótulo asociado nodo, arco "literal" ("" o nombre-del-nodo)
style Estilo del trazo nodo, arco solid, bold, dotted, dashed, invis
Relleno de una figura nodo diagonals, filled, rounded
color Color del trazo nodo, arco #rrggbb o nombre
fontcolor Color del texto nodo, arco
fillcolor Color de relleno nodo
URL Destino de enlace nodo, arco, grafo URL
concentrate Fundir arcos con origen o destino común grafo false, true
rankdir Orientación de la jerarquía grafo TB, LR, BT, RL

3.3 Generar el diagrama modular

Para generar la descripción del grafo del diagrama en la notación DOT se puede escribir un programa en AWK (gen-dot.awk) que procese el conjunto de los ficheros fuente e identifique las directivas #include. La salida del programa debe incluir:

Ejemplo:

Ficheros fuente Descripción del grafo (diagrama.dot)
Fichero: alfa.cpp
#include "alfa.h"
#include <stdio.h>
Fichero: alfa.h
#include <limits.h>
Fichero: beta.cpp
#include "alfa.h"
#include <stdio.h>
digraph {
  node [shape=box];
  "alfa.cpp" [URL="alfa.cpp.html"];
  "alfa.h" [URL="alfa.h.html",
            style=filled, fillcolor=cyan];
  "beta.cpp" [URL="beta.cpp.html"];
  "<stdio.h>" [style=dashed];
  "<limits.h>" [style=dashed];
  "alfa.cpp" -> "alfa.h";
  "alfa.cpp" -> "<stdio.h>";
  "alfa.h" -> "<limits.h>";
  "beta.cpp" -> "alfa.h";
  "beta.cpp" -> "<stdio.h>";
}

3.4 Generar una página web con el código de un fichero fuente

Aquí también se puede escribir un programa AWK (gen-code.awk) para realizar el proceso. Este programa recibirá un fichero fuente y generará el marcado HTML correspondiente a una página web que contenga el código fuente como texto preformado. El esquema general del código HTML sería:

<html>
  <head><title>fichero-fuente</title></head>
  <body>
    <h1>fichero-fuente</h1>
    <pre>
      ...
      código fuente
      ...
    </pre>
  </body>
</html>

En la parte de código fuente hay que tener en cuenta que ciertos caracteres están reservados para el marcado HTML, y por tanto hay que sustituirlos por símbolos especiales para que se interpreten literalmente. En concreto:

3.5 Generar una página web de índice

Finalmente hay que redactar un guión de órdenas (gen-web.bat) que procese todos los ficheros fuente de un directorio (xxx) y genere un subdirectorio (xxx\web) con:

El esquema de código HTML de la página de cabecera sería:

<html>
  <head><title>directorio</title></head>
  <body>
    <h1>directorio</h1>
    <img src="diagrama.png" usemap="#diagrama.map">
    <map name="diagrama.map">
      <area ... generado por dot ...>
      <area ... generado por dot ...>
      <area ... generado por dot ...>
      ...
    </map>
  </body>
</html>

Esta página de cabecera puede ser generada directamente por gen-web.bat escribiendo cada parte mediante órdenes echo, o copiando el contenido de un fichero con texto fijo preparado de antemano, o invocando la utilidad dot y redirigiendo su salida.

El diagrama se obtendrá invocando gen-dot.awk y la utilidad dot.

Las páginas web de código fuente se obtendrán mediante un bucle de llamadas a gen-code.awk con cada fichero fuente.

3.6 Ejemplo

Usando como ejemplo el código de ajustar.cpp se obtiene como resultado:

En el navegador web puede usarse la opción "ver código fuente de la página" (o "guardar como ...") para examinar el detalle del código HTML generado.

Apéndice A. Sugerencias para implementación

  1. Para facilitar los ensayos conviene preparar un script de inicio de la práctica que ponga en el PATH el subdirectorio ...\bin que contiene las órdenes gen-*.bat. Ejemplo:
    H:\entornos\practica1
       |- bin
       |  `- ...
       |- prueba.cpp
       |  `- ...
       `- p3.bat    --->  path H:\entornos\practica3\bin;%PATH%
    Esta orden se invocará manualmente una vez al comienzo de cada sesión de pruebas desde la misma ventana de órdenes desde la que se invocarán luego las operaciones de generar la web.
  2. En un fichero de órdenes .bat se puede hacer referencia a otros ficheros que estén en su mismo directorio (o en una posición relativa conocida) sin necesidad de escribir la ruta completa. Por ejemplo, la notación %~dp0xxxx hace referencia a un fichero xxxx que esté en el mismo directorio que el guión de órdenes. Esto facilita escribir código transportable que se puede copiar de un equipo a otro o de un directorio a otro y sigue funcionando sin necesidad de retoques.
  3. Para generar la descripción del grafo del diagrama modular quizá lo más cómodo es ir almacenando la información de nodos y arcos en vectores (tablas) durante el proceso de los ficheros fuente, y luego generar toda la salida al final, en la cláusula END.

Apéndice B. Lista de comprobaciones

Para comprobar si la utilidad desarrollada funciona correctamente bastará ejecutarla con los programas de prueba que se suministran como ejemplo.

  1. Descargar el código de ejemplo al directorio de trabajo del alumno.
  2. Invocar la orden principal gen-web con cada directorio de ejemplo, y comprobar que se generan los subdirectorios web con el contenido esperado.
  3. Abrir la página inicial index.html de cada subdirectorio web y comprobar que se visualiza el diagrama y se puede navegar a las páginas de código fuente haciendo click sobre los nodos del grafo que correspondan.
  4. Ensayar la herramienta con otros ejemplos de código obtenidos de proyectos de código abierto.