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

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

Ejercicio práctico: Programación en un lenguaje de órdenes (shell)


1. Invocar herramientas de análisis estático

El objetivo de esta práctica es desarrollar scripts que faciliten el invocar operaciones de análisis estático de código fuente (sintaxis, semántica, estilo, etc.).

Las facilidades aportadas por esta utilidad serán:

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

???

Los guiones de órdenes comprobar-xxx.bat son específicos para cada lenguaje de programación, y sirven para invocar la herramienta final de análisis estático con los parámetros adecuados.

El guión comprobar1.bat sirve para invocar la operación de analizar un único fichero fuente, y debe decidir a cuál de los guiones anteriores debe invocar en función de la extensión (o tipo) del fichero a analizar.

Finalmente, el guión comprobar.bat se encarga de procesar una colección de ficheros pasados como argumentos, invocando a comprobar1.bat para cada uno de ellos.

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:

1.1 Organización del trabajo

Se supone que los ficheros con el código a probar podrán estar en directorios diferentes. Las herramientas de análisis podrán generar ficheros complementarios (efectos laterales) en el directorio de los ficheros a probar o en el directorio desde el que se invoca la operación. En cualquier caso esos ficheros temporales deberían desaparecer al terminar la realización de las pruebas.

Por otra parte, los guiones de órdenes para automatizar las pruebas se mantendrán en un directorio separado. Es decir, no se deben mezclar los ficheros de órdenes que constituyen la utilidad a desarrollar con los ficheros del usuario a los que se aplicarán.

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

...\bin                   →  directorio con los guiones de órdenes
    |- comprobar.bat      →  orden de analizar varios ficheros
    |- comprobar1.bat     →  orden de analizar un fichero
    |- comprobar-ada.bat  →  orden de análisis Ada
    |- comprobar-awk.bat  →  orden de análisis AWK
    |- comprobar-cc.bat   →  orden de análisis C/C++
    |- comprobar-java.bat →  orden de análisis Java
    `- ...                →  otros ficheros auxiliares, si interesa

...\ejemplos-1            →  directorio con ficheros de prueba
    |- prueba.adb         →  fichero de prueba
    |- prueba.ads         →  fichero de prueba
    |- otro.awk           →  fichero de prueba
    `- ...

...\ejemplos-2            →  directorio con otros ficheros de prueba
    |- pruebaX.cpp        →  programa de prueba
    |- pruebaY.java       →  programa de prueba
    `- ...

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

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

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

...> cd camino-a-ejemplos
...> comprobar prefijo

1.2 Analizar un fichero fuente de un tipo determinado

Como herramientas de análisis estático usaremos simplemente los compiladores de los diferentes lenguajes, invocándolos con las opciones adecuadas para generar el máximo de comprobaciones y mensajes de aviso. La siguiente tabla recoge las extensiones de los ficheros de código fuente y los parámetros de invocación de los compiladores para cada lenguaje.

Lenguaje y orden Extensiones Ficheros
objeto
Compilador
y parámetros
Ada
comprobar-ada.bat
.ads
.adb
.o
.ali
gnat compile
  -gnatwadhl
  -gnatyaknr
  -gnatc
  -c fichero.ext
AWK
comprobar-awk.bat
.awk
gawk
  --lint
  --source "BEGIN {exit} END {exit}"
  -f fichero.ext
C/C++
comprobar-cc.bat
.c
.cpp
.o
gcc
  -Wall
  -pedantic
  -fmessage-length=0
  -Wformat=2
  -Winit-self
  -Wswitch-default
  -Wextra
  -Wfloat-equal
  -Wshadow
  -Wpointer-arith
  -Wwrite-strings
  -Wconversion
  -Wmissing-noreturn
  -Wmissing-format-attribute
  -Wredundant-decls
  -Wunreachable-code
  -c fichero.ext
Java
comprobar-java.bat
.java
.class
javac
  -Xlint
  fichero.ext

Nota: La orden de invocar al compilador debe escribirse en una sola línea, aunque resulte muy larga. En algunos casos se pueden escribir las opciones en un fichero de texto separado (p.ej. opciones.xxx) y hacer referencia a él en la orden de compilar mediante @opciones.xxx. Consultar el manual del compilador correspondiente.

En general, el uso del compilador para analizar el código fuente va acompañado de la generación de código objeto si no hay errores que lo impidan. Estos ficheros de código objeto deberían ser borrados después de realizar el análisis. También deberían ser eliminados los ficheros objeto antes de hacer un nuevo análisis, ya que a veces al compilar se aprovecha en parte el resultado de compilaciones anteriores.

Para facilitar esta eliminación las invocaciones de los compiladores deberían hacerse desde el propio directorio del fichero fuente. De esta manera los ficheros a eliminar serán todos los que tengan las extensiones indicadas y estén en ese mismo directorio. El seudocódigo del proceso será:

setlocal
  cd directorio-del-fichero-fuente
  borrar ficheros objeto *.xxx
  invocar el análisis
  borrar ficheros objeto *.xxx
endlocal

Ejemplo:

...>comprobar-cc tortuga.cpp
tortuga.cpp: In function `void Avanzar(t_tortuga&, t_casilla (*)[21])':
tortuga.cpp:73: warning: switch missing default case

1.3 Analizar un fichero fuente de cualquiera de los tipos admitidos

El guión de órdenes comprobar1.bat debe recibir como argumento el nombre de un fichero, e invocar la orden de comprobación correspondiente al lenguaje de programación de ese fichero. Este lenguaje se deduce de la extensión del nombre. La tabla del apartado anterior recoge las extensiones admisibles para los ficheros fuente de cada lenguage.

Si la extensión del nombre del fichero no es una de las aceptables se debe dar un mensaje de aviso indicándolo. Y en cualquier caso conviene emitir siempre al principio un mensaje indicando el nombre exacto del fichero que se va a procesar.

Ejemplo:

...>comprobar1 tortuga.cpp
---- Comprobando tortuga.cpp ...
tortuga.cpp: In function `void Avanzar(t_tortuga&, t_casilla (*)[21])':
tortuga.cpp:73: warning: switch missing default case

...>comprobar1 dibujar.adb
---- Comprobando dibujar.adb ...
dibujar.adb:20:11: (style) bad casing of "CurvaC" declared at curvac.adb:16

...>comprobar1 ajustar.dat
---- Comprobando ajustar.dat ...
-- 'ajustar.dat': tipo no aceptado '.dat'

...>comprobar1 prueba.xxx
---- No existe el fichero prueba.xxx

1.4 Analizar uno o varios ficheros fuente de diversos tipos

El guión de órdenes comprobar.bat puede recibir como argumento una lista de nombres de ficheros o patrones de nombre, con comodines, y debe invocar comprobar1.bat para cada uno de los ficheros.

Se asume que este guión de órdenes será invocado manualmente. Por lo tanto debe hacer comprobaciones de los argumentos de llamada y dar mensajes de aviso en caso de que sean inadecuados. Estas comprobaciones pueden hacerse en el código de este guión de órdenes o en el de comprobar1.bat, según convenga.

Ejemplo:

...>comprobar *.cpp

---- Comprobando curvac.cpp ...

---- Comprobando dibujar.cpp ...
dibujar.cpp:42: warning: unused parameter 'argc'
dibujar.cpp:42: warning: unused parameter 'argv'

---- Comprobando papel.cpp ...
papel.cpp: In function `void PonerEnBlanco(t_casilla (*)[21])':
papel.cpp:24: warning: declaration of 'papel' shadows a global declaration
papel.cpp:10: warning: shadowed declaration is here

---- Comprobando ppapel.cpp ...
ppapel.cpp:33: warning: unused parameter 'argc'
ppapel.cpp:33: warning: unused parameter 'argv'

---- Comprobando ptortuga.cpp ...
ptortuga.cpp:28: warning: unused parameter 'argc'
ptortuga.cpp:28: warning: unused parameter 'argv'

---- Comprobando tortuga.cpp ...
tortuga.cpp: In function `void Avanzar(t_tortuga&, t_casilla (*)[21])':
tortuga.cpp:73: warning: switch missing default case

...> comprobar
---- Falta(n) argumento(s)
-- Uso: comprobar ficheros-o-patrones

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 comprobar*.bat. Ejemplo:
    H:\entornos\practica1
       |- bin
       |  `- ...
       |- prueba.ada
       |  `- ...
       `- p1.bat    --->  path H:\entornos\practica1\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 análisis.
  2. Para determinar si el tipo de fichero es uno de los admisibles es mejor usar un bucle for:
    for %%X in (.adb .ads .awk ...) goto ...
    en lugar de una cadena de if:
    if %ext%==.adb goto ...
    if %ext%==.ads goto ...
    if %ext%==.awk goto ...
    ...

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 órdenes de análisis similares a las que se han dado como ejemplo, y comprobar que se obtienen resultados análogos.
  3. Comprobar que no quedan ficheros objeto en los directorios del código de ejemplo después de realizar las pruebas.