CMake
se refiere a cross-platform make, su uso principal es el de controlar el
proceso de compilación de software sin depender de la plataforma.
Actualmente abarca múltiples
herramientas para administrar la compilación, pruebas, y empaquetado de software.
Con CMake
es posible generar archivos de compilación Make
(Unix), Xcode
(Apple),
Visual Studio
(Microsoft), Ninja
, y Qt
, entre otros, para proyectos C
y C++
,
también se puede compilar, lanzar la ejecución de pruebas unitarias (ctest
), y
generar paquetes (cpack
).
Conforme han ido evolucionando los estándares para C
y C++
también lo ha hecho
CMake
, facilitando la forma de crear un proyecto. Desde 2014 con la liberación de
la versión 3.0 se considera como CMake moderno, en el cual se prioriza el uso de
targets y properties sobre las tradicionales variables.
Hola CMake
El primer paso para crear un proyecto CMake C++
es crear el archivo CMakeLists.txt
con las instrucciones mínimas requeridas:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.0...3.15)
project(hola_cmake VERSION 0.0.1 LANGUAGES CXX)
add_executable(${PROJECT_NAME} main.cpp)
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17)
La primera línea es un comentario.
La segunda línea valida que el proyecto sea compatible con la versión de CMake
instalada, donde la versión mínima es la 3.0
y la máxima es la 3.15
.
La tercera línea define el nombre del proyecto, su versión, y lenguaje.
La siguiente define el Binario Objetivo (target) y los archivos a incluir en éste.
La última línea define el estándar de C++
que el proyecto va a usar.
Con esas instrucciones ya se tienen los mínimos requerimientos para el proyecto
hola_cmake
con el siguiente código fuente:
// main.cpp
#include <iostream>
int main()
{
std::cout << "Hola CMake\n";
return 0;
}
Binarios objetivo: Targets
Un proyecto CMake
puede tener el objetivo de crear uno o varios Ejecutables, y Bibliotecas.
Los archivos binarios generados tienen diferentes sufijos o prefijos dependiendo
de la plataforma destino.
Por ejemplo, para los ejecutables en Windows CMake
agrega el sufijo .exe
.
Para las bibliotecas se puede configurar que sea estática (.lib
), o dinámica
(compartida .dll
, .so
, .dylib
).
El nombre del target debe ser único en el proyecto, pero pueden existir varios
targets con diferente nombre en un mismo proyecto. Desde la versión 3.1
de CMake
está disponible el comando target_sources()
.
En las versiones más recientes (>3.15
) los archivos de código fuente se pueden
definir por separado de los targets por medio del comando target_sources()
:
# CMakeLists.txt
#...
add_executable("hola_cmake")
add_library("hola_cmake_bib_estatica" STATIC)
add_library("hola_cmake_bib_compartida" SHARED)
target_sources("hola_cmake" PUBLIC main.cpp)
target_sources("hola_cmake_bib_estatica" PUBLIC foo.cpp)
target_sources("hola_cmake_bib_compartida" PUBLIC bar.cpp)
#...
Opciones
Las opciones son variables personalizadas para el proyecto que el usuario puede
modificar sin necesidad de cambiar el archivo CMakeLists.txt
:
# CMakeLists.txt
#...
# Options
option(ENABLE_TESTS "Habilitar compilación de pruebas" False)
option(ENABLE_CONAN "Habilitar uso de Conan" False)
#...
Así el usuario puede ejecutar los comandos de generación con valores diferentes dependiendo de la configuración requerida. Por ejemplo para una configuración en modo release se puede dejar sin habilitar la compilación de pruebas, pero para una configuración en modo debug habilitarlas:
cmake .. -DENABLE_TESTS=True
Subdirectorios
Por medio del comando add_subdirectory()
es posible indicarle al proyecto que
en un subdirectorio hay otro archivo CMakeLists.txt
que también hace parte del
proyecto.
Teniendo un directorio exclusivo para pruebas (test) con su
propio archivo de CMakeLists
, el archivo principal lo agrega al proyecto así:
# CMakeLists.txt
#...
if(ENABLE_TESTS)
add_subdirectory(test)
else()
message("Compilación de pruebas deshabilitada")
endif()
Ejecución de CMake
La ejecución de los comandos CMake
se divide en dos pasos: generación y compilación.
CMake
incluye integración con múltiples IDEs
en las cuales se pueden personalizar
estos dos pasos.
La generación corresponde a crear los archivos con las instrucciones de compilación
de acuerdo al generador seleccionado. Si no se selecciona uno, CMake
elige
el predeterminado para la plataforma en la cual se está ejecutando.
Es recomendable que la generación y compilación se hagan en un directorio diferente
al directorio raíz del proyecto, y dentro de éste, pero que sea excluido del
sistema de control de versiones.
Para un sistema GNU/Linux con Ubuntu, para el proyecto hola_cmake
se puede crear el
directorio cmake-build/
desde el cual se ejecuta el comando de generación:
cmake .. -G "Unix Makefiles" -DENABLE_TESTS=True
Donde "Unix Makefiles"
es el generador elegido.
La compilación desde CMake
crea los archivos binarios para los targets
definidos en el proyecto. Desde un terminal Unix el comando de compilación
desde el directorio cmake-build/
es:
cmake --build ./
Si los archivos de compilación fueron generados de manera satisfactoria, y la compilación fue exitosa, se habrán creado los objetivos:
hola_cmake
libhola_cmake_bib_estatica.a
libhola_cmake_bib_compartida.so
Al correr el ejecutable se obtiene el resultado esperado:
$ ./hola_cmake
Hola CMake
Consideraciones
- Para versiones de cmake menores a 3.15 pueden aparecer mensajes de
advertencia relacionados con el comando
target_sources()
.
Fuentes
- Documentación oficial de CMake
- Henry Schreiner: Introducción a CMake moderno - Gitbook.
- Pablo Arias: It’s time to do cmake right - Blog.
- Bill Hoffman: Open Source Tools to Build Test and Deploy C++ Software - Google Tech Talk 2007.
- Mateusz Pusz: Git, CMake, Conan - How to ship and reuse our C++ projects - CppCon 2018.
Deja un comentario