Configurando proyectos multiplataforma fácilmente con CMake

Configurando proyectos multiplataforma fácilmente con CMake
Facebook Twitter Flipboard E-mail


Cuando nos disponemos a desarrollar un proyecto multiplataforma en C++, y a medida que éste crece y evoluciona con el tiempo, nos vemos en la tesitura de tener que mantener distintos scripts de compilación, teniendo que dar soporte a múltiples plataformas y entornos de desarrollo (Xcode, GNU Makefiles, Visual Sutidio 2003 a 2010, Eclipse, CodeBlocks, etc.).

Además, es habitual que tengamos varias configuraciones por cada proyecto, o como mínimo dos: una de debug, y otra de release. Por lo tanto, ante un cambio de configuración de software, o de ubicación de una dependencia interna, estamos obligados a cambiar todos los scripts anteriormente mencionados, teniendo que cambiar rutas a directorios de inclusión, de librerías, etc.
Todo esto hace que los programadores dediquemos demasiado tiempo a tareas que no tiene que ver con nuestro trabajo, que no es otro que programar.

Qué es CMake y cómo nos ayuda

Existen varias alternativas que ayudan a resolver algunos de los problemas detectados: SCons, Make, GNU Autotools, QMake, Jam, Maven, Ant, y un largo etc. Todas ellas, con más o menos funcionalidades que CMake, son alternativas válidas. La principal ventaja de CMake frente al resto es que la única dependencia que necesita es un entorno de ejecución C++.

CMake es un sistema de generación de scripts de compilación (build system) que utiliza ficheros de configuración independientes de plataforma y de compilador para generar nuestros proyecto. Con CMake no sólo controlamos la compilación de proyectos, sino que cubrimos todo el proceso de desarrollo de software, desde creación del proyecto, compilación, lanzamiento de tests, y empaquetado.

Con cada proyecto se distribuye un fichero llamado CMakeLists.txt, en el que describimos el proyecto utilizando un lenguaje con su propia sintaxis. Con él declaramos dónde están los directorios de inclusión, los ficheros fuente, las librerías de las que depende, los productos que generamos (ejecutables o librerías), etc. Partiendo de esta declaración de proyecto, el script es procesado por CMake y genera los ficheros de compilación para el entorno que elijamos. Actualmente están soportados los más comúnmente utilizados en cada plataforma: GNU Makefiles, Xcode, Visual Studio 2003 a 2010, Eclipse-CDT y CodeBlocks. En las siguientes líneas veremos de forma un poco más detallada su funcionamiento más básico.

Cada proyecto con CMake está compuesto por una serie de targets, que pueden ser ejectuables o librerías (que pueden ser a su vez shared o static). A la hora de usar dependencias externas, estén configuradas con CMake o no, se nos ofrecen unas funciones para la búsqueda de directorios de inclusión y de enlazado con las mismas. Nuestro fichero de proyecto hará referencia a las variables que contienen la información de ubicación de cada librería. De esta forma, si una librería cambia de sitio sus directorios de inclusión o sus binarios, no tenemos que cambiarlos en nuestro proyecto para cada script y configuración, sino que utilizando una variable se actualizará esta información correctamente.

Además, con CMake podemos acceder a variables de entorno del sistema, o bien sobreescribirlas directamente con valores que te interesen. Esto viene bien si queremos actualizar la versión de una librería de la que nuestro software es cliente. También podemos controlar las versiones de las librerías contra los que enlazamos.

Podemos marcar dependencias como obligatorias, o bien como opcionales, de forma que el proceso de compilado/enlazado se vea afectado en función de las dependencias que CMake encuentre. Además, si marcamos una dependencia como obligatoria y CMake no la encuentra, no se generarán ficheros de compilación. Es decir, si CMake se ejecuta de satisfactoriamente, nuestro proyecto compilará seguro. Esto es muy importante cuando trabajamos en un proyecto con varios programadores, cada uno utilizando su entorno preferido.

Una vez hemos creado el proyecto y lo hemos compilado en el entorno deseado, podemos crear reglas muy sencillas de instalación de binarios, copiando a la carpeta destino no sólo los resultados de nuestros ejecutables, sino también las librerías dinámicas que necesitamos para ejecutarlos. Es más, nos ofrece facilidades para determinar en tiempo de instalación qué librerías dinámicas necesita un ejecutable (utilizando internamente herramientas propias de cada Sistema Operativo. Por ejemplo en Windows se usa dumpbin), facilidades para buscarlas, y facilidades para moverlas al destino.

Además, podemos declarar tests unitarios (CTEST). En el caso de hacerlo, unos targets especiales serán generados por CMake. El el caso de Visual Studio, nos aparecerá un proyecto llamado RUN_TESTS que engloba todos los tests declarados, y que si lo lanzamos, nos sacará el resultado de cada uno, así como el tiempo de ejecución. Por otro lado, podemos mandar los resultados a un servidor (CDash), y en cuyo caso veremos un dashboard con todos lo resultados de los tests. Podemos programar estos tests para que sean ejecutados periódicamente sobre la rama inestable de nuestro software, y poder ver de forma contínua los resultados de los tests, así como resultados de code coverage.

Por último, con CMake somos capaces de generar instaladores de nuestros productos (CPACK). Tal y como ocurre en el caso de los generadores para Visual Studio, XCode, etc, tenemos generadores específicos para crear instaladores. Por ejemplo, si tenemos instalado el software de creación de instaladores NSIS en Windows, se nos generará un instalador con NSIS. Si estamos en Mac OSX, te nos generará un .app, etc. En el caso de Windows y Visual Studio, nos aparecerá en nuestra solución un proyecto más que se llama PACKAGE, y que si lo compilamos se construirá el instalable.

Conclusiones

La principal ventaja que otorga esta herramienta es que si queremos desarrollar un proyecto multiplataforma, nos olvidamos completamente de dar soporte a todas y cada una de las mismas. Además, utilizando un lenguaje de descripción de proyectos podemos añadir, modificar y eliminar dependencias con el mínimo esfuerzo necesario. Y por último, con CMake, tenemos el control sobre todo el proceso de producción de software, desde la configuración, compilación, testeo y empaquetado.


Roberto Garrido

Roberto Garrido es Ingeniero Informático especializado en la programación C++ de gráficos por computador. Actualmente trabaja en una empresa que desarrolla simuladores de conducción (trenes, camiones, tranvías) con propósitos formativos. Sus intereses se centran en la programación utilizando motores gráficos como Ogre3D u OpenSceneGraph.

Puedes seguirlo en Twitter: @che1404


Más información | CMake

Comentarios cerrados
Inicio