El próximo 28 de Abril se celebra el Día del Software Libre en la Escuela Superior de Ingeniería Informática (ESII) de Albacete.
Durante el evento se organizarán charlas y talleres sobre software libre, como la Fase Final del Concurso Univ. de Software Libre de CLM, Android, Bazaar, GvSIG, etc.
Entre los asistentes a los eventos habrá un sorteo de un móvil HTC con Android y un equipo Media Center.
Esta jornada se engloba dentro de las actividades organizadas para la Semana Cultural del Campus Universitario de Albacete, que tendrá lugar del 26 al 30 de Abril.
Puede encontrarse más información sobre la agenda de la jornada e inscribirse en: http://ceslcam.com/dia-del-software-libre-albacete.html
Este artículo es una guía de referencia para el uso básico de cámaras AXIS 214 Pam-Tilt-Zoom (PTZ). No obstante, es posible que para otros modelos de AXIS sea útil.
Esta receta explica cómo utilizar, fundamentalmente, el comando INSTALL de CMake para crear objetivos de instalación.
El comando INSTALL
CMake proporciona el comando INSTALL que facilita en gran medida seleccionar los componentes de nuestro proyecto que queremos instalar y dónde.
En esta receta nos vamos a centrar en un uso básico del comando. Para algo más avanzado nada mejor que la documentación.
Ejemplo
Supongamos que queremos instalar un binario y dos versiones de una misma librería: una estática y otra dinámica. El código CMake para este caso podría ser el siguiente:
Inicialmente, podrías añadir un comando por cada uno de los componentes que queremos instalar indicando el directorio destino. Sin embargo, como es habitual necesitar instalar varios objetivos, el comando INSTALL proporciona una forma más compacta:
En primer lugar, se asocia a la librería dinámica las cabeceras que serán necesarias instalar posteriormente como "cabeceras públicas" utilizando el comando SET_TARGET_PROPERTIES. Es posible asociar distintos archivos a objetivos para, posteriormente, usarlos en el comando INSTALL. De hecho, lo apropiado sería crear un FRAMEWORK... pero quizás es demasiado para este ejemplo. Consulta el manual para obtener más información que, además, explica cómo utilizar INSTALL de otras formas.
La forma en que se ha usado INSTALL es la más compacta. Básicamente, viene a decir lo siguiente (por defecto, CMAKE_INSTALL_PREFIX=/usr/local):
Al objetivo hello se le debe tratar como un ejecutable (RUNTIME) y debe instalarse en el directorio ${CMAKE_INSTALL_PREFIX}/bin.
Al objetivo dummy_static se le debe tratar como una librería estática (ARCHIVE) y debe instalarse en el directorio ${CMAKE_INSTALL_PREFIX}/lib/static.
Al objetivo dummy_shared se le debe tratar como una librería dinámica (LIBRARY) y debe instalarse en el directorio ${CMAKE_INSTALL_PREFIX}/lib.
Finalmente, las cabeceras públicas (PUBLIC_HEADER) configuradas a lo largo del programa deben instalarse en ${CMAKE_INSTALL_PREFIX}/include.
Y con ello, ya disponemos de un objetivo install en el Makefile generado que nos permitirá instalar el software.
¿Cómo personalizar mi instalación?
Supón que tienes un directorio en tu home llamado ~/usr y que las variables de entorno pertinentes las tienes configuradas a ese path. Estaría muy bien que pudieras hacer que CMAKE_INSTALL_PREFIX tuviera un valor concreto para instalar el software en ese lugar. Para ello, simplemente ejecuta lo siguiente:
Sin embargo, es posible que hayas instalado otras cosas en ese ruta (cabeceras de código, por ejemplo) y las uses en tu proyecto. Obviamente, CMake no puede buscar en todo el disco duro en busca de la cabecera problemática. Por ello, es recomendable que cuando cambies el directorio de instalación también cambies lo siguiente:
De esta forma, los módulos que buscan cabeceras y librerías añadirán ese path a su ruta de búsqueda (además de las rutas del sistema).
En esta receta se explica cómo linkar binarios con librerías estáticas y dinámicas.
Linkado básico
A estas alturas ya conocemos cómo construir archivos binarios y librerías en CMake. Ahora supón que la librería dummy que se viene utilizando en los ejemplos está instalada en un path accesible y que el programa hello.cpp hace uso de la librería de forma dinámica. Una posible implementación de este escenario sería la siguiente:
En primer lugar, se comprueba que es posible acceder a la librería dummy (tanto la cabecera como el archivo .so). Utilizando el comando TARGET_LINK_LIBRARIES se indica a CMake que el objetivo hello debe ser linkado con la librería dummy_lib.
Para una librería estática, sería muy similar el código ya que CMake detecta que se trata de un tipo u otro y genera el código necesario para linkar estática o dinámicamente según la librería encontrada.
FindPkgConfig
Habitualmente, muchas librerías que se utilizan en sistemas como GNU utilizan la herramienta pkg-config que ayuda a obtener las distintas variables necesarias (CFLAGS, LDFLAGS, etc).
CMake proporciona un módulo llamado FindPkgConfig que incluye comandos útiles para encontrar librerías que utilizan pkg-config. Pese a que se incluye en la distribución estándar, CMake no recomienda el uso de este módulo porque es posible que haya sistemas privativos que no tengan instalada la aplicación pkg-config. Pero eso, no nos preocupa... ¿verdad? :-)
Supón que el programa hello utiliza glib. Podríamos comprobar que está instalado y enlazarlo como sigue:
¿Y cómo es que en glib_LIBRARIES y glib_INCLUDE_DIRS se guardan esos datos?. Los módulos Find* de CMake crean variables para que el usuario las pueda utilizar en caso de encontrar la librería o el objeto que se esté buscando. Una buena forma de saber sobre qué variables se dispone es consultando la documentación. En la cabecera de los módulos también viene esta información (en Debian /usr/share/cmake2.8/Modules/*.cmake).
Esta receta explica cómo compilar un Hola Mundo para el middleware ZeroC Ice utilizando CMake.
Software necesario
Puedes descargarte el software que se va a utilizar del siguiente repo subversion:
En el repositorio se encuentra la implementación de un "Hola Mundo" para ZeroC Ice y el CMakeLists.txt asociado.
Herramientas externas
Un programa en Ice presenta un problema común: archivos que se generan con herramientas externas al proyecto son necesarios para la compilación. En Ice, slice2cpp genera código que debe ser incluido en la compilación.
CMake proporciona un comando muy útil llamado ADD_CUSTOM_COMMAND y que permite definir cómo se generan ciertos objetivos. Utilizando este comando, veamos cómo quedaría el programa CMake para construir el Hola Mundo en Ice:
Al principio, se hacen unas liberas comprobaciones de que la librería de Ice existe. A continuación se define el comando slice2cpp diciendo que como salidas los ficheros hello.cpp y hello.h a partir del fichero Slice hello.ice. Los archivos de salida son dependencias de los distintos ejecutables que se deben crear. Finalmente, se enlaza con las librerías necesarias.
Esta receta es una guía rápida de CMake. Se trata de una herramienta para la construcción automática de proyectos software, facilitando su portabilidad.
Esta receta explica cómo construir librerías estáticas y dinámicas con CMake.
Software necesario
Los ejemplos que vamos a utilizar puedes descargarlos utilizando subversion desde:
Los directorios contienen una librería con una definición de la clase Dummy (dummy.h y dummy.cpp) con un único método. También se incluye el CMakeLists.txt correspondiente.
Librería estática
Para crear una librería estática nada más fácil que lo que sigue:
El comando INCLUDE_DIRECTORIES permite añadir los directorios donde se encuentran las cabeceras del proyecto. En este caso, el fichero dummy.h se encuentra en el mismo directorio donde están los fuentes (el mismo en el que se encuentra CMakeLists.txt). La variable global CMAKE_CURRENT_SOURCE_DIR tiene esta ruta.
De una forma similar a como se añadían ejecutables, para crear una librería se debe utilizar ADD_LIBRARY que acepta, entre muchos otros, los siguientes parámetros:
objetivo: se trata un nombre simbólico para el objetivo que representa el crear la librería. Si no se especifica otra cosa, este nombre se utilizará para crear el fichero final de la forma libobjetivo.a.
STATIC: modo de la librería.
dependencias: de qué ficheros u objetivos depende la librería para su construcción
Librería dinámica
A la vista de lo anterior, parece sencillo crear una librería dinámica. ¿Bastará cambiar STATIC por SHARED?. Sí, con eso bastaría. Pero vamos a añadir algo más para hacer que la librería sea más "distribuible":
En CMake, los targets (objetivos) tienen propiedades que, a priori, tienen ciertos valores por defecto. La propiedad SOVERSION de los objetivos que son librerías dinámicas, por defecto, no tiene valor. Sin embargo, es conveniente versionar las librerías para que aquellas plataformas que lo soporten (como por ejemplo, GNU) puedan gestionar las versiones.
Con el comando SET_PROPERTY se puede modificar las propiedades de las estructuras que se van creando en el programa. En este caso, para el objetivo 'dummy' se cambia el valor de la propiedad SOVERSION a 1.0. De esta forma, al compilar la librería CMake generará los enlaces simbólicos necesarios de forma automática.
Librería estática y dinámica
Es posible que para una misma librería quieras que pueda ser enlazada de forma estática y también dinámica. Veamos una posible solución:
No se puede utilizar dummy como objetivos de ambas librerías. Lo que se puede hacer, por tanto, es crear un objetivo por cada tipo de librería (dummy_shared y dummy_static). Si no decimos nada más, CMake nos generará los archivos libdummy_static.a, libdummy_shared.so,... Y eso, obviamente, no es lo deseable.
Por ello utilizamos SET_TARGET_PROPERTIES que sirve para configurar propiedades de objetivos (es un caso particular de SET_PROPERTY).
Referencias
Documentación CMake2.8: consultar las propiedades genéricas y de los diferentes tipos abstractos (directorios, objetivos, ficheros fuente...).
Esta receta explica cómo compilar un programa trivial con CMake.
Software necesario
El ejemplo que vamos a utilizar puedes descargarlo utilizando subversion desde:
svn co https://arco.esi.uclm.es/svn/public/samples/cmake/single_binary
El directorio 'single_binary' contiene una implementación (hello.cpp) de un "Hola Mundo" en C++ y el archivo CMakeLists.txt.
Versión "quick" y no tan "dirty"
El contenido del archivo es el siguiente:
En primer lugar, se especifica la versión mínima de CMake que se debe utilizar. A continuación, se debe dar un nombre al proyecto utilizando el comando PROJECT.
Como se trata de un proyecto archi-sencillo, sólo consta de un ejecutable y éste se debe generar a partir del fichero "hello.cpp" (su dependencia). El comando ADD_EXECUTABLE acepta una lista de dependencias. Como puedes ver, prácticamente es una regla de Makefile pero más abstracta de forma que sea posible no sólo crear Makefiles sino también exportar a proyectos de Eclipse y otros entornos de desarrollo privativos (a los cuales no recomiendo exportar).
Versión "paranoic"
En el ejemplo anterior, desde un punto de vista ortodoxo, hemos supuesto muchas cosas. Por ejemplo, en el fichero "hello.cpp" se incluye la cabecera "iostream". ¿Hemos comprobado que está instalada? El compilador de C++ que utilizamos, ¿tiene soporte para que se pueda utilizar el namespace "std"? Como ves, hay muchas cosas que se han dado por supuestas en el ejemplo anterior.
Aunque para este ejemplo no sea necesario, veamos una versión del archivo CMakeLists.txt más avanzada:
En este ejemplo se introduce el uso de módulos que se proporcionan en la distribución de CMake 2.8 en Debian:
TestForSTDNamespace: incluye el código necesario para comprobar que existe el compilador soporta el uso del espacio de nombres std. Esta macro modifica una variable global (CMAKE_NO_STD_NAMESPACE) con el resultado de dicho test.
CheckIncludeFileCXX: se trata de un módulo que contiene el comando CHECK_INCLUDE_FILE_CXX que acepta como primer parámetro el nombre del archivo a incluir y, como segundo parámetro, una variable donde almacenar el resultado. De esta forma, utilizando un IF podemos comprobar que efectivamente se encuentra instalada la cabecera iostream.