martes, 10 de febrero de 2015

Herramientas personalizadas en ZinjaI

Desde hace algunas versiones, ZinjaI permite definir hasta 20 herramientas personalizadas. Esto es, hay items en el menú de herramientas (y opcionalmente sus correspondientes botones en la barra de herramientas, y/o sus atajos de teclado) para los cuales el usuario puede configurar acciones a gusto. En la última versión, hay mejoras que las hacen más flexibles y fáciles de usar que antes. Esto está pensado para cosas que requieren ejecutar programas externos a ZinjaI, como puede ser abrir una referencia de una biblioteca en un navegador, ejecutar scripts de mantenimiento, o ejecutar un proyecto de forma alternativas. Pueden configurarse herramientas "globales" (para esa instalación de ZinjaI, siempre disponibles), y por proyecto (disponibles en cualquier ZinjaI solo para ese proyecto). En este post pongo algunos ejemplos de uso para mostrar qué cosas se pueden hacer y presentar algunas de las opciones nuevas que pueden resultarles útiles.


Ejemplo 1: Abrir documentación

El ejemplo más básico, pero también generalmente el más útil es el de abrir una documentación externa. Este caso ya estaba cubierto en este otro post, así que les dejo solo una captura actualizada con la configuración final:
Al ejecutar el comando, la variable "${BROWSER}" se reemplazará automáticamente por el comando que configuren para el navegador en las preferencias, o por el navegador por defecto si nunca configuraron nada (si está en blanco). Asegúrense de que la ejecución sea asíncrona para que ZinjaI no se quede esperando a que cierren la referencia antes de permitirles seguir trabajando en el proyecto. Además, para el directorio de trabajo (así como para el comando) pueden usar algunas variables especiales para que la configuración sea independiente de la instalación (como "${MINGW_DIR}" en este caso), o de la ubicación del proyecto (ejemplos más adelante).


Ejemplo 2: Reformatear el código fuente

El segundo ejemplo es un poco más interesante, y consiste en integrar alguna de las herramientas que ofrece clang. Tomemos por ejemplo clang-format, una herramienta que reacomoda un código fuente para que siga determinados estándares de estilo de codificación (arregla tabulados, espacios, anchos de lineas, etc). Básicamente, es una herramienta sin interfaz (se usa por linea de comandos), a la que se le pasa un nombre de archivo fuente, y esta reformatea el código contenido en ese archivo. Si queremos tener esta funcionalidad a mano en ZinjaI, podemos poner un botón en la barra de herramientas que al hacerle click, nos reacomode el fuente que tengamos abierto. Para ello, la herramienta debe: 1) guardar el fuente abierto (ver "Acción antes de ejecutar"), 2) pasarle a clang-format la ruta donde guardó el fuente (que puede ser un temporal si era un archivo sin nombre), con el argumento "-i" que le indica que sobreescriba el archivo (ver "Comando"), 3) esperar a que la ejecución finalice (es decir, no usar "Ejecución asíncrona"), preferentemente sin mostrar ninguna ventana ni consola mientras lo hace (ver "Salidas"), y 4) actualizar el código con el resultado, que estará en el mismo archivo (ver "Acción luego de ejecutar").
Hay que aclarar aquí 2 cosas: 1) En Windows, seguramente será necesario colocar la ruta completa al ejecutable de clang-format en "Comando", y 2) (más importante), debo advertir que esta acción no tiene "deshacer". Tal vez podría mejorarse con un script wrapper que haga un backup del fuente antes de invocar a la herramienta.


Ejemplo 3: Medir tiempos de ejecución

Las herramientas personalizadas también puede usarse para ejecutar de forma "especial" un programa o proyecto (digamos, que no simplemente lanzando el ejecutable como hace ZinjaI por defecto). Si siempre van a querer ejecutar de forma especial, entonces será mejor configurar directamente el mecanismo de ejecución del proyecto (menú "Ejecución" -> "Opciones..." -> pestaña "General" -> campos "Mecanismo de ejecución" y "Script para ejecución/Comando wrapper"). Pero si esta ejecución especial es solo ocasional, conviene tenerla a mano como un botón adicional/opcional en la barra de herramientas. Es útil cuando queremos ejecutar el proyecto a través de otro programa que hará ciertas acciones adicionales, como instrumentar la ejecución, cambiar el ambiente de trabajo, o en este caso, recopilar información sobre tiempos de ejecución. En este caso lo vamos a hacer con la herramienta "time" de GNU/Linux (el ejecutable en /usr/bin/time, no el comando de bash homónimo), que le pide cierta información estadísitica al kernel. A pesar de ser básica, nos sirve para medir cosas como el consumo máximo de memoria, o el tiempo real que tarda el programa, y eventualmente compararlo con el tiempo de CPU (que también mide) para ver, por ejemplo, si está aprovechando la CPU al máximo, o está perdiendo tiempo a la espera de un recurso (bloqueos entre hilos, acceso a disco, red, etc). Para ello, simplemente invocamos a time pasandole la linea de comandos que debe ejecutar, y este recolectará e informará los datos de su ejecución.
En el comando, invocamos primero a time, luego el argumento "-v" le pide que muestre de forma legible toda la información que pueda, y luego el comando de ejecución normal del proyecto, que se forma con el ejecutable ("${PROJECT_BIN}") y los argumentos que hayamos configurado para la ejecución desde el menú "Ejecución"->"Opciones..." ("${ARGS}"). Además, si nuestro programa carga información desde archivos, es importante considerar el directorio de trabajo ("${WORK_DIR}") también configurado para la ejecución. El otro aspecto interesante de este caso es que necesitamos que guarde los cambios y compile antes de ejecutar (como hace el botón de ejecución regular en ZinjaI, campo "Acción antes de ejecutar), para evitar tener que hacerlo manualmente cada vez que la usamos. Y finalmente, queremos ver la consola donde se ejecuta, ya que allí correrá nuestro programa, y allí mostrará time además los resultados (campo "Salidas").
Ejemplo de resultados de "time"
Este mecanismo permite integrar además herramientas similares a "valgrind", solo que para esa en particular ya tenemos comandos específicos en el menú de herramientas.


Ejemplo 4: Ejecutar pruebas en lote

En varios proyectos tengo la necesidad de ejecutar regularmente muchos casos de prueba. Por ejemplo, en PSeInt, para verificar que no rompí nada cuando hago algún cambio en el intérprete. Para ello tengo un script de bash que automatiza todas las corridas, tomando de un directorio archivos con los algoritmos, y archivos con los resultados esperados y comparando con diff estos últimos con las salidas de ejecutar pseint sobre los primeros.

En este ejemplo entonces agrego en la barra de herramientas del proyecto del intérprete un botón que compila el proyecto y realiza todas las corridas. Los ejemplos a probar están en el directorio "../test", y "runall" es el script, al que le paso el ejecutable (para que sirva tanto para debug como para release). Y lo que tiene de particular esta configuración es que elijo mostrar el resultado en una ventana propia de ZinjaI, que mostrará por separado ("en cuadro de diálogo") las salidas estándar (que en este caso me informa qué algoritmos se van probando) y de error (que en este caso muestra solo el diff de los que fallan). Esta es la ventana que se usaba para las corridas de doxygen y cppcheck, ahora está disponible también para herramientas del usuarios que no requieran entradas por teclado.



Espero que les hayan sido útiles los ejemplos y les den ideas para integrar sus herramientas favoritas o facilitar tareas tediosas en sus proyectos con ZinjaI. Cualquier sugerencia para mejorar esta funcionalidad o por alguna herramienta que no encuentren cómo integrar será también bienvenida.

2 comentarios:

  1. Excelente Pablo, algunas de estas no las sabia. Muchas gracias y seguí así !

    ResponderEliminar
  2. Muy buena informaciòn :v

    ResponderEliminar