domingo, 3 de julio de 2016

Compilando con CMake


¿Que es?

Es una herramienta para multiplataforma para generar Makefiles a partir de scripts y poder compilar proyectos escritos en C, C++, Objective-C o Fortran usando un lenguaje más elevado que el propio Makefile o proyectos de IDEs comunes.

¿Para que sirve y porque usarlo?

Tiene capacidad para generar proyectos de Eclipse, Visual Studio, o plain Makefiles a partir de dichos scripts (CMakeLists.txt) para facilitar el desarrollo de un proyecto. Además, al estar orientado a cualquier plataforma y enfocado a facilitar la crosscompilación mediante toolchains, facilita la compilación en cualquier plataforma GNU/Linux, MAC y Windows con cualquier target (x86, ARM).

Hay que tener en cuenta que no hace magia, el propio código siempre tiene que estar adaptado a cada plataforma, tener precondiciones para que funcione en todas, etc. No obstante, usar CMake facilita mucho la adaptación si tienes en cuenta ciertos detalles que se adquieren con la experiencia.

Organización

A la hora de organizar un proyecto, lo primero que se tiene que tener claro es como lo vamos a organizar. Recomiendo usar control de versiones git, ya sea administrado por nosotros mismos o usando uno de los servicios populares: Github o Bitbucket. Sugiero utilizar diversas carpetas para separar conceptualmente las distintas partes del código, por ejemplo:

Proyecto general:

Constará con la configuración del proyecto en lineas generales, y la organización del código. Se recomienda tener unos ficheros básicos en la carpeta raiz del proyecto:
  • CMakeLists.txt principal que configure el proyecto usado por CMake.
  • README.md que explique que hace el proyecto, autores, datos de interés...
  • Contributing.md, fichero opcional para informar al que quiera contribuir en el proyecto.
  • License, fichero con la licencia del código, aunque este en las cabeceras de el source code, es importante dejarlo claro.
Y las carpetas con el contenido separado del proyecto:
  • Modules: Carpeta con las distintas librerias, bien sean estáticas o dinámicas que componen nuestro proyecto. Contendrán todo el código de la librería separado conceptualmente en distintas sublibrerías si procede. No debe contener ejecutables.
  • 3rdParty: Carpeta con todo el código de terceros, ya sea en submodulos de git
  • Samples: Carpeta con minimo código para crear ejecutables de ejemplo de funciones básicas de la libreria, o ejemplos complejos ilustrativos.
  • Test: Carpeta con todos los test unitarios para probar los módulos si son necesarios. Esta carpeta es opcional. Por ejemplo usando GTests
  • cmake, carpeta con los módulos y otros scripts de cmake utiles para compilar el proyecto.
Módulo individual: 

Será todo aquel en un subdirectorio del proyecto general. Puede ser un ejecutable o una librería de nuestro proyecto general. Se sugiere que conste con:
  • CMakeLists.txt: Fichero con la configuración de ese subproyecto concreto.
  • src: Carpeta con todos los ficheros *.cpp, *.c, *.m del subproyecto.
  • include: Carpeta con todos los ficheros *.h, *.hpp del subproyecto
Por supuesto, esto es un esquema recomendado, cada proyecto tiene sus propias pautas, normalmente explicadas en el README, la wiki, o la página oficial del proyecto. Yo por ejemplo siempre utilizo una carpeta build fuera de el código fuente. Las razones varias, aunque con gitignore puedes evitar añadirla al repositorio, ayuda a no hacerlo. Eclipse da problemas si tiene build anidado en el source. Ayuda a separar diversas compilaciones si necesitas copias sin emborronar la carpeta del código.

Por ejemplo, yo suelo tener una carpeta con el proyecto que contiene:

  • git, carpeta con todos los repositorios relacionados con el proyecto.
  • doc, carpeta con toda la documentación o referencias relacionadas.
  • build, carpeta con todas las plataformas compiladas, pruebas, Doxygen documentation output, etc.
Variables importantes:

CMake dispone de muchos comandos y variables importantes, que pueden ser consultadas en su referencia, y que nos ayudarán a configurar nuestro proyecto. Por supuesto, puedes crearte tus propios comandos, o macros si lo prefieres, llamar a ejecutables como hacen los Makefiles, y muchas cosas más.
  • CMAKE_CONFIGURATION_TYPES, configura Release, debug... en proyectos como VS
  • PROJECT_BINARY_DIR, variable que apunta a la base de la carpeta build
  • CMAKE_VERBOSE, información extra al generar el proyecto-
  • CMAKE_DEBUG_POSTFIX, sufijo en el caso de target debug
  • CMAKE_CURRENT_SOURCE_DIR, el proyecto donde se ejecuta el actual CMakeLists.txt  

Comandos importantes:
  • cmake_minimum_required(VERSION 2.8.8), versión minima probada de los scripts
  • PROJECT(${PROJ_MAIN_NAME}), Crea un proyecto con el nombre de la variable PROJ_MAIN_NAME.
  • ADD_SUBDIRECTORY, añade una subcarpeta al script actual, para revisar su CMakeLists.txt
  • ADD_EXECUTABLE, A partir del código proporcionado después del comando PROJECT, crea un ejecutable.
  • ADD_LIBRARY, lo mismo que ADD_EXECUTABLE pero creando una librería estática o dinámica según la opción proporcionada. 
  • LINK_DIRECTORIES, provee paths que contentan librerías que necesita el proyecto
  • TARGET_LIBRARY, provee el nombre de las librerías necesitadas.
  • MESSAGE, comando para printear información, datos o variables.
Para los más avanzados:

Tengo un pequeño repositorio en Github de código abierto llamado CMake-supporter. Utilizado como submodulo en un repositorio o simplemente copiando el código en la carpeta cmake, puede ser usado para manejar toolchains que compilan código en C++ para iOS y Android, también tiene templates de los proyectos principales y está perfectamente documentado como usarlos. Si no tienes mucha idea de CMake, pueden serte de utilidad tanto para no pensar en organizar un proyecto, como para aprender n poco sobre este sistema.

Un ejemplo:

 
Puedes ver el funcionamiento de un proyecto de prueba más avanzado que lo usa como submodulo en este repositorio. Simplemente haz:
git clone --recursive https://github.com/vgonisanz/cmake-supporter-sample
Configura y ejecuta el proyecto, y trata de compilarlo.

Trabajo futuro:

A parte de que mejoraré CMake-supporter, y el ejemplo, estoy pensando como generar automáticamente con unos pocos datos la estructura del proyecto, ya adaptada para no tener que copiar los templates y renombrar a mano, crear las carpetas, etc. Os mantendré informados.

Otros enlaces utiles:
  • http://www.cmake.org/cmake-tutorial/
  • http://outcoldman.com/en/archive/2014/05/02/xcode-and-cmake/
  • http://voices.canonical.com/jussi.pakkanen/2013/03/26/a-list-of-common-cmake-antipatterns/

No hay comentarios:

Publicar un comentario