lunes, 12 de noviembre de 2012

Integrando CMake con Eclipse CDT

Acostumbro a usar Eclipse para desarrollar en C++. Admito que se bebe la memoria (sobretodo cuando indexa el código), pero la funcionalidad que me da para navegar, buscar, autocompletar y depurar es impagable.

Ahora, la compilación deja un poco que desear. Para empezar, porque compilar un proyecto de Eclipse desde la linea de comandos no es tarea fácil. Para seguir, porque a veces se lía un poco con las dependencias y compila menos cosas de las que debiera. No mola nada que tu programa falle porque la mitad de los fuentes están compilados contra una versión de un fichero de cabecera distinta de la otra mitad. Lo digo por experiencia.

Así que la opción era pasarse a otro sistema de compilación, pero manteniendo la integración con Eclipse. Tras varias pruebas, el que más me convencía era CMake. Multiplataforma, código abierto, permite generar todos los ejecutables y librerías que quieras (y no solo uno por proyecto, como hace Eclipse) y gestiona el descubrimiento de las dependencias que te hagan falta. Bueno, y muchas más cosas. Yo, desde que lo uso, soy más feliz.

Sin embargo la integración con Eclipse no es inmediata. El mismo wiki de CMake propone varias opciones. La primera es generar el proyecto de Eclipse CDT con CMake, y la ponen como recomendada. Tiene varias desventajas conocidas. Yo además veo un par de choques de conceptos. Primero, los ficheros generados por CMake deberían ser transitorios, mientras que Eclipse espera que los ficheros de proyecto sean permanentes, como el código fuente. Segundo, CMake generará un fichero de proyecto por cada configuración (Debug, Release, RelWithDebInfo o MinSizeRel), mientras que Eclipse utiliza el mismo proyecto para todas las configuraciones.

La segunda opción, en la que me he basado, propone el camino contrario: usar un proyecto de tipo Standard Makefile que llame a CMake en el momento de compilar. En este tipo de proyecto, Eclipse asume que el usuario gestiona sus ficheros Makefile. En el wiki sugieren usar o bien una herramienta externa o bien make targets adicionales para llamar a CMake. Eso hace que la compilación pueda requerir un paso adicional por parte del usuario, así que he ido un poco más alla: utilizar un Makefile maestro que dependa de los Makefiles que genera CMake, y que por tanto le llame cuando sea necesario. Además, las reglas dependerán de la configuración usada por Eclipse y llamarán a CMake de manera consecuente. El Makefile es el siguiente:

CONFIG?=Debug

all: build/$(CONFIG)/Makefile
    $(MAKE) -C build/$(CONFIG)

build/$(CONFIG)/Makefile: build/$(CONFIG)
    cmake -E chdir build/$(CONFIG) cmake -G "Unix Makefiles" ../../ -DCMAKE_BUILD_TYPE:STRING=$(CONFIG)

build/$(CONFIG):
    mkdir -p build/$(CONFIG)

.PHONY: clean

clean:
    rm -fr build/$(CONFIG)
   
Comienza estableciendo la variable CONFIG, en caso de que no exista, a la configuración Debug. Luego vienen las tres reglas principales:
  1. Una regla que compila el código cuando el fichero build/$(CONFIG)/Makefile se haya generado.
  2. Una regla que genera ese fichero llamando a CMake desde el directorio build/$(CONFIG), si existe.
  3. Y una regla que crea ese directorio.
Finalmente está la regla que limpia la compilación anterior.

Fijaos que al llamar a CMake, se establece la variable CMAKE_BUILD_TYPE al mismo valor que CONFIG, para que compile el proyecto con los parámetros adecuados. Las alternativas, como he dicho antes, son Debug, Release, RelWithDebInfo y MinSizeRel. Así que hay hacer que Eclipse llame a make con el valor apropiado de la variable CONFIG. Es tan sencillo como ir a las propiedades del proyecto -> C/C++ Build -> Environment, y añadir una variable de entorno llamada CONFIG con el valor que queramos. Si le damos el valor ${ConfigName}, haremos que el nombre de la configuración de Eclipse coincida con la de CMake, y la jugada ya es redonda.

No hay comentarios: