Migración a Github

Como la mayoría de los desarrolladores ya sabrán, Google decidió hace poco prescindir de otro de sus productos, en este caso Google Code. Mover de sitio un repositorio de Subversion no es complicado, el problema es que no se trata sólo de código fuente: la Wiki, el gestor de tareas o la lista de colaboradores también desaparecerán este año. En el caso de Quimera Engine, lo único que supondría una pérdida considerable de tiempo sería mover las más de 800 tareas, entre abiertas y cerradas, que existen en el Issue Tracker. Afortunadamente, Google puso a disposición (qué menos) de todos los administradores un script para facilitar la migración a una de tantas plaraformas de gestión de proyectos con repositorio de código incluido: Github. Desconozco si el script fue creado por la gente de Github o por la de Google Code.

A continuación se describen brevemente los problemas y los cambios necesarios que hubo que hacer para este proyecto:

  • Hubo que cambiar la estructura de directorios del repositorio. Git sólo admite en el directorio raíz del repositorio una carpeta trunk y una carpeta branches. El árbol de directorios de este proyecto era el típico que puede encontrarse en cualquier repositorio de SVN: trunk, team, tags, branches. Al exportar con el script, sólo las carpetas trunk y branches son exportadas. Por lo tanto, para que el usuario encuentre exactamente la misma estructura al entrar en Github (que sólo muestra el contenido de trunk), todo lo que antes estaba en el directorio raíz del repositorio fue movido a la carpeta trunk (dándose el caso de que hay un trunk dentro de otro).
  • Se perdieron todos los commits de todas las carpetas fuera de trunk. Aunque se hayan movido las carpetas tal y como se describe en el punto anterior, todos los commits asociados a la carpeta team, por ejemplo, se pierden. Toda la valiosa información del progreso, fechas, comentarios con soluciones, etc. perdido.
  • Hubo que crear archivos .txt vacíos en todos los directorios vacíos del repositorio. Git maneja archivos, no directorios, por lo que un directorio vacío no existe para él y por tanto no se mantiene en el repositorio. Algunos de los unit tests de SQDirectory y SQFile requerían la existencia de ciertos directorios vacíos, con lo que ahora hay que crearlos por código; además, existían otros directorios únicamente como una declaración de intenciones, para remarcar que ciertas cosas están planificadas pero no se han hecho todavía. Hay ciertos hacks para simular directorios vacíos en Git, pero es innecesario.
  • Se tuvieron que crear y asignar las etiquetas del Status de cada tarea. Esta información se pierde durante la exportación, sólo se distingue entre tareas cerradas o abiertas. Por ejemplo, una tarea en estado "Ready For Review" (abierta, lista para ser revisada por alguien), esto no aparecía en la tarea correspondiente en Github.
  • Aunque no era necesario, en lugar de usar etiquetas para distinguir la fase a la que pertenece cada tarea, estas se han eliminado y sustituido por milestones.
  • Evidentemente, se actualizaron tanto el portal como la wiki del proyecto para que todos los links apuntaran a la web de Github.

Como puede verse, si bien no fue muy complicado, sí que fue tedioso y consumió mucho tiempo. Los siguientes párrafos dejan de ser objetivos e incluyen opiniones personales sobre la nueva situación.

Después de trastear mucho con Github (del cual había oído hablar muchísimo aunque nunca lo había probado), empecé a hacerme muchas preguntas y a agobiarme, sobre todo al ver el nuevo Issue Tracker. En la siguiente tabla se comparan los pros y contras del Issue Tracker de Github con respecto al de Google Code (nótese que se trata de funcionalidades que este proyecto utiliza a día de hoy, puede haber cosas mejores o peores que no afectan a este proyecto):

Pros Contras
Las etiquetas de colores se ven mejor, siempre y cuando no haya muchas. Cuando se abre la lista de etiquetas para asignarla o para filtrar, aparecen en un orden que no se puede controlar. No se pueden poner todas las etiquetas de prioridad juntas, por ejemplo, hay que ponerles un prefijo común.
Se puede editar la descripción de la tarea. No se pueden borrar las tareas.
Es más sencillo gestionar y ver las ramas del repositorio. No se puede modificar el orden en que aparecen las etiquetas en el listado de tareas.
¿Más bonito? Hay muy poca libertad a la hora de ordenar la lista de tareas (al no haber columnas ni grupos de etiquetas, no se puede ordenar por Status, por ejemplo).
 Mayor facilidad para filtrar. No se puede filtrar por las tareas del usuario activo usando un comodín. Para ver mis tareas tengo que seleccionar mi usuario en el filtro.
  No existe el concepto de "tarea bloqueada", por lo que no hay relación posible entre tareas bloqueadas y tareas bloqueantes.
  Sólo se pueden ver simultáneamente 25 tareas en la lista.

 

Después de descubrir todo esto, mi conclusión fue clara: aquí ha primado lo bonito sobre lo práctico. El diseño de la web en general es infinitamente mejor que el de Google Code, mucho más moderno y accesible, eso es innegable. Pero en lo que se refiere al listado de tareas es horrible, de lo peor que he visto desde el punto de vista de una persona que tiene que gestionar más de 10 tareas al día, relacionadas entre sí, con varias etiquetas por tarea. Sinceramente, esperaba mucho más de una herramienta que acapara tal cantidad de proyectos y usuarios. ¿Sólo 25 tareas por página? ¿En serio? Por no decir que sólo caben 15 tareas en mi pantalla de 24". Y como puntualizaba antes, cuando hay más de 2 etiquetas por tarea, el listado marea con solo mirarlo, es la fiesta del color. Llamadme raro o clásico, pero prefiero una tabla en la que vea de un plumazo 100 tareas, sus prioridades, sus tipos, sus estados, etc.

Como no me gusta quejarme sin más, empecé a pensar en soluciones a este tipo de incomodidades. Para obtener más de 25 filas por página, la única alternativa (que yo conozca) es realizar llamadas a la API de Github, lo cual sólo tiene sentido si creas un cliente que te muestre los resultados; aunque sea algo sencillo, por desgracia no dispongo de tiempo para eso. Sin embargo, busqué algún plug-in para el navegador que me permitiera sobreescribir las CSS de las webs que visitase. En efecto, existía uno bastante bueno que sirve tanto para Firefox como para Chrome, Stylilsh, con una web repleta de hojas de estilo creadas por usuarios para miles de páginas web distintas, incluida Github. Sin embargo, los estilos disponibles no eran lo que buscaba (por si alguno tiene curiosidad, los hay que ponen el fondo negro, interesante si programas a oscuras). Así que escribí mi propia CSS y no puedo estar más contento con el resultado:

Antes y despues

AntesDespués

El estilo está ajustado a resoluciones iguales o superiores a 1680x1050.

Evidentemente habrá gente a la que no le guste, pero bajo mi punto de vista así es muchísimo más útil. Se ven todas las tareas de la página, conservando toda la información original, sin mezclar el texto del título con las etiquetas; incluso las etiquetas del mismo tipo caen unas sobre otras facilitando su distinción.

En resumen, entiendo que todo el mundo tenga sus proyectos en Github por la visibilidad que ofrece, porque fue de los primeros en alojar repositorios de Git, incluso por las facilidades que ofrecen para buscar trabajo e interactuar con los demás, pero desde luego, como herramienta para un proyecto mediano-grande que requiera una gestión con un mínimo de complejidad, no es práctico en absoluto.

Para acabar, quisiera pedir a quien lo lea que si cree que mi opinión es fruto de la ignorancia, me saque de ella, y que si conoce algún cliente gratuito de Github, ya sea web o local, que por favor me lo haga saber.

Fin de la Fase 3

Después de mil batallas y cientos de horas de trabajo sin cuartel a lo largo de casi 3 meses, llega a su fin la Fase 3 de desarrollo de Quimera Engine. Se han añadido montones de nuevas funcionalidades, la mayoría de ellas relacionadas con la abstracción del sistema operativo y mejoras de usabilidad de componentes ya existentes; se han mejorado los tiempos de compilación, el tamaño de las librerías y se ha mejorado el diseño en general. Más abajo se describen los progresos de forma más detallada. Estoy muy contento de haber podido realizar muchas mejoras que venía posponiendo desde el principio. Ahora el conjunto de componentes es más robusto y más usable, aunque aún queda mucho por hacer.

Esta etapa ha sido algo especial: ha sido la primera en la que he estado desarrollando en solitario de principio a fin. Ha sido interesante por el hecho de poder comparar la forma de trabajo y el grado de avance, los pros (como poder realizar cambios de diseño rápidamente) y los contras (como tener que revisarte tu propio código o no poder pedir opiniones). No voy a hablar en este post sobre los motivos que han hecho que el equipo pase de hasta 7 personas a 1, ni de mi experiencia dirigiendo un equipo de personas desconocidas entre sí y dispersas por todo el país, cada una con sus propios problemas personales y sus objetivos en la vida, en un proyecto apenas incipiente y de abstracto futuro; algún día escribiré sobre ello para compartirlo con quien pudiera interesarle. Más allá de los errores que pudimos cometer unos y otros, que los hubo, evitables algunos e inevitables otros, me quedo con todo lo aprendido y con la confirmación de que hay más personas ahí fuera que buscan realizarse y que son capaces de embarcarse en proyectos locos como este. Es un pensamiento que me reconforta. "Si quieres ir rápido camina solo, si quieres llegar lejos ve acompañado", dicen; mi intención en un principio fue llegar lejos por lo que busqué compañía; lo que la frase no dice es "...y prepara víveres para alimentar a tu compañía durante todo el viaje, un mapa oficial, una brújula y una postal bonita del sitio al que vas, de lo contrario te va a seguir Dora la Exploradora". Viendo las cosas con perspectiva, no me desanimo: creo que llegamos bastante lejos.

Ahora que voy a continuar la andadura en solitario, toca cambiar de tercio y abrazar las ventajas que ello conlleva en cuanto a agilidad. Ya en su día cambié la hoja de ruta, pospuse el desarrollo de ciertos componentes menos prioritarios de las capas Common y Tools y adelanté otros más necesarios para poder desarrollar las capas más altas; es el momento de dar el salto a la parte más interesante y agradecida de todas: los gráficos. La siguiente fase consistirá en la creación de un prototipo de motor gráfico en OpenGL que será posteriormente integrado en Quimera Engine. Será una prueba de concepto, no una "prueba de producción" (alguno ya entenderá por dónde voy); una vez que implemente unas funciones mínimas será pasado a limpio, se diseñará una interfaz de usuario y se unirá al resto del proyecto a modo de plug-in. Daré más detalles en próximas publicaciones.

Al grano

A continuación unos cuantos números extraídos del proyecto, tal y como se hizo al final de la fase anterior:

  • 274.856* líneas de código, sin líneas en blanco o comentarios de cabecera de archivo, contando el código de test.
  • 84.544* líneas de código, sin líneas en blanco o comentarios de cabecera de archivo,  sin contar el código de test.
  • 35.000 líneas de código, aproximadamente, escritas durante esta fase.
  • 461* archivos de código, contando los archivos de código de test.
  • 276* archivos de código, sin contar los archivos de código de test.
  • 196* clases, 40 de ellas creadas durante esta fase.
  • 43%* de las líneas de código (sin tests) son documentación interna o documentación pública.
  • Más de 7.750 unit tests en total, 650 de ellos escritos durante esta fase.
  • 155 revisiones en Subversion durante esta fase (el código se sube sólo cuando una tarea está completamente terminada, incluyendo unit tests, y después de que ha pasado la revisión).
  • 85 tareas completadas durante esta fase.
  • Más de 542 tareas terminadas hasta ahora en total, de 702 creadas en el tracker.

*Estos números fueron obtenidos usando la herramienta Source Monitor.

Resumen del trabajo realizado

Clases nuevas

  • Herramientas de trazado de pila de llamadas: QCallTrace, QCallStackTrace, QAbstractCallStackTracePrinter, IQCallStackTraceFormatter, QCallStackTracePlainTextFormatter, QCallStackTraceConsolePrinter, QCallStackTracer, QScopedCallTraceNotifier, QTypeWithToString, QTypeWithGetObjectType, macros para facilitar el uso de este mecanismo interno.
  • Iteradores: QConstHashtableIterator, QConstDictionaryIterator.
  • Medición del tiempo: QStopwatchEnclosed.
  • Sistema de ficheros: SQFile, SQDirectory, QFileStream.
  • IO: QBinaryStreamWriter, QTextStreamReader, QTextStreamWriter.
  • Tipos de datos: QArrayBasic, QArrayResult.
  • Contenedores: QHashtable, QDictionary.
  • Componentes de contenedores: QKeyValuePair, SQStringHashProvider, SQIntegerHashProvider, SQKeyValuePairComparator, SQNoComparator, SQEqualityComparator.
  • Threading: QThread, SQThisThread, QMutex, QSharedMutex, QRecursiveMutex, QScopedExclusiveLock, QScopedSharedLock, QConditionVariable, QScopedLockPair.
  • Otras: QEvent, QReferenceWrapper, QAssertException.

Mejoras

  • QDynamicArray y QFixedArray han sido renombradas a QArrayDynamic y QArrayFixed, respectivamente. Esto hace que sean más fáciles de descubrir, ya que el usuario espera que los arrays se llamen "Array" + algo. Aunque pueda parecer una decisión sencilla, no lo ha sido, ya que también tiene consecuencias negativas: a menudo usamos la combinación "control+barra espaciadora" para que la ayuda contextual de turno complete el nombre, y pulsamos "Enter" para seguir escribiendo; ahora hay 4 clases cuyo nombre empieza por "QArray", de manera que hay que escribir todas esas letras antes de poder elegir la clase que queremos, mientras que antes bastaba con escribir "QDy" o "QFix" para obtener el nombre completo. La mejora de legibilidad es algo más subjetivo en este caso, aunque en inglés es menos intuitivo leer las palabras en este orden.
  • Refactoring de tipos enumerados. Se ha eliminado la dependencia con contenedores de la STL (la intención es, en todo el proyecto, no depender de la STL en ningún caso). Ahora las clases de enumeración son más sencillas, rápidas y tardan menos en compilar. Además, ahora tampoco dependen necesariamente del tipo string_q, aunque pueden hacerlo; esto permite que el tipo string_q incluya en su propio archivo de cabecera los enumerados que necesite, ahorrando al usuario la necesidad de incluirlos cada vez que los necesite (obviamente, esto tiene un pequeño impacto en el tiempo de compilación que, según parece, merece la pena).
  • Refactoring de métodos de conversión de tipos de datos básicos a string. Existen clases estáticas específicas para cada tipo de dato básico, esto es, SQInteger para enteros, SQFloat para tipos de coma flotante, SQBoolean para booleanos y SQVF32 para vectores de 128 bits. Hasta ahora cada una ofrecía un método ToString para la conversión del tipo correspondiente a su representación como cadena de caracteres. Ahora toda esa funcionalidad se ha movido a la clase QStringUnicode en forma de sobrecargas de operator+ y Append y métodos estáticos From***. De esta forma se consiguen 2 cosas: Por un lado, no es necesario incluir la clase QStringUnicode si sólo necesitas realizar operaciones con tipos básicos; por otro, es mucho más sencillo y legible concatenar variables sin tener que escribir llamadas a métodos cuando, por ejemplo, se quiere loguear un mensaje. Como muestra: QE_LOG(myString + 5 + " + " + 3 + " = " + (5 + 3) resultaría directamente en "Suma: 5 + 3 = 8".
  • Refactoring del sistema de RTTI interno. El sistema interno de RTTI permite reducir drásticamente el espacio necesario para almacenar información sobre los tipos utilizados en el proyecto, permitiendo conocer y comparar determinados tipos incluso de forma dinámica, cuando las clases integradas en el mecanismo son polimórficas. Hasta ahora se pretendía realizar esto de manera sencilla haciendo que todas las clases que participaran heredasen, directa o indirectamente, de la clase raíz QObject. Esto requería el uso de herencia virtual para romper diamantes de la muerte lo cual, más allá de discusiones sobre su utilidad o peligrosidad, impedía la correcta resolución de tipos polimórficos tal y como estaba implementada. Ahora QObject ha desaparecido (junto a SQTypeExtensions) y en su lugar se utilizan macros en las clases base.
  • Pequeña mejora de la interfaz de QDateTime. Hasta ahora no era necesario especificar la zona horaria para crear fechas y horas, usándose UTC por defecto, es decir, poniendo a nulo el valor del parámetro correspondiente al puntero a QTimeZone. Esto acabó provocando que las distintas sobrecargas del constructor se solapasen debido a la conversión implícita de entero a puntero que tiene lugar cuando el valor es cero. Para resolver la ambigüedad, será obligatorio suministrar un huso horario siempre. Es una pérdida de usabilidad a cambio de evitar potenciales problemas graves en el código cliente.
  • Pequeña mejora de usabilidad de QStringUnicode. Al contrario que en el caso anterior, se han añadido ciertos valores por defecto en algunos métodos para que sean más cómodos de usar. Por ejemplo, antes hacía falta pasar el tipo de comparación a utilizar cuando se llamaba a Contains, y ahora se presupone que es binaria y distinguiendo mayúsculas de minúsculas (la más rápida y más frecuente) pudiendo, simplemente, escribir algo como myString.Contains("myPattern").
  • Compilación multi-proceso en Visual Studio. Ahora parece que los proyectos compilan en la mitad de tiempo.
  • Desactivación del soporte de RTTI y de manejo de excepciones. Quimera Engine utiliza su propio sistema de RTTI, más liviano y rápido, sólo en las partes donde pueda aportar algún valor. Respecto al manejo de excepciones, la única excepción lanzada por el motor es la que tiran los asserts cuando se configuran para tal efecto, únicamente con fines de testing. Se acepta la norma de que un videojuego funciona correctamente o no funciona. Los errores inesperados (aquellos no encontrados durante el desarrollo) se trazarán, si es posible, y el programa acabará. Todas las razones para tomar esta decisión están escritas en el documento de Fundamentos de Diseño de Quimera Engine (actualmente desactualizado, por cierto). Las librerías de Boost han tenido que ser recompiladas con las opciones correspondientes y algunos archivos de cabecera han tenido que ser modificados in situ para hacerlos compilar, a falta de un remedio mejor. Como añadido importante, se ha reducido notablemente el tamaño de todas las librerías, como puede verse en la comparativa más abajo.
  • Mejora de las utilidades para detectar el endianness1 de la máquina. Añadidas varias definiciones para conocer en tiempo de compilación si la máquina es Little-endian o Big-endian. También se han añadido funciones para comprobar lo mismo en tiempo de ejecución.
  • Migración de la Test Automation Tool de Code::Blocks a CodeLite y portabilidad a Linux y Mac OS X. Hasta ahora, la herramienta de automatización de tests que se creó ad-hoc para ejecutar los unit tests de Quimera Engine estaba montada sobre un proyecto de Code::Blocks y nunca había funcionado sobre Mac. Ahora está montada sobre CodeLite (ya no usamos Code::Blocks para nada) y funciona bien sobre los sistemas operativos soportados.
  • Creación de una herramienta para convertir de forma automática workspaces de CodeLite a makefiles. Actualizar los makefiles de todos los proyectos cada vez que se añadía un archivo de código fuente o se cambiaba una opción de compilación era un coñazo y sobre todo un riesgo. Para evitarlo he creado una pequeña herramienta en C# (por rapidez de desarrollo y no necesitar portabilidad) que transforma los workspaces y proyectos de CodeLite en makefiles listos para ejecutar, con un par de clics de ratón. El ahorro de tiempo es considerable. Más abajo se dan más detalles.
  • Limpieza de warnings en general. Son como las malas hierbas, hay que mantenerlas a raya. Los warnings pierden su utilidad si no se mantienen a cero, dentro de lo posible. Además, hay que evitar que se propaguen al proyecto del usuario de la librería. Esto ocurre, por poner un ejemplo cercano, con Boost, lo cual te obliga a incluir sus librerías como librerías del sistema para evitar un torrente de warnings de terceros en tu log de compilación.
  • Adición de soporte de tipos e instrucciones SIMD a los proyectos. Ahora también al compilar con GCC/Clang.
  • Optimización parcial de QArrayFixed y QArrayDynamic. Tal y como ya anticipé en el anterior post de fin de fase, he llevado a cabo algunas optimizaciones en los contenedores. La más beneficiada ha sido QArrayFixed, que a la hora de iterar es igual de eficiente que std::vector (al final se reduce a incrementar un entero y hacer una derreferenciación para obtener el valor real). No he dedicado mucho tiempo a hacer experimentos de rendimiento, cosa que me encantaría, pero sí hice algunas pruebas concretas y puedo decir que QList es más rápida que std::list, por ejemplo. Ojalá tenga tiempo en el futuro de publicar una demo.
  • Mejora de usabilidad de métodos que devuelven arrays. Algunos métodos, como QStringUnicode::Split, devuelven múltiples resultados cuyo número es desconocido. La solución típica es pasar un puntero y un entero como parámetros de salida. Para mejorar la usabilidad y legibilidad, se ha optado por devolver una pequeña estructura que contenga el número de elementos y el array, cuya memoria se ha reservado en el interior del método. La estructura está ligada el contenido de manera que, en cuanto se llame a su destructor, liberará la memoria ocupada por el array, de forma que el usuario no tiene que preocuparse. Para evitar acabar creando algo parecido a unique_ptr (con los problemas que ello conlleva) se ha remarcado que el uso de tal estructura está limitado exclusivamente a la devolución de este tipo de resultados y de ahí su nombre: QArrayResult. La estructura puede desvincularse del contenido manualmente, llamando a Detach. No se descarta la inclusión de sobrecargas que reciban un puntero (el mismo que se devolvería con la estructura) para dar la posibilidad de suministrar un bloque de memoria previamente alojado y así aumentar el rendimiento.

      1¿Tiene traducción al español?

Trabajo pendiente

Clases

  • Threading: QConditionVariableExclusive.
  • Hashed strings: SQStringPool, QHashedString

Mejoras

  • Mecanismo para activar/desactivar aserciones por tipo (error, warning o deprecation).
  • Trazado del alojamiento de memoria.
  • Quitar dependencia de la STL en QLocalTimezone.
  • Conversión de algunos métodos de los contenedores en plantillas, de manera que puedan interactuar entre ellos.
  • Añadir métodos ForEach a los contenedores para ejecutar una función por cada elemento.
  • Añadir métodos poco prioritarios a muchas de las clases existentes.
  • Investigar si es posible cambiar las librerías de zonas horarias de Boost por las de ICU, que parecen más completas.

 

Comparativa de tamaño de librerías compiladas con y sin RTTI y excepciones

A continuación hay 2 tablas comparativas del tamaño de las librerías antes y después de los cambios. Los detalles de cada tabla aparecen debajo de cada una.

  Antes Después Reducción Total
QuimeraEngineCommon.dll 554 kb 447 kb 20%  
QuimeraEngineTools.dll 2068 kb 1741 kb 16%  
QuimeraEngineSystem.dll 1308 kb 1070 kb 18% 17%
QuimeraEngineCommon.lib 4635 kb 3660 kb 21%  
QuimeraEngineTools.lib 12100 kb 9756 kb 20%  
QuimeraEngineSystem.lib 14254 kb 9728 kb 32% 26%

Compilado con Visual Studio 2010, configuración DebugWin32Sharedrt. Los resultados de esta primera tabla no son del todo fiables debido a que, para hacer compilar las librerías con la nueva configuración, hubo que hacer cambios en el código, lo cual pudo influir en el tamaño final. Sin embargo, las diferencias son suficientemente significativas como para asegurar que ambas desactivaciones produjeron binarios más pequeños.

  Antes Después Reducción Total
QuimeraEngineCommon.dll 315 kb 259 kb 18%  
QuimeraEngineTools.dll 1838 kb 1558 kb 15%  
QuimeraEngineSystem.dll 1015 kb 842 kb 17% 16%
QuimeraEngineCommon.lib 2188 kb 1778 kb 19%  
QuimeraEngineTools.lib 9156 kb 7368 kb 20%  
QuimeraEngineSystem.lib 10700 kb 6971 kb 35% 27%

Compilado con Visual Studio 2010, configuración DebugWin32Sharedrt. Aquí se muestra, tiempo después, la reducción debida sólo a la desactivación de las excepciones, sin tocar nada de código entre una y otra muestra. Hasta un 35% según el fichero.

Desde que se empezara la tarea hasta que se terminó, el tamaño total se redujo en torno a un 25-30%.

 

Conversor de proyectos de CodeLite a Makefiles

Esta es una breve presentación de la mini-herramienta creada para generar makefiles totalmente funcionales a partir de proyectos y workspaces del IDE CodeLite 6.01.

1. Se selecciona el workspace

 

 

2. Al seleccionarlo, se carga todo el contenido en pantalla, pudiendo visualizar cada configuración de compilación (mediante el combo box).

3. Se generan los makefiles para todas las configuraciones y todos los proyectos del workspace:

 

 

Enlaces a la Wiki del proyecto para más información

Documentación de referencia de la API

Estado detallado del proyecto

Hoja de ruta

Tablón de propuestas

P.D.: Tengo que aprender a dosificar la información en varios posts, de lo contrario es infumable.

Fin de la Fase 2

La fase 2 termina, por fin. Ha durado lo mismo que el verano, desde principios de Mayo a finales de Octubre (sí, eso es lo que dura el verano aquí, en la Costa del Sol). Seis meses de duro trabajo del cual me siento orgulloso, aunque no se hayan cumplido todas las metas; muchas cosas aprendidas, un montón de desafíos superados y unos pasos más hacia la construcción de una infraestructura para el motor sólida, independiente y útil. Como se ha dicho ya en publicaciones anteriores nuestros recursos son mínimos, pero un poco de sacrificio es suficiente para seguir adelante.

Antes de exponer algunas estadísticas del proyecto y resumir los principales hitos de desarrollo, me gustaría destacar que cada componente en el repositorio de código (rev. 1398) está listo-para-usar; todo está documentado y totalmente probado, y compila en cualquiera de los SO soportados (Windows, Linux y Mac). Sin embargo, todavía no se ha dedicado ningún esfuerzo a la optimización; algunas funcionalidades pueden estar ya optimizadas mientras que otras pueden no estarlo tanto. Ejemplo de esto son los contenedores: actualmente no son tan rápidos como los contenedores de la STL pero ciertos experimentos han revelado que pueden ser fácilmente mejorados para ser algo más rápidos que aquellos; esta mejora podría ser incluida en la siguiente fase de desarrollo ya que es muy barata de hacer (simplemente almacenar un puntero). Lo mismo ocurre con la librería Math, la cual no hace uso de SIMD todavía a pesar de que incluir tales instrucciones en el código existente es muy sencillo; simplemente no es una prioridad en este momento. Para terminar, sólo una advertencia obvia: la interfaz pública de toda la librería es susceptible de cambiar hasta que la primera versión del motor sea oficialmente publicada, de manera que por favor, tenedlo en mente si decidís incluirlo en vuestros proyectos.

A grandes rasgos

Vamos con varias cifras sobre el estado actual del proyecto:

  • 239.092* líneas de código, sin líneas en blanco o comentarios de cabecera de archivo, contando el código de test.
  • 68.800* líneas de código, sin líneas en blanco o comentarios de cabecera de archivo,  sin contar el código de test.
  • 350* archivos de código, contando los archivos de código de test.
  • 208* archivos de código, sin contar los archivos de código de test.
  • 143* clases.
  • 43%* de las líneas de código (sin tests) son documentación interna o documentación pública.
  • Más de 7.100 unit tests en total, 1.600 de ellos escritos durante esta fase.
  • 116 revisiones en Subversion durante esta fase (el código se sube sólo cuando una tarea está completamente terminada, incluyendo unit tests, y después de que ha pasado la revisión).
  • 73 tareas completadas durante esta fase.
  • Más de 450 tareas terminadas hasta ahora en total, de 600 creadas en el tracker.

*Estos números fueron obtenidos usando la herramienta Source Monitor.

Echando un vistazo a estos datos, uno puede hacerse una idea de la importancia que damos al testing (tres veces más código de test que código testeado) y a la documentación (43% de las líneas de código). Si sumamos estos esfuerzos a nuestra preocupación por la usabilidad (cosa que no puede ser medida, por desgracia), el resultado es que el desarrollo de nuevas funcionalidades es lenta, pero merece la pena realmente. Al final, no tener que dedicar tiempo a arreglar un bug grave ni tener que explicar a los usuarios, uno por uno, cómo funcionan las cosas es una buena inversión de futuro.

 

Resumen del trabajo realizado

Clases nuevas

  • Contenedores: QNTree, QDynamicArray, QBinarySearchTree.
  • Iteradores: QListIterator, QNTreeIterator, QBinarySearchTreeIterator, QConstArrayIterator, QConstListIterator, QConstNTreeIterator, QConstBinarySearchTreeIterator.
  • Hora y fecha del sistema: QDateTimeNow, QLocalTimeZone.
  • Medición del tiempo: QStopWatch.
  • Sistema de ficheros: QPath, QFileInfo, QDirectoryInfo.
  • IO: QMemoryStream, QBinaryStreamReader.
  • Tipos de datos: SQTypeExtensions, SQAnyTypeToStringConverter.
  • Logging: SQInternalLogger.
  • Alojamiento de memoria: QLinearAllocator.
  • Otras: QDelegate, QUri.

Desarrollos pendientes completados y ampliación de funcionalidades

  • Contenedores: QFixedArray, QList.
  • Iteradores: QArrayIterator.
  • Fecha y hora: QDateTime, QTimespan.
  • Tipos de datos: QStringUnicode, SQInteger.
  • Alojamiento de memoria: QPoolAllocator.

Mejoras

  • Las aserciones ahora se loguean, incluyendo información sobre el número de línea y el nombre de fichero. Se han dividido en tres categorías: errores, avisos (warnings) y descartes (deprecations). Además, funcionan con cadenas Unicode a través de QStringUnicode.
  • Algunas utilidades básicas de logging que permiten enviar texto desde las capas Common y Tools, las cuales no cuentan con funcionalidades de logging (las clases de logging se definirán en la capa System), a cualquier función de logging externa. Actualmente, por defecto se utiliza una función miembro estática que envía el texto a la consola de depuración activa.
  • Los operadores globales new y delete han sido sobrescritos. Ahora es posible trazar cada alojamiento.
  • Migración de Code::Blocks 13.12 a CodeLite 6.0.1. La razón principal fue el pobre soporte del IDE C::B en Mac OS. Además, se usó el compilador Clang (versión 3.4) junto con LLDB en lugar de GCC + GDB debido a problemas de instalación y bugs (de GDB mayormente). Todo es más sencillo de esta forma y funciona mejor.

 

Trabajo pendiente

Clases

  • Threading: QThread, QMutex, QSharedMutex, QRecursiveMutex, QScopedExclusiveLock, QScopedSharedLock, QConditionVariable, QScopedLockPair.
  • Trazado de la pila de llamadas: QCallTrace, QCallStackTrace, QCallStackTracePlainTextFormatter, QCallStackTraceConsolePrinter, QCallStackTracer, QScopedCallTraceNotifier.
  • Sistema de ficheros: QFile, QDirectory.
  • IO: QTextStreamReader, QBinaryStreamWriter, QTextStreamWriter, QFileStream.
  • Medición del tiempo: QStopwatchEnclosed.
  • Otros: QEvent, QAssertException.

Mejoras generales del código

  • Mecanismo para activar/desactivar aserciones por tipo (error, warning o deprecation).
  • Mecanismo para auto-detectar el endianness de la máquina.
  • Trazado del alojamiento de memoria.
  • Adaptación de código debido a algunos cambios en la convención de nombres.
  • Realización de varios reemplazos de texto y añadido de otras funcionalidades pequeñas a algunas clases.

 

Es el momento de actualizar la documentación del proyecto, la hoja de ruta y los diagramas de clase. La fase 3 se planificará a lo largo de este mes por lo que es probable que empiece en Diciembre.

Enhorabuena a las personas que participaron durante esta fase.

Fin de la Fase 1

Termina una etapa en la que hemos conseguido subir un peldaño más hacia las capas más altas, hemos reforzado la infraestructura, probado nuevas formas de trabajo y actualizado el software de terceros. A continuación, un resumen de lo desarrollado en este periodo de casi 4 meses y otros acontecimientos relevantes:

  • Soporte de codificación Unicode, mediante la clase QStringUnicode, basada en la librería ICU. Puede alternarse con la clase string de la STL si sólo se necesita usar codificación ASCII, mediante un flag de compilación.
  • Utilidades de cálculo y manejo de fechas y horas, mediante las clases QDateTime, QTimeSpan, QTimeZone y SQTimeZoneFactory.
  • Utilidades de optimización de alojamiento de memoria, mediante las clases QPoolAllocator y QStackAllocator. También se han añadido sobrecargas de operadores new globales.
  • Diseño de contenedores de datos, cuya implementación está aún por terminar, con las clases QFixedArray, QDynamicArray, QList, QBinarySearchTree y QNTree.
  • Introducción de nuevos conceptos del núcleo del motor, con las clases QObject y QType.
  • Soporte completo en Mac OS X, ya puede compilarse en esta plataforma con GCC.
  • Actualización de las librerías de Boost a la versión 1.55.0.
  • Actualización de los proyectos de CodeBlocks a la versión 13.10.
  • Rediseño de las capas bajas del motor, introducida la capa Common y movida la capa Core.
  • Mejorado el sistema de testing interno.
  • Mejorados los asserts.
  • Otros refactorings menores.
  • La herramienta Test Automation Tool, que utilizamos para automatizar la ejecución de unit tests, ha sido mejorada para agilizar el trabajo de desarrollar nuevos tests. Ahora la ventana de resultados está separada de la ventana de ejecución.
  • Introducido y probado el sistema de recompensas, o ranking de colaboradores, por primera vez. Se han entregado hasta la fecha 3 premios, 2 videojuegos en digital y 1 libro físico. Sobre la marcha, este sistema fue mejorado para premiar también el esfuerzo de revisar el código de los compañeros.
  • Los miembros del equipo Thund y AndersonJAG acudieron a RetroMadrid 2014, donde se conocieron en persona por primera vez y representaron al proyecto.

Es importante recalcar que todo el desarrollo que aquí se contempla ha sido documentado, revisado y sobradamente testeado (actualmente contamos con más de 5.500 tests que cubren 16 configuraciones de compilación).

Enhorabuena a todos los que han participado durante este periodo y han mantenido vivo el proyecto. ¡Ahora a por la siguiente fase!

Más información

Hoja de ruta del proyecto

Estado actual del proyecto

Documentación de referencia de la API hasta hoy

¿Cómo colaborar?

Basta con ponerse en contacto con nosotros vía e-mail ( Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo. ) o por Twitter (@QuimeraEngine). Se hará una breve entrevista por escrito para saber si la persona es adecuada para formar parte del equipo.

Para saber más acerca de lo que buscamos, visita esta sección.

Twitter