La gestión de dependencias en C++ moderno se divide en dos estrategias principales: buscar librerías ya instaladas en el sistema mediante find_package o descargar el código fuente directamente mediante FetchContent. Esta distinción responde a una necesidad de equilibrio entre velocidad y reproducibilidad: find_package es extremadamente rápido porque aprovecha binarios ya compilados por el gestor de paquetes del sistema (como apt o brew), pero depende de que el entorno del desarrollador esté correctamente configurado. Por el contrario, FetchContent garantiza que todos los miembros del equipo usen exactamente la misma versión de una librería descargándola de un repositorio de Git, lo que elimina el problema de “en mi máquina sí funciona”, aunque aumenta el tiempo de la primera configuración.
Usa find_package para dependencias pesadas, complejas o estándares (como OpenSSL, Qt o Boost) y opta por FetchContent para librerías ligeras, header-only o cuando necesites un commit específico de un repositorio para asegurar la estabilidad del proyecto. Finalmente, para evitar que cada desarrollador tenga que recordar largas cadenas de comandos en la terminal, CMakePresets.json permite estandarizar configuraciones de compilación. Si usas find_package sin la palabra clave REQUIRED y la librería no está presente, CMake no se detendrá inmediatamente, lo que provocará errores crípticos durante la fase de compilación que son mucho más difíciles de depurar que un error de configuración claro.
# CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(GestionDependencias VERSION 1.0.0 LANGUAGES CXX)
# Forzamos el estándar C++20
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 1. find_package: Busca una librería instalada en el sistema.
# Usamos REQUIRED para asegurar que el proceso falle si no se encuentra.
find_package(Threads REQUIRED)
# 2. FetchContent: Descarga la dependencia directamente de su origen.
# Ideal para librerías pequeñas o cuando queremos controlar la versión exacta.
include(FetchContent)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 10.1.1 # Versión específica para asegurar reproducibilidad
)
# FetchContent_MakeAvailable descarga, añade al grafo de dependencias
# y configura la librería (equivalente a add_subdirectory).
FetchContent_MakeAvailable(fmt)
# Definición del ejecutable
add_executable(app_demo main.cpp)
# 3. Enlace de targets: Usamos "Imported Targets" para evitar gestionar
# manualmente los directorios de inclusión o las flags de compilación.
target_link_libraries(app_demo
PRIVATE
fmt::fmt # Target creado por FetchContent
Threads::Threads # Target creado por find_package(Threads)
)
(Para compilar este ejemplo, necesitas un archivo main.cpp que use <fmt/core.h> y <thread>).
En el ejemplo anterior, la directiva find_package(Threads REQUIRED) busca el paquete de hilos del sistema y crea el target Threads::Threads. Al usar este target en target_link_libraries, no solo enlazamos la librería, sino que heredamos automáticamente las flags de compilación necesarias (como -pthread en Linux), evitando errores de enlace manuales.
Con FetchContent_Declare, hemos definido la ubicación de la librería fmt. Es crucial especificar un GIT_TAG fijo; si no lo hiciéramos, CMake podría descargar la rama master en cada limpieza de la carpeta de compilación, lo que rompería tu código si la librería introduce cambios incompatibles. Al llamar a FetchContent_MakeAvailable(fmt), CMake descarga el código en la carpeta de compilación y lo integra en el árbol de objetivos. Esto nos permite usar el target fmt::fmt, que ya viene configurado con su propio INTERFACE_INCLUDE_DIRECTORIES.
Respecto a los modos de construcción, aunque no se declaran explícitamente en el CMakeLists.txt, el comportamiento del binario cambiará drásticamente según la variable CMAKE_BUILD_TYPE que pases por línea de comandos: Debug para desarrollo con símbolos de depuración y sin optimización, o Release para máxima velocidad mediante -O3. Para estandarizar esto, se utiliza un archivo CMakePresets.json que define, por ejemplo, un preset llamado ci-release que configure automáticamente CMAKE_BUILD_TYPE como Release y el generador como Ninja.
El error frecuente
Un error común es olvidar la palabra clave REQUIRED al usar find_package. Si intentas usar una librería como OpenSSL para cifrado y el sistema no la tiene, pero escribes find_package(OpenSSL), CMake continuará la configuración sin protestar. El error no aparecerá hasta que el compilador intente procesar un #include <openssl/ssl.h> o el enlazador busque los símbolos, lanzando un error de “archivo no encontrado” o “símbolo indefinido” que es mucho más difícil de rastrear que un error de configuración inmediato. Además, mezclar un CMAKE_BUILD_TYPE=Debug con una librería externa que fue compilada en Release (común en Windows/MSVC) puede causar fallos de segmentación debido a la incompatibilidad en la implementación de la biblioteca estándar de C++ (como el modo de depuración de iteradores).
N° 130