Ir al contenido

Sistemas de Compilacion: CMake y Gestion de Paquetes

Domina los fundamentos de CMake, CMake moderno basado en targets, FetchContent para dependencias, gestores de paquetes vcpkg y Conan, y flags del compilador para compilaciones seguras y optimizadas.

Por Que Importan los Sistemas de Compilacion

A medida que los proyectos de C++ crecen mas alla de un solo archivo, necesitas un sistema de compilacion para gestionar la compilacion, el enlace, las dependencias y las diferencias de plataforma. Ejecutar manualmente g++ *.cpp no escala: necesitas compilaciones incrementales (recompilar solo archivos cambiados), deteccion de bibliotecas, soporte multiplataforma y compilaciones reproducibles. CMake se ha convertido en el generador de sistema de compilacion estandar de facto para C++. No compila codigo por si mismo — genera archivos de compilacion nativos (Makefiles, archivos Ninja, proyectos de Visual Studio) para tu plataforma.

Fundamentos de CMake

Un archivo CMakeLists.txt describe la estructura de tu proyecto. CMake moderno (3.12+) usa un enfoque basado en targets donde todo se adjunta a targets (ejecutables o bibliotecas):

CMakeLists.txt
# CMakeLists.txt — minimum viable project
cmake_minimum_required(VERSION 3.20)
project(MyApp VERSION 1.0.0 LANGUAGES CXX)

# Set C++ standard for the entire project
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)  # use -std=c++20, not -std=gnu++20

# Create an executable target
add_executable(myapp
    src/main.cpp
    src/engine.cpp
    src/utils.cpp
)

# Add include directories for this target
target_include_directories(myapp PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/include
)

# Create a library target
add_library(mathlib STATIC
    src/math/vector.cpp
    src/math/matrix.cpp
)

target_include_directories(mathlib PUBLIC
    ${CMAKE_CURRENT_SOURCE_DIR}/include/math
)

# Link the library to the executable
target_link_libraries(myapp PRIVATE mathlib)

# Compiler warnings — essential for quality code
target_compile_options(myapp PRIVATE
    $<$<CXX_COMPILER_ID:GNU,Clang>:-Wall -Wextra -Wpedantic -Werror>
    $<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
)

Compilando con CMake

CMake usa un proceso de dos pasos: configurar (generar archivos de compilacion) y compilar (compilar). Usa siempre compilaciones fuera del arbol fuente para mantener el arbol fuente limpio:

terminal
# Configure — generate build files in a 'build' directory
cmake -B build -DCMAKE_BUILD_TYPE=Release

# Build — compile the project
cmake --build build --parallel $(nproc)

# Install (optional)
cmake --install build --prefix /usr/local

# Common build types:
# Debug          — -O0 -g (full debug symbols, no optimization)
# Release        — -O3 -DNDEBUG (max optimization, no asserts)
# RelWithDebInfo — -O2 -g -DNDEBUG (optimized with debug info)
# MinSizeRel     — -Os -DNDEBUG (optimize for size)

# Run with sanitizers (add to CMakeLists.txt or command line)
cmake -B build-asan -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer"

FetchContent: Gestion de Dependencias

El modulo FetchContent de CMake descarga y compila dependencias en tiempo de configuracion, facilitando la incorporacion de bibliotecas sin configuracion manual:

CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(MyApp LANGUAGES CXX)

include(FetchContent)

# Fetch GoogleTest
FetchContent_Declare(
    googletest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG        v1.14.0
)

# Fetch fmt (formatting library)
FetchContent_Declare(
    fmt
    GIT_REPOSITORY https://github.com/fmtlib/fmt.git
    GIT_TAG        10.2.1
)

# Download and make available
FetchContent_MakeAvailable(googletest fmt)

add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE fmt::fmt)

# Test executable
enable_testing()
add_executable(tests tests/test_main.cpp)
target_link_libraries(tests PRIVATE GTest::gtest_main)
add_test(NAME unit_tests COMMAND tests)

vcpkg y Conan

Para proyectos mas grandes con muchas dependencias, los gestores de paquetes dedicados de C++ proporcionan mejor resolucion de dependencias y almacenamiento en cache:

vcpkg (Microsoft) — se integra directamente con CMake a traves de un archivo de toolchain. Instala paquetes globalmente o en "modo manifiesto" con un archivo vcpkg.json:

{
    "dependencies": ["fmt", "spdlog", "nlohmann-json", "catch2"]
}

Usar con: cmake -B build -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake

Conan — usa un conanfile.txt o conanfile.py para declarar dependencias. Soporta multiples configuraciones de compilacion y almacenamiento en cache de binarios:

[requires]
fmt/10.2.1
spdlog/1.13.0

[generators]
CMakeDeps
CMakeToolchain

Ambos gestores de paquetes mantienen repositorios de miles de bibliotecas de C++ con binarios probados y pre-compilados.

Buena práctica

Sigue estas reglas para un CMake mantenible:

1. Usa siempre comandos basados en targets: target_include_directories, target_compile_options, target_link_libraries — nunca include_directories ni add_definitions (que contaminan el ambito global).
2. Comprende PRIVATE/PUBLIC/INTERFACE: PRIVATE = solo para este target, PUBLIC = para este target y sus consumidores, INTERFACE = solo para consumidores.
3. Establece el estandar de C++ por target o a nivel de proyecto, nunca con flags crudos como -std=c++20.
4. Habilita las advertencias maximas: -Wall -Wextra -Wpedantic (GCC/Clang) o /W4 (MSVC). Considera -Werror / /WX en CI.
5. Usa cmake --preset para compilaciones reproducibles (CMakePresets.json).
6. Nunca codifiques rutas de forma fija — usa find_package, FetchContent o gestores de paquetes.

Error común

Usar file(GLOB ...) para recolectar fuentes: file(GLOB SOURCES src/*.cpp) NO se re-ejecuta cuando se agregan o eliminan archivos. Los archivos nuevos no seran compilados hasta que reconfigures manualmente. Siempre lista los archivos fuente explicitamente.

Olvidar CMAKE_CXX_EXTENSIONS OFF: Sin esto, GCC usa -std=gnu++20 en lugar de -std=c++20, habilitando extensiones no estandar que rompen la portabilidad.

Mezclar add_subdirectory y FetchContent: Ambos incorporan proyectos externos, pero interactuan de forma diferente con la cache de compilacion. Elige un enfoque por dependencia.

No establecer CMAKE_EXPORT_COMPILE_COMMANDS ON: Esto genera compile_commands.json, que es necesario para clang-tidy, clangd (LSP) y muchas integraciones de IDE. Habilitalo siempre.

Puntos clave
  • CMake es el generador de sistema de compilacion estandar de C++ — aprende su API basada en targets
  • Usa comandos target_* (no de ambito global) con visibilidad PRIVATE/PUBLIC/INTERFACE
  • FetchContent descarga y compila dependencias en tiempo de configuracion — ideal para conjuntos pequenos de dependencias
  • vcpkg (modo manifiesto) y Conan manejan grafos de dependencias mas grandes con almacenamiento en cache de binarios
  • Siempre habilita las advertencias (-Wall -Wextra -Wpedantic) y usa compilaciones fuera del arbol fuente
  • Genera compile_commands.json para herramientas de IDE y analisis estatico

Quiz — Pon a prueba tus conocimientos

(15 XP)

1. Que hace `target_link_libraries(myapp PRIVATE mathlib)`?

2. Por que se considera una mala practica `file(GLOB SOURCES src/*.cpp)` en CMake?

3. Cual es el proposito de `CMAKE_CXX_EXTENSIONS OFF`?