martes, mayo 23, 2006

cojo-gestión de configuración

Este post me ha llevado un poco más de la cuenta, pero es que se las trae... espero que a los informáticos que leen el blog les sirva de algo, y a los profanos... bueno, a lo mejor aprenden que no es oro todo lo que reluce.
Hay un refrán que dice "en casa del herrero cuchillo de palo", lo cuál es bastante cierto si hablamos de informática. Parece que los ingenierillos disfrutan haciendo trabajo de monos espaciales. Se enciende una luz y tiras de una palanca. Repítelo un millón de veces y tendrás a un especialista en ingeniería del software.

Con la ingeniería del software me pasa lo mismo que con el comunismo. Las dos cosas me gustan, pero no puedo decir lo mismo de los ingenieros del software y los comunistas, que suelen ser gilipollas.

Vayamos al meollo de la cuestión. En las asignaturas de "injenierida del chofgüer" y derivadas parece que son asignaturas en las que enseñan a manejar el "Guord", a poner bien de colorines el documento, a hacerte unos encabezados muy chulos, a hacer un montón de fichas en tablitas para requisitos y componentes... y los alumnos, en cuanto tienen que pensar un poco, se quedan pasmados con el dedo en la nariz (ver diagrama:
Free Image Hosting at www.ImageShack.us)
y repiten como buenos simios amaestrados que son. Los más afortunados recibirán su plátano, trabajarán de consultores ganando una pasta y se sentirán dichosos de haber triunfado en la vida. Los menos, acabarán con estos jefes.

Siempre he pensado que la informática se creó con los siguientes objetivos:
  1. Automatizar tareas.
  2. Asistir en la resolución de problemas.
  3. Almacenar datos.
(Y el punto dos se puede abstraer en el punto uno, teniendo en cuenta que la resolución de problemas por ordenador es automatizar procedimientos de resolución.)

Luego vino el ocio, la multimierda y tal, pero eso ya es harina de otro costal y se me escapa un poco del tema... hmmmm... no, ya me he escapado, voy a intentar volver a lo que tenía en la cabeza desde un principio.

Estaba intentando describir cómo para la última práctica de la carrera (Sistemas Informáticos) nos hemos desviado un poco de las típicas chapuzas artesanales y hemos automatizado los procesos de documentación todo lo que hemos podido (se podría haber hecho más, pero hay que encontrar un equilibrio entre el tiempo que empleas en mejorar los procesos y el tiempo que te ahorras con los procesos mejorados); con el fin de poder recurrir aquí cuando quiera repetir y de paso que cualquier otro se pueda subir al carro de los elegidos.

Empecemos enumerando los requisitos necesarios para montar el sistema:
  • Servidor unix (o windows con cygwin) conectado a la red.
    • cvs
    • ssh
    • make
    • openjade (y todas las utilidades para docbook)
    • ghostscript
    • xmllint
    • python
  • Clientes
    • edición xml (posibles alternativas)
      • jedit + plugin xml (recomendada)
      • vex (algo wysiwyg, pero todavía incompleto... como ventaja se puede incluir como workspace en eclipse).
      • kxml (no está mal para ver la estructura, pero pesado para editar)
      • emacs + sgml mode (un clásico que nunca falla)
    • cliente cvs (posibles alternativas)
      • cvs a pelo (algo coñazo, pero efectivo y disponible en casi todas partes)
      • tortoise cvs (cómodo y divertido, para windows)
      • eclipse (es lo más cómodo si ya lo usas para desarrollar y editar xml)
    • hoja de cálculo
      • oocalc (recomendada, porque exporta como queremos)
      • gnumeric (copy & paste a un vi o emacs para exportar)
      • excel (copy & paste a un notepad para exportar)
Ahora vamos a explicar cómo se monta el chiringuito para hacer URD's, SRD's y otras mierdas variadas:
  1. Montar las cuentas de usuarios
  2. Montar un repositorio CVS:
    1. entrar en el directorio que queramos tener como repositorio y ejecutar el comando "cvs init" (un sitio típico es "/var/cvs", aunque puede estar en el home de una cuenta especial para el proyecto, a la que tienen permiso de acceso, lectura y escritura los miembros del grupo)
    2. crear un módulo para la documentación (la manera más fácil es hacerlo con el asistente del eclipse, en el menú contextual sobre un proyecto "team > share project", o en la línea de comandos con "cvs import nombre-modulo grupo version")
  3. Crear uno directorio para cada grupo de:
    • Documentos
    • Imágenes compartidas (logotipos, etc.)
    • Plantillas comunes
  4. Crear un directorio para el primer documento (bajo el de documentos):
    1. Crear un Makefile (que luego explicaremos qué tiene).
    2. Subdirectorio para las imágenes propias del documento.
    3. Subdirectorio para los requisitos, con sus scripts y más cosas (luego lo explicamos).
    4. Documentos xml para cada capítulo ó sección (con el grano todo lo fino que queramos) y un documento maestro (también explicamos cómo funciona ahora).
Aquí está la disposición de nuestro módulo de documentación para Sistemas Informáticos (con algunas cosas abreviadas y con comentarios seguidos de una #):

workspace/ssii_docs/
|-- CVS #esto es información del cvs, los siguientes están abrevidados
| |-- Entries
| |-- Repository
| |-- Root
| `-- Template
|-- diagramas #diagramas del rational y otros dibujitos
| |-- Bender.emx
| |-- CVS
| |-- Disenyo.mdx
| |-- Disenyo.wdx
| |-- Disenyo5EC87526B7F54B30B9A09AFCE8E3EBA7.idx
| |-- Mermelada.mdx
| |-- Mermelada.wdx
| |-- build.xml
| |-- disenyo1037AB48F0554ECA9D513EDDDE78C57A.idx
| |-- instalacion.svg
| `-- sensor_tapon.svg
|-- documentos
| |-- CVS
| |-- anteproyecto #el anteproyecto, abarcará URD y SRD
| | |-- CVS
| | |-- Makefile #fantástico makefile
| | |-- anteproyecto.xml #documento maestro
| | |-- anteproyecto_final.pdf #el resultado final
| | |-- anteproyecto_final.ps #un resultado intermedio
| | |-- anteproyecto_final.xml #un resultado intermedio
| | |-- chap_descripcion_modelo_logico.xml #los siguientes chap*.xml son capítulos
| | |-- chap_disenno_arquitectonico.xml
| | |-- chap_estimacion.xml
| | |-- chap_introduccion.xml
| | |-- chap_maquinas_estados.xml
| | |-- chap_planificacion.xml
| | |-- chap_plano_instalacion.xml
| | |-- chap_presupuesto.xml
| | |-- chap_pruebas.xml
| | |-- chap_requisitos_software.xml
| | |-- chap_requisitos_usuario.xml
| | |-- chap_resumen.xml
| | |-- cocomo #informes del cocomo
| | | |-- CVS
| | | |-- cocomo-report.rpt
| | | `-- cocomo.est
| | |-- images #gráficos para incrustar (*.ps y *.eps)
| | | |-- CVS
| | | |-- Mermelada__Arrancar__Actividad__Arrancar.ps
| | | |-- Mermelada__Arrancar__Secuencia__InteractionInstance1__Arrancar.ps
| | | |-- Mermelada__Casos_uso.ps
| | | |-- Mermelada__Clases_analisis.ps
| | | |-- Mermelada__Configurar__Actividad__Configurar.ps
| | | |-- Mermelada__Configurar__Secuencia__InteractionInstance1__Configurar.ps
| | | |-- Mermelada__Despliegue.ps
| | | |-- Mermelada__Emergencia__Actividad__Emergencia.ps
| | | |-- Mermelada__Emergencia__Secuencia__InteractionInstance1__Emergencia.ps
| | | |-- Mermelada__Generar_informes__Actividad__Generar_informes.ps
| | | |-- Mermelada__Generar_informes__Secuencia__InteractionInstance1__Generar_informes.ps
| | | |-- Mermelada__Parar__Actividad__Parar.ps
| | | |-- Mermelada__Parar__Secuencia__InteractionInstance1__Parar.ps
| | | |-- Mermelada__esclavo 1__estados.ps
| | | |-- Mermelada__esclavo2__estados.ps
| | | |-- Mermelada__maestro__estados.ps
| | | |-- bender.eps
| | | |-- cocomo.eps
| | | |-- costes-proyecto.eps
| | | |-- costos-proyecto.pdf
| | | |-- esclavo1.eps
| | | |-- esclavo2.eps
| | | |-- instalacion.ps
| | | |-- maestro.eps
| | | |-- planificacion.ps
| | | |-- sensor_tapon.ps
| | | `-- vaf.eps
| | |-- presupuesto #una simple hoja de cálculo para trabajar con ella
| | | |-- CVS
| | | `-- hardware.ods
| | |-- pruebas_aceptacion #cositas para las pruebas
| | | |-- CVS
| | | |-- hoja_pruebas_aceptacion.csv #el fichero que se procesa, derivado del excel
| | | |-- hoja_pruebas_aceptacion.excel.xml #fichero de excel para trabajar
| | | |-- procesar_pruebas.py #script que procesa el csv y lo transforma en xml docbook
| | | `-- pruebas_aceptacion.docbook.xml #fragmento de docbook
| | `-- requisitos #los requisitos de usuario y del software
| | |-- CVS
| | |-- README.TXT #instrucciones para usarlos
| | |-- hoja_requisitos.csv #fichero para procesar, derivado del xls
| | |-- hoja_requisitos.xls #fichero de excel, para trabajar
| | |-- matriz-d-sr-ur.docbook.xml #matriz de trazabilidad, derivada del script
| | |-- matriz-i-sr-ur.docbook.xml #matriz de trazabilidad, derivada del script
| | |-- matriz.py #módulo genérico para las matrices de trazabilidad
| | |-- matriz.pyc #bytecode de python
| | |-- procesar_lista_requisitos.py #script que genera listas de requisitos
| | |-- procesar_matriz_directa.py #script que genera una matriz de trazabilidad directa
| | |-- procesar_matriz_inversa.py #script que genera una matriz de trazabilidad inversa
| | |-- requisitos-sr.docbook.xml #fragmento docbook, derivado
| | |-- requisitos-ur.docbook.xml #fragmento docbook, derivado
| | |-- requisitos.py #modulo generico para los requisitos
| | |-- requisitos.pyc #bytecode de python
| | `-- urd.xml #fragmento de docbook, que incrusta los *.docbook.xml
| |-- disenyo #otro documento, más de lo mismo
| | |-- CVS
| | |-- Makefile
| | |-- chap_contexto.xml
| | |-- chap_descripcion.xml
| | |-- chap_descripcion_completo.xml
| | |-- chap_disenyo.xml
| | |-- chap_estandares.xml
| | |-- chap_introduccion.xml
| | |-- chap_viabilidad.xml
| | |-- chap_vision_sistema.xml
| | |-- componentes #descripción detallada de los componentes software
| | | |-- CVS
| | | |-- matriz-trazabilidad.docbook.xml #derivado
| | | |-- modelo_componentes.xls
| | | |-- modulo_automatas #una carpetita para modulito
| | | | |-- CVS
| | | | |-- especificacion-componentes.docbook.xml #fichero derivado del csv
| | | | |-- modelo_componentes.csv #csv exportado del xls
| | | | |-- modelo_componentes.xls #descripción de los componentes
| | | | `-- pseudocodigo #pseudocódigo de los componentes
| | | | |-- CVS
| | | | |-- TODO
| | | | |-- comp_AUT-003.txt
| | | | |-- comp_AUT-004.txt
| | | | |-- comp_AUT-005.txt
| | | | |-- ... #muchos más
| | | | `-- comp_AUT-059.txt
| | | |-- modulo_comunicaciones #otro modulito, igual que el anterior
| | | | |-- CVS
| | | | |-- especificacion-componentes.docbook.xml
| | | | |-- modelo_componentes.csv
| | | | |-- modelo_componentes.xls
| | | | `-- pseudocodigo
| | | | |-- CVS
| | | | |-- comp_COM-003.txt
| | | | |-- comp_COM-004.txt
| | | | |-- ... #muuuuuuchos más
| | | | `-- comp_COM-100.txt
| | | |-- ... #un par de módulos más
| | | `-- scripts
| | | |-- CVS
| | | |-- modulos.py #modulo genérico para los módulos... vaya juego de palabras
| | | |-- modulos.pyc #bytecode de python
| | | |-- procesar_lista_modulos.py #genera la lista de los módulos de un componente
| | | `-- procesar_matriz_trazabilidad.py #genera las matrices de trazabilidad
| | |-- disenyo.xml #documento maestro
| | |-- disenyo_final.pdf #resultado final
| | |-- disenyo_final.ps #resultado intermedio
| | |-- disenyo_final.xml #resultado intermedio
| | `-- images #graficos para incrustar
| | |-- CVS
| | |-- Disenyo__Automatas__Main.eps
| | |-- Disenyo__Comunicaciones__Buzon__Main.eps
| | |-- Disenyo__Comunicaciones__Main.eps
| | |-- Disenyo__Comunicaciones__Mensajes__Main.eps
| | |-- Disenyo__GestorInformes__Main.eps
| | |-- Disenyo__Main.eps
| | |-- Disenyo__SCADA__Main.eps
| | |-- Mermelada__Automatas__Main.ps
| | |-- Mermelada__Comunicaciones__Buzon__Main.ps
| | |-- Mermelada__Comunicaciones__Main.ps
| | |-- Mermelada__Comunicaciones__Mensajes__Main.ps
| | |-- Mermelada__GestorInformes__Main.ps
| | |-- Mermelada__Main.ps
| | |-- Mermelada__SCADA__Main.ps
| | |-- bender.eps
| | `-- diagrama_contexto.ps
| |-- manual_usuario #el manual. estamos trabajando en él ahora mismo
| | |-- CVS
| | |-- Makefile
| | |-- images
| | | |-- CVS
| | | `-- bender.eps
| | `-- sum.xml
| `-- stylesheets #plantillas comunes (sólo se usa custom.dsl, las otras son de referencia)
| |-- CVS
| |-- cogent-both.dsl
| |-- custom.dsl
| |-- freebsd_tuned.dsl
| |-- gdp-both.dsl
| `-- guide.dsl
`-- plantillas_docbook #ejemplos de archivos docbook
|-- CVS
|-- Makefile #ejemplo de makefile
|-- articulo.xml #ejemplo de articulo, para los manuales
|-- chap1src.xml #ejemplo de capitulo, para incluir en documentos
|-- images #varios logos, para guarrear
| |-- CVS
| |-- logo.wmf
| |-- logotipo1.gif
|-- libro.xml #ejemplo de libro
|-- prueba.xml #no sé qué tenía este, creo que era un ejemplo de xinclude
`-- stylesheets
|-- CVS
|-- cogent-both.dsl
|-- custom.dsl
|-- gdp-both.dsl
`-- ldp.dsl

57 directories, 468 files
Ahora vamos a hablar un poco más detalladamente de cada tipo de archivo.

Los XML de DocBook

Para el que no conozca DocBook, habría que decir que es un lenguaje de marcas XML diseñado para escribir documentos estructurados. Los documentos estructurados son aquellos que tienen partes, capítulos, secciones, subsecciones, etc.

La edición de estos documentos puede ser un tanto tediosa si se hace a mano sin ningún editor especializado (que nos ayude con las marcas), pero tiene la ventaja de que al ser texto plano (bueno, al ser XML no es plano, es un árbol :-P), lo cuál tiene algunas recompensas:
  • Se puede gestionar directamente con un SCV, como CVS ó SVN, con lo cuál se mantiene un histórico de las revisiones, se puede volver a una versión anterior en el caso de fallo, se puede ver quién ha escrito cada cosa...
  • Se pueden hacer fácilmente programas que escriban este tipo de documentos (lo cuál lo explotaremos bastante).
  • Se puede editar en (casi) cualquier parte.
Una nota a tener en cuenta aquí, es que como estamos usando OpenJade, le gustan los documentos que están codificados en el formato ISO-8859-1 (eso, o escapáis todos los caracteres acentuados), para lo cuál deberéis especificarlo en las opciones del editor que uséis.

Nosotros también queremos tener documentos modulares, lo cuál lo conseguiremos con dos tecnologías: XInclude y las System Entities de XML.

XInclude
XInclude tiene una ventaja y un inconveniente principales. La ventaja es que permite hacer un pre-procesado de los documentos a incluir, como puede ser seleccionar una rama del XML concreta para incluir ó escapar los caracteres no válidos en un documento XML al incluir texto plano (como los > y los <, por ejemplo).

El inconveniente es que el motor de render que usamos (Jade) no lo procesa correctamente (para los que usen XSL-FO no creo que tengan este problema, pero yo ya había apostado por Jade, ¡y uno no aprende DSSSL para luego cambiarse!).

Para solucionar el problema de procesamiento, optamos por hacer un pre-procesado de los documentos con una herramienta externa, en este caso xmllint.

System Entities
Es un mecanismo bastante más sencillo que el XInclude, ya que simplemente vuelva el contenido del fichero incluido en el maestro, lo cuál tiene el problema de que por lo general, los documentos que incluyamos no serán de por sí documentos DocBook válidos (por las cabeceras y tal) y no serán reconocidos como tal por los editores XML.

Esta característica nos viene muy bien para incluir los cachos de DocBook generados por nuestros programas, ya que en ellos no querremos perder tiempo declarando cabeceras, ni puede que sepamos de antemano en qué rama estarán metidos.

Gráficos

Como habréis podido observar, los gráficos están en formato PS ó EPS, que son formatos de gráficos vectoriales, lo cuál nos vendrá bien para que los diagramas no pierdan calidad en los documentos.

El problema que tienen estos gráficos es que están definidos en un lenguaje de programación de impresoras (que por cierto, es muy divertido aprenderlo, yo lo estoy haciendo) y no hay programas para editarlos directamente... pero por suerte, desde (casi) cualquier programa se puede exportar a PostScript, si bien no directamente, sí mediante la opción de imprimir a un archivo (habiendo instalado los drivers de una impresora PostScript, como los de casi cualquier impresora Láser).

Un pequeño escollo con el que nos encontramos aquí es que al generar los PDF's a partir de DocBook con Jade, nos dice que no le gustan los ficheros PS ni EPS. La solución es crear un PS con Jade y luego convertirlo a PDF con ps2pdf.

Las hojas de cálculo XLS y los CSV

Las hojas de cálculo son para editar registros de formato fijo, como son las típicas plantillas de requisitos y módulos de sofware. Con un poco de arte, se pueden automatizar varias cosillas, como la numeración automática, la selección de enumerados, etc.

Aquí os pongo un par de pantallazos para que veáis de qué hablo:
Free Image Hosting at www.ImageShack.usFree Image Hosting at www.ImageShack.us

El problema de usar los XLS es que son ficheros binarios y no se llevan muy bien con el CVS, así que si usáis Excel (a partir del 2003, creo), podéis guardarlos en el XML ese guarruzo de Microsoft. Si usáis OpenOffice, podéis seguir el tutorial de Pesso para guardar los documentos ODF en texto plano que enlazaré en cuanto esté listo. Si usáis Gnumeric, sólo tenéis que ir a preferencias, y especificar que el nivel de compresión de los archivos es 0.

Los CSV tienen algunas consideraciones especiales, para que puedan ser procesados más fácilmente:
  • Usar el tabulador como separador.
  • No usar delimitadores para el texto.
  • Estar codificados en UTF-8

Los scripts en Python

Para los que no lo conozcan, Python es un lenguaje dinámico interpretado, que te permite hacer muchas guarrerías de forma elegante y rápida, por lo que resulta ideal para lo que queremos hacer aquí.

Estos scripts también deberían estar codificados en UTF-8.

Estos scripts leeran los CSV y generarán cachos de DocBook a porrillo... como no hay mucho más que decir, aquí los pongo tal cual:

anteproyecto/requisitos/requisitos.py

#!/usr/bin/python
# -*- coding: UTF-8 -*-
import sys

"""Encapsula un requisito"""
class Requirement:
def __init__(self):
self.id = ""
self.type = ""
self.nid = -1
self.priority = ""
self.need = ""
self.stability = ""
self.source = ""
self.description = ""
self.verification = ""
self.dependencies = []
def __init__(self, line):
self.parse(line)

def parse(self, line):
tokens = line.strip().split("\t")
self.type = tokens[0]
self.nid = int(tokens[1])
self.id = tokens[2]
self.priority = tokens[3]
self.need = tokens[4]
self.stability = tokens[5]
self.source = tokens[6]
self.description = tokens[7]
self.verification = tokens[8]
self.dependencies = tokens[9:]

def printDocBookSheet(self):
print "<table id =\""+self.id+"\">"
print "<title>"+self.id+"</title>"
print "<tgroup cols=\"4\"><tbody>"
print "<row>"
print "<entry><emphasis role=\"strong\">Prioridad</emphasis></entry>"
print "<entry>", self.priority,"</entry>"
print "<entry><emphasis role=\"strong\">Necesidad</emphasis></entry>"
print "<entry>", self.need,"</entry>"
print "</row>"
print "<row>"
print "<entry><emphasis role=\"strong\">Estabilidad</emphasis></entry>"
print "<entry>", self.stability, "</entry>"
print "<entry><emphasis role=\"strong\">Fuente</emphasis></entry>"
print "<entry>", self.source, "</entry>"
print "</row>"
print "</tbody></tgroup>"
print "<tgroup cols=\"1\"><tbody>"
print "<row>"
print "<entry><emphasis role=\"strong\">Descripción</emphasis></entry>"
print "</row>"
print "<row>"
print "<entry>", self.description, "</entry>"
print "</row>"
print "<row><entry><emphasis role=\"strong\">Dependencias</emphasis></entry></row>"
print "<row><entry>"
for i in self.dependencies:
print "<link linkend=\"" +i+"\">", i, "</link>"
print "</entry></row>"
print "</tbody></tgroup>"
print "</table>"

def createRequirementsList():
reqs = []
headings = True
for line in sys.stdin.readlines():
if headings:
if line.startswith("\t"):
headings = False
else:
if not line.startswith("\t"):
r = Requirement(line)
reqs.append(r)
return reqs
anteproyecto/procesar_lista_requisitos.py

from requisitos import *
import sys

def main():
type = sys.argv[1]

reqs = createRequirementsList()

for r in reqs:
if r.type == type:
r.printDocBookSheet()

main()

anteproyecto/requisitos/matriz.py

from requisitos import *

def directTraceabilityMatrix(reqs, fromType, toType):
matrix = []
types = {}
for i in reqs:
types[i.id] = i.type

for i in reqs:
#si tenemos un requisito del tipo de partida
if i.type == fromType:
#cogemos las dependencias del tipo de destino
d = [j for j in i.dependencies if types[j] == toType]
matrix.append((i.id, d))
return matrix

def inverseTraceabilityMatrix(reqs, fromType, toType):
matrix = []
table = {}
types = {}

#inicializamos la tabla
for i in reqs:
if i.type == toType:
table[i.id] = []

for i in reqs:
types[i.id] = i.type

for i in reqs:
#si tenemos un requisito del tipo de destino
if i.type == fromType:
#miramos las dependencias del tipo de partida
for j in i.dependencies:
if types[j] == toType:
table[j].append(i.id)

#pasamos de la tabla a la lista
for i in table:
matrix.append((i, table[i]))

matrix.sort()

return matrix

def printTraceabilityMatrix(matrix, title):
print "<table>"
print "<title>", title, "</title>"
print "<tgroup cols = \"2\">"
print "<colspec colwidth=\"1*\"/>"
print "<colspec colwidth=\"4*\"/>"
print "<tbody>"
for i,j in matrix:
print "<row>"
print "<entry>", link(i), "</entry>"
print "<entry>"
for k in j:
print link(k)
print "</entry>"
print "</row>"
print "</tbody>"
print "</tgroup>"
print "</table>"
return

def link(r):
return "<link linkend=\""+r+"\">"+r+"</link>"

anteproyecto/requisitos/procesar_matriz_directa.py

from matriz import *

def main():
fromType = sys.argv[1]
toType = sys.argv[2]
title = sys.argv[3]

reqs = createRequirementsList()
matrix = directTraceabilityMatrix(reqs, fromType, toType)
printTraceabilityMatrix(matrix, title)

main()

anteproyecto/requisitos/procesar_matriz_inversa.py

from matriz import *

def main():
fromType = sys.argv[1]
toType = sys.argv[2]
title = sys.argv[3]

reqs = createRequirementsList()
matrix = inverseTraceabilityMatrix(reqs, fromType, toType)
printTraceabilityMatrix(matrix, title)

main()


Makefile (juntándolo todo)

Si alguna vez has utilizado "make", seguramente ya tendrás una idea de cómo funcionan los Makefiles, pero por si acaso, primero haré una breve introducción.

La herramienta make sirve para gestionar dependencias en proyectos, por lo que normalmente se asocia con programas en C (para no tener que compilar módulos que no han sido modificados), pero se puede usar para automatizar casi cualquier proceso de flujo de datos.

Los ficheros Makefile están formados por una lista de reglas, que están separadas entre sí por una o más líneas en blanco. Las reglas tienen 3 partes:
  • Objetivo (nombre del fichero a actualizar o de la regla)
  • Dependencias (otras reglas o nombres de ficheros de los que dependa el objetivo)
  • Acciones (comandos que se ejecutan para crear o actualizar el objetivo)
Así que tenemos que una regla es:
objetivo : dep1 dep2 dep3 #y más... a partir de la almohadilla va comentado
accion1
accion2
#y más...


Ahora aquí van los Makefiles que hemos usado:

anteproyecto/Makefile
NAME = anteproyecto

FLAGS = --dcl `if test -e /usr/share/sgml/xml.dcl ;then echo /usr/share/sgml/xml.dcl; else locate xml.dcl | tail -n1; fi`
STYLESHEET = ../stylesheets/custom.dsl

MOD_REQ = requisitos/requisitos.py
MOD_MAT = requisitos/matriz.py
PROC_LIST_REQ = requisitos/procesar_lista_requisitos.py
PROC_MAT_D_REQ = requisitos/procesar_matriz_directa.py
PROC_MAT_I_REQ = requisitos/procesar_matriz_inversa.py

HOJA_REQUISITOS = requisitos/hoja_requisitos.csv
MATRIZ_D_SR_UR = requisitos/matriz-d-sr-ur.docbook.xml
MATRIZ_I_SR_UR = requisitos/matriz-i-sr-ur.docbook.xml

all: pdf view

view:
xpdf $(NAME)_final.pdf || evince $(NAME)_final.pdf || acroread $(NAME)_final.pdf

pdf: $(NAME)_final.pdf

html: $(NAME)_final.xml $(STYLESHEET)
db2html $(FLAGS) $(NAME)_final.xml
cp -r images $(NAME)_final

$(NAME)_final.pdf: $(NAME)_final.ps
ps2pdf $<

$(NAME)_final.ps: $(STYLESHEET) $(NAME)_final.xml ../images/* images/*
db2ps $(FLAGS) -d $(STYLESHEET) $(NAME)_final.xml

$(NAME)_final.xml: $(NAME).xml chap*.xml requisitos matrices pruebas cocomo/*
xmllint --noent --encode "iso-8859-1" --xinclude $(NAME).xml > $@

#REQUISITOS

requisitos: requisitos/requisitos-ur.docbook.xml requisitos/requisitos-sr.docbook.xml

requisitos/hoja_requisitos.csv: requisitos/hoja_requisitos.xls
echo $@ : HAY QUE ACTUALIZAR EL CSV A MANO!!!
false

requisitos/requisitos-ur.docbook.xml: requisitos/hoja_requisitos.csv $(PROC_LIST_REQ)
cat $< | python $(PROC_LIST_REQ) UR > $@

requisitos/requisitos-sr.docbook.xml: requisitos/hoja_requisitos.csv $(PROC_LIST_REQ)
cat $< | python $(PROC_LIST_REQ) SR > $@

#MATRICES REQUISITOS
matrices: $(MATRIZ_D_SR_UR) $(MATRIZ_I_SR_UR)

$(MATRIZ_D_SR_UR): $(HOJA_REQUISITOS) $(PROC_MAT_D_REQ) $(MOD_REQ) $(MOD_MAT)
cat $< | python $(PROC_MAT_D_REQ) SR UR "Matriz de dependencias directas SR-UR" > $@

$(MATRIZ_I_SR_UR): $(HOJA_REQUISITOS) $(PROC_MAT_I_REQ) $(MOD_REQ) $(MOD_MAT)
cat $< | python $(PROC_MAT_I_REQ) SR UR "Matriz de dependencias inversas SR-UR" > $@

#PRUEBAS ACEPTACION

pruebas: pruebas_aceptacion/pruebas_aceptacion.docbook.xml

pruebas_aceptacion/hoja_pruebas_aceptacion.csv: pruebas_aceptacion/hoja_pruebas_aceptacion.excel.xml
echo $@ : HAY QUE ACTUALIZAR EL CSV A MANO!!!
false

pruebas_aceptacion/pruebas_aceptacion.docbook.xml: pruebas_aceptacion/hoja_pruebas_aceptacion.csv pruebas_aceptacion/procesar_pruebas.py
cat $< | python pruebas_aceptacion/procesar_pruebas.py PA > $@


#PRESUPUESTO


#CLEAN
clean:
rm -rf $(NAME)_final || true
rm -rf $(NAME)_final.junk || true
rm $(NAME)_final.xml || true
rm *.tex *.aux *.dvi *.log *.out *.ps *.pdf || true
rm requisitos/requisitos-??.docbook.xml || true
rm requisitos/matriz-?-??-??.docbook.xml || true
rm pruebas_aceptacion/pruebas_aceptacion.docbook.xml || true
disenyo/Makefile
NAME = disenyo

FLAGS = --dcl `if test -e /usr/share/sgml/xml.dcl ;then echo /usr/share/sgml/xml.dcl; else locate xml.dcl | tail -n1; fi`
STYLESHEET = ../stylesheets/custom.dsl

HOJA_AUT = componentes/modulo_automatas
HOJA_GEI = componentes/modulo_gestor_informes
HOJA_GUI = componentes/modulo_gui
HOJA_COM = componentes/modulo_comunicaciones
SCRIPT_COMPONTENTES = componentes/scripts/procesar_lista_modulos.py
SCRIPT_MATRIZ = componentes/scripts/procesar_matriz_trazabilidad.py

all: pdf view componentes

view:
xpdf $(NAME)_final.pdf || evince $(NAME)_final.pdf || acroread $(NAME)_final.pdf

pdf: $(NAME)_final.pdf

html: $(NAME)_final.xml $(STYLESHEET)
db2html $(FLAGS) $(NAME)_final.xml
cp -r images $(NAME)_final

$(NAME)_final.pdf: $(NAME)_final.ps
ps2pdf $<

$(NAME)_final.ps: $(STYLESHEET) $(NAME)_final.xml ../images/* images/*
db2ps $(FLAGS) -d $(STYLESHEET) $(NAME)_final.xml

preprocesado_xinclude_nivel2: chap_descripcion.xml
xmllint --noent --encode "iso-8859-1" --xinclude chap_descripcion.xml > chap_descripcion_completo.xml

$(NAME)_final.xml: $(NAME).xml chap*.xml matriz componentes preprocesado_xinclude_nivel2
xmllint --noent --encode "iso-8859-1" --xinclude $(NAME).xml > $@

#COMPONENTES

matriz:
python ${SCRIPT_MATRIZ} ${HOJA_AUT}/modelo_componentes.csv ${HOJA_COM}/modelo_componentes.csv ${HOJA_GEI}/modelo_componentes.csv ${HOJA_GUI}/modelo_componentes.csv > componentes/matriz-trazabilidad.docbook.xml

componentes: ${HOJA_AUT}/especificacion-componentes.docbook.xml ${HOJA_COM}/especificacion-componentes.docbook.xml ${HOJA_GEI}/especificacion-componentes.docbook.xml ${HOJA_GUI}/especificacion-componentes.docbook.xml

${HOJA_AUT}/modelo_componentes.csv: ${HOJA_AUT}/modelo_componentes.xls
echo $@ : HAY QUE ACTUALIZAR EL CSV ${HOJA_AUT} A MANO!!!
false

${HOJA_COM}/modelo_componentes.csv: ${HOJA_COM}/modelo_componentes.xls
echo $@ : HAY QUE ACTUALIZAR EL CSV ${HOJA_COM} A MANO!!!
false

${HOJA_GEI}/modelo_componentes.csv: ${HOJA_GEI}/modelo_componentes.xls
echo $@ : HAY QUE ACTUALIZAR EL CSV ${HOJA_GEI} A MANO!!!
false

${HOJA_GUI}/modelo_componentes.csv: ${HOJA_GUI}/modelo_componentes.xls
echo $@ : HAY QUE ACTUALIZAR EL CSV ${HOJA_GUI} A MANO!!!
false

${HOJA_AUT}/especificacion-componentes.docbook.xml: ${HOJA_AUT}/modelo_componentes.csv $(SCRIPT_COMPONTENTES)
cat $< | python $(SCRIPT_COMPONTENTES) ${HOJA_AUT}/pseudocodigo < ${HOJA_AUT}/modelo_componentes.csv > $@

${HOJA_COM}/especificacion-componentes.docbook.xml: ${HOJA_COM}/modelo_componentes.csv $(SCRIPT_COMPONTENTES)
cat $< | python $(SCRIPT_COMPONTENTES) ${HOJA_COM}/pseudocodigo < ${HOJA_COM}/modelo_componentes.csv > $@

${HOJA_GEI}/especificacion-componentes.docbook.xml: ${HOJA_GEI}/modelo_componentes.csv $(SCRIPT_COMPONTENTES)
cat $< | python $(SCRIPT_COMPONTENTES) ${HOJA_GEI}/pseudocodigo < ${HOJA_GEI}/modelo_componentes.csv > $@

${HOJA_GUI}/especificacion-componentes.docbook.xml: ${HOJA_GUI}/modelo_componentes.csv $(SCRIPT_COMPONTENTES)
cat $< | python $(SCRIPT_COMPONTENTES) ${HOJA_GUI}/pseudocodigo < ${HOJA_GUI}/modelo_componentes.csv > $@

#CLEAN
clean:
rm `find componentes -name "*.docbook.xml"` || true
rm -rf $(NAME)_final || true
rm -rf $(NAME)_final.junk || true
rm $(NAME)_final.xml || true
rm *.tex *.aux *.dvi *.log *.out *.ps *.pdf || true



Bueno, pues hasta aquí hemos llegado, que ya estoy hasta los cojones de escribir... si encuentro algún josting de archivos chulo, cuelgo un tbz con todo (o si alguien lo quiere, que me mande un mail a fortranito arroba gmail punto com).

Y recordad: ¡make es vuestro amigo!

8 comentarios:

  1. Muy currado... aunque en lugar de CVS quizá yo hubiera optado por SVN........ aunque Eclipse no lo trae integrado (tiene unos cuantos plugins para ello).

    ResponderEliminar
  2. je je je, si alguien hiciese un estudio serio sobre el peso que tienen los plugins del Eclipse a la hora de escoger herramientas y tecnologías...

    por ejemplo, yo para hacer un parser en Java escogí ANTLR frente a JavaCC porque el plugin para Eclipse me pareció mejor, pese a que como toolkit me pareció más coherente y currado JavaCC.

    ResponderEliminar
  3. joer veo la piedra. Veo la primera respuesta y pienso Eclipse...... CVS...estan hablando de coches? ya no entiendo tampoco de coche?

    no entiendo nada xD que triste existencia

    ResponderEliminar
  4. vamos allá con las respuestas, Granaino ;-)

    a) supongo que sí que puede ser un problema, aunque toda la comunicación va por SSH y todas las conexiones se realizan de manera activa hacia el servidor, así que dependerá de lo estrictas que tengan las reglas... si permiten SSH, entonces OK.

    b) ahí le has dado, no hay una forma precisa de saber en qué página del PDF va cada cacho del XML... aunque por el tema de la modularidad, puedes hacer un fichero para cada capítulo, sección... incluso para cada párrafo (excesivo), con lo que mejoras un poco la localización de contenidos.

    c) lo de las imágenes no es problema, están incrustadas en el PDF y se ven como en cualquier documento (de hecho, el PDF es un PS compilado y con algunas restricciones más).

    d) meter un tag con la mayoría de los editores es seleccionar un cacho de texto y hacer click sobre el tag, así que en rapidez anda igual; la verdadera ventaja viene por el aspecto semántico. En DocBook hay un montón de marcas pensadas específicamente para documentar software, como por ejemplo GUIButton, ProgramListing, ComputerOutput, incluso para especificar interacciones con el software, (en plan menu->submenu->item), de modo que ahorras bastante tiempo pensando en cómo es la presentación de cada elemento (aunque luego los nombres de los botones estén con una simple negrita, pero eso se puede cambiar)...

    e) sip, es muy doloroso incluso cuando lo hacen los compañeros de práctica (no lo dicen, no sé si por vergüenza, timidez o yo qué sé, pero ponen mala cara, refunfuñan... coñe, si no gusta, se dice al principio). Contra eso, sólo se pueden hacer 2 cosas: usar editores visuales que lo dejen todo bien bonito (tenemos el VEX y el Conglomerate, pero todavía les queda un largo camino), o esperar a que la alfabetización informática cale un poco más en las empresas (difícil, difícil...)

    f) lo de las plantillas es un tema bastante doloroso... todo depende de si se tiene un gurú del lenguaje de transformación en concreto a mano. Nosotros hemos usado DSSSL (Document Style Sheet and Semantics Language), que es un derivado del Scheme (un LISP capado, vamos), que funciona en plan "pattern matching"; es decir, le pones un cacho de ruta XML y luego le dices en qué se transforma (con su propio lenguaje, que también es cosa fina); así que hacer modificaciones fáciles de Copy&Paste y cambia el color, el tipo o el tamaño de la letra es muy fácil, pero cuando quieres hacer cosas complicadas... para que te hagas una idea, los encabezados de nuestros títulos ponen "capítulo N" con la N bien grande y luego debajo, el título del capítulo, todo alineado a la derecha; el código que hace eso es:

    ;;------------------------------------------------------
    ;; Títtle for Chapter
    ;; It's changed only for chapter title.
    (define ($component-title$)
    (let* ((info (cond
    ((equal? (gi) (normalize "appendix"))
    (select-elements (children (current-node)) (normalize "docinfo")))
    ((equal? (gi) (normalize "article"))
    (node-list-filter-by-gi (children (current-node))
    (list (normalize "artheader")
    (normalize "articleinfo"))))
    ((equal? (gi) (normalize "bibliography"))
    (select-elements (children (current-node)) (normalize "docinfo")))
    ((equal? (gi) (normalize "chapter"))
    (select-elements (children (current-node)) (normalize "docinfo")))
    ((equal? (gi) (normalize "dedication"))
    (empty-node-list))
    ((equal? (gi) (normalize "glossary"))
    (select-elements (children (current-node)) (normalize "docinfo")))
    ((equal? (gi) (normalize "index"))
    (select-elements (children (current-node)) (normalize "docinfo")))
    ((equal? (gi) (normalize "preface"))
    (select-elements (children (current-node)) (normalize "docinfo")))
    ((equal? (gi) (normalize "reference"))
    (select-elements (children (current-node)) (normalize "docinfo")))
    ((equal? (gi) (normalize "setindex"))
    (select-elements (children (current-node)) (normalize "docinfo")))
    (else
    (empty-node-list))))
    (exp-children (if (node-list-empty? info)
    (empty-node-list)
    (expand-children (children info)
    (list (normalize "bookbiblio")
    (normalize "bibliomisc")
    (normalize "biblioset")))))
    (parent-titles (select-elements (children (current-node))
    (normalize "title")))

    (info-titles (select-elements exp-children (normalize "title")))
    (titles (if (node-list-empty? parent-titles)
    info-titles
    parent-titles))
    (subtitles (select-elements exp-children (normalize "subtitle"))))

    ;; ==================== Changed for Chapter ======================
    (if (or (equal? (gi) (normalize "chapter")) (equal? (gi) (normalize "appendix")))
    (make sequence
    ;; Parrafo con "Capitulo n"
    (make paragraph
    font-family-name: %title-font-family%
    ;; font-weight: 'bold
    font-weight: 'semi-light
    font-size: (HSIZE 5)
    space-before: 0pt
    ;; space-after: (* (HSIZE 6) %head-before-factor% )
    space-after: 1cm
    quadding: 'end
    heading-level: (if %generate-heading-level% 1 0)
    keep-with-next?: #t
    (if (string=? (element-label) "")
    (empty-sosofo)
    (make sequence
    line-spacing: (* (HSIZE 1) %line-spacing-factor%)
    (literal (gentext-element-name-space (current-node)))
    (make sequence
    font-size: (* (HSIZE 5) 2.5)
    font-weight: 'bold
    ;color: light-blue
    (literal (element-label))))))
    ;; Quito el punto (gentext-label-title-sep (gi))

    ;; Para with chapter title
    (make paragraph
    font-family-name: %title-font-family%
    font-weight: 'bold
    font-size: (HSIZE 5)
    line-spacing: (* (HSIZE 1) %line-spacing-factor%)
    ;; space-before: (* (HSIZE 10) %head-before-factor%)
    space-before: 0.5cm
    ;; space-after: (* (HSIZE 14) %head-before-factor%)
    ;; se suma al 1cm anterior
    space-after: 1.5cm
    start-indent: 0pt
    first-line-start-indent: 0pt
    quadding: 'end
    heading-level: (if %generate-heading-level% 1 0)
    keep-with-next?: #t
    (if (node-list-empty? titles)
    (element-title-sosofo) ;; get a default!
    (with-mode chapter-title-mode
    (make sequence
    (process-node-list titles))))))

    ;; If not chapter:
    (make sequence
    (make paragraph
    font-family-name: %title-font-family%
    font-weight: 'bold
    font-size: (HSIZE 4)
    line-spacing: (* (HSIZE 4) %line-spacing-factor%)
    space-before: (* (HSIZE 4) %head-before-factor%)
    start-indent: 0pt
    first-line-start-indent: 0pt
    quadding: %component-title-quadding%
    heading-level: (if %generate-heading-level% 1 0)
    keep-with-next?: #t

    (if (string=? (element-label) "")
    (empty-sosofo)
    (literal (gentext-element-name-space (current-node))
    (element-label)
    (gentext-label-title-sep (gi))))

    (if (node-list-empty? titles)
    (element-title-sosofo) ;; get a default!
    (with-mode component-title-mode
    (make sequence
    (process-node-list titles)))))

    (make paragraph
    font-family-name: %title-font-family%
    font-weight: 'bold
    font-posture: 'italic
    font-size: (HSIZE 3)
    line-spacing: (* (HSIZE 3) %line-spacing-factor%)
    space-before: (* 0.5 (* (HSIZE 3) %head-before-factor%))
    space-after: (* (HSIZE 4) %head-after-factor%)
    start-indent: 0pt
    first-line-start-indent: 0pt
    quadding: %component-subtitle-quadding%
    keep-with-next?: #t

    (with-mode component-title-mode
    (make sequence
    (process-node-list subtitles))))))))

    ;; Chapter-Title Mode
    (mode chapter-title-mode
    (element title
    (make sequence
    (process-children))))
    ;;------------------------------------------------------


    Pero bueno, no hay porqué irse tan lejos, hay cambios más sencillos, como es que aparezcan los subtítulos de los apartados (la plantilla por defecto los omite):

    ;;-------------------------------------
    ;; (fortran)
    ;; añadimos los subtítulos de los apartados (pobrecitos ellos)
    (element (section subtitle)
    (make paragraph
    font-family-name: %title-font-family%
    font-weight: 'bold
    ;font-posture: 'italic
    space-after: (* (HSIZE 4) %head-after-factor%)
    first-line-start-indent: 1cm
    ))
    ;;-------------------------------------


    Quizá todo esto con XSL-FO sea más fácil (por lo menos seguro que sí lo es para alguien que ya pilote de XSLT y toda la pesca, como a mí me daba igual aprender cualquier cosa, me puse con DSSSL que sonaba más bohemio)...

    Pero no nos quedemos sólo con lo del DocBook, la solución es igual de válida con LaTex, por ejemplo, que hoy por hoy está todavía más extendido que DocBook (aunque no nos desviemos, LaTex está orientado a la presentación, no al contenido).


    De todos modos, yo creo que el tema es ir hacia una solución más integrada que lo de tener unas hojillas de cálculo y unos scripts chapuceros... lo ideal sería tenerlo todo en una base de datos, usar herramientas estándar para hacer "reports" (que los puedes meter luego en el word, o donde te dé la gana) (le he echado un ojo a esto: BIRT y parece muy apetecible) y que la trazabilidad de requisitos, componentes y demás llegue hasta el código, que es el objetivo (utópico) con el que se hacen estas cosas, de modo que cuando se esté picando en el eclipse (o donde sea) y toques una función (que tenga algún comentario especial, tipo javadoc o algo así) te diga "oye, que alterar esta función influye en ...", o que puedas llegar a base de hiperlinks desde un requisito de usuario hasta el código que lo implementa, que te diga cuántos están cubiertos y cuántos te quedan por implementar, etc.

    Ahora mismo estoy con algo que da un pasito más que las hojas de cálculo (aunque todavía le queda mucho para llegar a lo que he comentado en el párrafo anterior), para usarlo en la documentación de mi PFC (aunque es un poco putada, porque tengo ahí un cuello de botella que me está jodiendo xD)... ya hablaré más sobre ello cuando lo tenga terminado.


    Hmmmmmmm... no, no estamos hablando de coches xD

    ResponderEliminar
  5. << Con la ingeniería del software me pasa lo mismo que con el comunismo. Las dos cosas me gustan, pero no puedo decir lo mismo de los ingenieros del software y los comunistas, que suelen ser gilipollas. >>

    Gran frase, me la apunto.

    ResponderEliminar
  6. Ole ese Fortran y su peacho "pitoN"... has dejao la injenierida del chofgüer tiritando :p

    Ale, con Dios.

    ResponderEliminar
  7. Aquí pongo un enlace para los que quieran usar un SVC (Software Versioning System) para documentos de OpenOffice:

    Control de versiones para documentos I

    Control de versiones para documentos II

    ResponderEliminar
  8. Hay que tener cuidado en los parkings del IKEA , sino fijaros lo que me pasó el otro dia:
    Así funciona el timo: dos chicas bastante guapas de entre 18 y 20 años se acercan al coche mientras estas colocando en el maletero tus compras del centro comercial. Entonces empiezan a limpiarte el parabrisas con esponjas haciendo salir disimuladamente un poco de pechuga de sus camisas bastante apretaditas.
    Cuando al final al darles las gracias les intentas dar una propina ellas renuncian y piden a cambio que las lleves hasta el Ikea de la otra punta de la ciudad. Si aceptas ellas suben y se sientan en los asientos traseros. Mientras tu conduces ellas empiezan a hacer juegos lesbicos que veras por el retrovisor. Cuando llegas al parking del otro IKEA una de ellas se pasa a asiento delantero y te hace una mamada bestial , mientras la otra sin que te des cuenta te roba la cartera.
    Con este ingenioso sistema me han robado la cartera el martes, el miércoles, dos veces el jueves, otra vez el sábado y probablemente también mañana por la tarde.

    Saludos e id con cuidado....

    ResponderEliminar