Y el bit se hizo

En este blog incluyo experiencias, recetas y otras cosas, todas relacionadas con el mundo del software. También me pondré en plan "abuelo Cebolleta" de vez en cuando, y contaré historietas de mis comienzos.

¡Que lo disfrutéis!

Compile CMake with user compiled curl (to have SSL support)

2017-09-11

Resulta que tengo que usar una máquina Linux en la que no tengo permisos de administrador. Para la instalación de herramientas y paquetes sin demasiada complicación acudo por supuesto a nuestro servicio de IT. Sin embargo, en algunas ocasiones resulta más rápido (siempre), cómodo (apenas), y práctico (siempre) remangarse un poco y ponerse manos a la obra, sorteando por supueste el hecho de que no puedo instalar nada con permisos de administrador.

Para estos casos tengo preparado un directorio en el HOME del usuario que usamos en esa máquina en donde instalo todas esas herramientas que he de instalar manualmente.

En el pasado, por ejemplo, tuve que instalar curl y libcurl. No entraré en el detalle de cómo los instalé. Baste decir que se encuentran instalados bajo un subdirectorio en $HOME/opt.

Ahora tengo un problema. Resulta que uso, en un proyecto, la biblioteca Google Test para la creación de pruebas unitarias. En el pasado, esta biblioteca era compilada e instalada de manera independiente. Actualmente, su compilación ha sido integrada en el proceso de construcción de mi proyecto. Este proceso usa el sistema CMake. De manera que al pedir que se compilen los tests (con TESTS=ON al llamar a CMake), lo primero que se hace es bajarse (con un git clone) el código de Google Test (o se actualiza si se ha bajado ya previamente, y hay alguna modificación), se compila, y luego se compilan los tests de mi proyecto.

Pero ahora tengo un problema. CMake, por defecto, se construye usando copias propias de curl y otras herramientas. Y en particular curl, por defecto, no soporta SSL. El corolario es que, por defecto, CMake no tiene soporte para acceder a URLs con el protocolo HTTPS. Y justo, Google Test, está accesible en un URL con HTTPS.

Así que tengo que decir a nuestro grupo de IT que me instale un CMake con soporte SSL, o bien hacerlo yo mismo, compilándolo con el curl que ya tenía compilado en HOME/opt. Esto último es lo que ilustro en este post.

Primero, nos bajamos el código fuente de CMake. En mi caso, cmake-3.9.2.tar.gz.

Hay que hacer un bootstrap, y luego compilarlo e instalarlo. Para hacer el bootstrap de manera que use el curl que yo quiero, he de ejecutar la siguiente secuencia (que hubiese querido tener a mano):

tar xvzf ~/cmake-3.9.2.tar.gz
mkdir cmake-3.9.2-build
cd cmake-3.9.2-build/
../cmake-3.9.2/bootstrap --system-curl --prefix=../cmake-3.9.2-install -- -DCURL_LIBRARY=~/opt/curl/lib/libcurl.so -DCURL_INCLUDE_DIR=~/opt/curl/include -DCMAKE_CXX_FLAGS="-L/home/eucops/opt/curl/lib -lcurl"

make
make install

Solamente queda incluir el directorio cmake-3.9.2-install/bin en el PATH.

SVN merge conflicts and Emacs

2016-06-15

Hace un montón que no escribo ninguna entrada en este blog (o en cualquiera de ellos, ya que lo mencionamos). Pero me he propuesto volver a las andadas, al menos poco a poco. Y para retomar la cuestión, cualquier excusa es buena. En este caso, hacer un merge con SVN.

He usado muchos gestores de versiones de ficheros de codigo fuente, y todos sin excepción tienen sus ventajas e inconvenientes. Así que no viene al caso discutirlas. Tomemos como un hecho simplemente que en la actualidad me veo obligado a usar SVN. Y vayamos al problema.

Problema

En el repositorio donde se guarda el código de desarrollo de determinada aplicación en el que estoy trabajando, tengo un tronco principal (trunk) que es donde se realiza normalmente el desarrollo principal de la aplicación de marras. De vez en cuando me veo en la situación de hacer un desarrollo suficientemente extenso y a la vez desacoplado de la evolución actual de la aplicación, como para que me sea útil y conveniente crear una rama en la que desarrollar esa nueva y compleja funcionalidad.

En el caso que nos ocupa, la rama a seguido evolucionando, pero también el tronco. Éste no ha evolucionado lo suficiente, pero sí lo bastante como para que sea conveniente actualizar la rama con aquellas modificaciones que llevamos hechas en el tronco.

Solución: svn merge

Esto se hace fácilmente, situándonos en la copia de trabajo de la rama y haciendo un svn merge dando como origen de las modificaciones el tronco, así:

$ cd <base_dir>/branches/mybranch
$ svn merge <base_url>/trunk 2>&1 | tee merge.log

En general se realizará un merge con éxito, pero pueden surgir conflictos. SVN nos pedirá la acción a realizar para cada uno de ellos:

Conflict discovered in '<file>'.
Select: (p) postpone, (df) diff-full, (e) edit,
    (mc) mine-conflict, (tc) theirs-conflict,
    (s) show all options: p

Por sistema selecciono postpone para todos los conflictos, dado que después los solucionaré con Emacs.

Una vez realizado el merge, haciendo un svn status comprobaremos que en efecto tenemos algunos conflictos por resolver.

Resolución de conflictos

En este momento, nos vamos a Emacs y habrimos el directorio con vc-dir.

Nos aparecerá el estado de los ficheros del directorio en cuestión, y observaremos que tenemos algunos conflictos. Para resolverlos, editamos uno por uno los ficheros.

Los ficheros, al editarlos, nos aparecen con las secciones conflictivas en forma de diferencias, donde tendremos que seleccionar una de las versiones, o editarlas para realizar la mezcla convenientemente. Pero para facilitar este proceso, podemos pedir que se nos abra esto en forma de ediff merge. Para ello, tecleamos C-c ^ E. Nos aparece el frame partido en las habituales ventanas de diff y merge. A es la versión inicial, B es la versión con la que queremos hacer merge (en nuestro caso, la del trunk), y C es la versión final.

Una vez que hemos llegado a una versión final satisfactoria, tecleando q salimos del modo ediff/emerge y volvemos al modo de edición del fichero en cuestión, que se corresponde con el C que estabamos viendo. Bastará salvar el fichero.

Al volver a la pantalla de vc-dir, si tecleamos g para actualizar el status de los ficheros que se muestran, veremos que aquellos que aparecían con conflictos ahora aparecen como edited.

A partir de aquí, podremos continuar con nuestras modificaciones, hacer commits o lo que deseemos.

Importando datos en Python

2015-07-14

El otro día nos surgió la siguiente duda. De la misma manera que uno puede abrir cierto tipo de ficheros con aplicaciones a las que se encuentre asociado ese tipo de ficheros (p.ej. ficheros de datos en CSV asociados a Microsoft Excel), ya sea haciendo dole click en un navegador de ficheros o a través de menús contextuales (del estilo de "Abrir con…"), ¿sería posible asociar un determinado tipo de fichero para se abra en una sesión de consola Python, para realizar con esos datos cualquier tipo de cálculo que deseemos? La respuesta sabíamos que era sí, pero no sabíamos muy bien cómo hacerlo.

La solución que encontré fue la siguiente. Para fijar ideas, supongamos que queremos leer ficheros CSV. Estoy trabajando en un sistema Linux Ubuntu 14.04 LTS, pero el procedimiento es equivalente en otros sistemas.

Lo primero es crear el lector de datos. En nuestro caso la lectura la podemos realizar así:

from numpy import genfromtxt

dataset = genfromtxt(csvFile, delimiter=',')

Con esto nos aseguramos la lectura de los datos de un fichero cuyo nombre está en la variable csvFile. Ahora bien, siempre que ejecutemos esto, los datos se cargarán siempre en una variable con el nombre dataset. Vamos a darle algo que carácter. Vamos a hacer que el nombre de la variable donde se carguen los datos varíe con el nombre del fichero. Una forma de hacerlo es

import os
import sys
from os.path import basename

csvFile = sys.argv[1]
datasetName = os.path.splitext(basename(csvFile))[0]

Con esto creamos un nombre de variable idéntico al del fichero, pero sin el path y sin la extensión. De esta forma, si csvFile vale /home/pepe/data/mydata201508.csv, el valor de la variable datasetName (y por tanto el nombre de nuestra futura variable con los datos leídos) será mydata201508.

Ahora nos queda juntar las dos partes. Además, no queremos solamente que se ejecute un script en Python cuando seleccionemos un fichero CSV; queremos que se ejecute (que lea el fichero), y además que nos deje la consola abierta para trabajar.

Nuestro mini-script Python final es el siguiente (en mi sistema uso Anaconda, instalado en /opt).:

 1  #!/opt/anaconda/bin/ipython -i
 2  
 3  import os
 4  import sys
 5  from numpy import genfromtxt
 6  from os.path import basename
 7  
 8  csvFile = sys.argv[1]
 9  datasetName = os.path.splitext(basename(csvFile))[0]
10  evalCmd = datasetName + " = genfromtxt(csvFile, delimiter=',')"
11  exec(evalCmd)
12  print "Dataset " + datasetName + " loaded."

Hemos completado la mitad del puzle. La otra mitad es conseguir asociar los ficheros .CSV con este script. Una manera rápida de hacerlo (exclusiva de mi sistema Linux) es crear el siguiente fichero en ~/.local/share/applications/, con el nombre que deseemos, con la extensión .desktop. En mi caso, readCSV.desktop:

#!/usr/bin/env xdg-open

[Desktop Entry]
Version=1.0
Type=Application
Terminal=true
Icon[en_US]=gnome-panel-launcher
Exec=/home/jcgonzalez/ws/python/call_and_get_data/readCSV.py %F
Name[en_US]=readCSV into ipython
Name=readCSV into ipython
Icon=gnome-panel-launcher

De esta manera, tendremos una aplicación llamada readCSV en nuestro menú contextual de "Abrir con…", a la que podremos asociar los ficheros CSV. Al abrir uno con este script, se nos abre una sesión interactiva de Python (ipython en mi caso), con los datos cargados en memoria, y mostrando un mensaje con el nombre de la variable leída.

img

img

Incrustando Python en C++

2015-07-13

Me encanta navegar por la red buscando trucos nuevos de programación, incluso en lenguajes que no domino. Hoy he encontrado dos joyitas, la primera de las cuales detallo brevemente en esta entrada de blog. Se trata de la manera de incrustar la ejecución de scripts de Python en programas en C++.

Como es obligado, incluyo el programa "Hello World!" de ejemplo de marras.

1  #include <Python.h>
2  int main(int argc, char *argv[])
3  {
4      Py_Initialize();
5      PyRun_SimpleString("print 'Hello world!'");
6      Py_Finalize();
7      return 0;
8  }

Para compilar este código hay que saber dónde está nuestra instalación Python. Yo uso Anaconda de Continuum Analytics Inc.. De esta manera, en mi caso la compilación la realizo de esta forma:

$ g++ main.cpp -o main $(/opt/anaconda/bin/python-config --cflags --ldflags)

Una vez compilado y enlazado, comprobamos que el programa hace lo que se espera de él:

$ ./main
Hello world!

En fin, esto me abre un mundo de posibilidades.

¡Arrancamos!

2015-07-13

Esta es la obligada entrada cero de este nuevo blog sobre cositas de programación. Comenzamos.