cpp-introduccion-cmake

Introducción a CMake para proyectos en C++

  • 5 min

CMake es una herramienta que genera sistemas de construcción a partir de una configuración común para nuestro proyecto.

Cuando empezamos a programar en C++, solemos tener un solo archivo main.cpp. Compilarlo es fácil: pulsas el botón de ejecución del IDE o escribes g++ main.cpp en la consola.

La situación cambia cuando el proyecto crece:

  • Tienes 20 archivos .cpp y 20 .h.
  • Usas librerías externas.
  • Quieres que tu código compile tanto en Windows (con Visual Studio) como en Linux (con GCC).

Hacer esto a mano es una pesadilla. Para eso usamos CMake.

CMake tiene fama de ser difícil, y su sintaxis a veces es extraña, pero es la herramienta que une todo el ecosistema de C++.

¿Qué es realmente CMake?

CMake es una herramienta de construcción de software de código abierto y multiplataforma que simplifica la creación y gestión de proyectos en C++.

Utiliza archivos de configuración denominados CMakeLists.txt para definir cómo se debe construir un proyecto.

Hay una confusión muy común: CMake no es un compilador. No compila tu código, sino que es un generador de sistemas de construcción (build system generator).

Piénsalo así:

  1. Tú escribes una “receta” en un archivo llamado CMakeLists.txt.
  2. CMake lee esa receta.
  3. CMake genera los archivos necesarios para tu herramienta de compilación favorita.
    • En Windows, puede generar una solución de Visual Studio (.sln).
    • En Linux, puede generar archivos para Make o Ninja.
    • En macOS, puede generar un proyecto de Xcode.

Gracias a esto, tú solo mantienes un único archivo de configuración, y CMake se encarga de adaptar el proyecto a cualquier sistema operativo o compilador.

Tu primer CMakeLists.txt

Todo proyecto de CMake empieza con un archivo de texto llamado, obligatoriamente, CMakeLists.txt (respetando las mayúsculas).

Vamos a crear el más básico posible para un “Hola Mundo”.

# 1. Versión mínima de CMake requerida (Buena práctica)
cmake_minimum_required(VERSION 3.10)

# 2. Nombre del proyecto y lenguaje
project(MiProyectoHolaMundo CXX)

# 3. Definir el estándar de C++ (ej: C++17)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# 4. Crear el ejecutable
# Sintaxis: add_executable(NombreDelEjecutable ArchivosFuente...)
add_executable(MiApp main.cpp)
Copied!

Los comandos en CMake (project, add_executable) no distinguen entre mayúsculas y minúsculas, pero la convención moderna es escribirlos en minúsculas. Las variables (CMAKE_CXX_STANDARD) sí son Case Sensitive.

Estructura de un proyecto con CMake

Una estructura típica de proyecto CMake podría ser la siguiente:

mi_proyecto/ ├── CMakeLists.txt # Configuración principal del proyecto. ├── src/ # Directorio de archivos fuente. │ ├── CMakeLists.txt │ ├── main.cpp │ └── utils.cpp └── include/ # Directorio de archivos de cabecera. ├── CMakeLists.txt └── utils.h

El flujo de trabajo

Una de las mejores prácticas en CMake es hacer Out-of-Source Builds.

Esto significa que no queremos ensuciar nuestra carpeta de código fuente con archivos temporales de compilación (.o, .obj, .exe). Queremos que toda la “basura” se vaya a una carpeta separada, generalmente llamada build.

El proceso estándar en la terminal es este:

Configuración

Le decimos a CMake que lea el CMakeLists.txt y genere los archivos de proyecto en la carpeta build.

# -S .      -> Source (dónde está el CMakeLists.txt, aquí mismo)
# -B build  -> Build (dónde generar los archivos temporales)
cmake -S . -B build
Copied!

Compilación

Ahora le pedimos a CMake que llame al compilador real (GCC, MSVC, Clang) para compilar lo que hay en build.

# --build build -> Compila lo que hayas configurado en esa carpeta
cmake --build build
Copied!

Si todo va bien, tendrás tu ejecutable dentro de la carpeta build (o build/Debug en Windows).

Trabajando con múltiples archivos

Un proyecto real nunca tiene un solo archivo. Imaginemos que tenemos main.cpp, calculadora.cpp y calculadora.h.

Podríamos listarlos todos en el ejecutable:

add_executable(MiApp main.cpp calculadora.cpp)
Copied!

Sin embargo, en C++ moderno, preferimos organizar el código en Librerías.

Podemos agrupar calculadora.cpp en una librería interna. Esto hace que el proyecto sea más modular.

cmake_minimum_required(VERSION 3.10)
project(ProyectoMatematico CXX)

# 1. Creamos una librería estática llamada "Calculos"
add_library(Calculos calculadora.cpp)

# 2. Creamos el ejecutable principal solo con main.cpp
add_executable(MiApp main.cpp)

# 3. Enlazamos (Link) la librería al ejecutable
# Esto le dice a CMake: "MiApp necesita usar el código de Calculos"
target_link_libraries(MiApp PUBLIC Calculos)
Copied!

El concepto de Target (Objetivo) es fundamental en CMake moderno. MiApp es un target. Calculos es un target. En lugar de usar variables globales, definimos relaciones entre targets: “Este target depende de este otro”.

Gestión de includes

Si tus archivos .h están en una carpeta separada (por ejemplo include/), necesitas decirle al compilador dónde buscarlos.

No uses flags del compilador (-I include). Usa el comando semántico de CMake:

# Le decimos que el target 'Calculos' tiene sus headers en la carpeta 'include'
target_include_directories(Calculos PUBLIC include)
Copied!

Al usar PUBLIC, cualquier otro ejecutable (como MiApp) que se enlace con Calculos heredará automáticamente esa ruta de inclusión. CMake se encarga de propagar esa configuración.