Práctica de CMake
Este artículo demostrará cómo usar CMake para gestionar un proyecto C++ de complejidad media, desde la creación del proyecto hasta el flujo completo de compilación y ejecución.
El contenido cubre la configuración básica, el enlazado de bibliotecas, las pruebas unitarias, los comandos personalizados y características avanzadas como la compilación cruzada multiplataforma.
Flujo de compilación de CMake
La siguiente figura muestra el flujo completo de compilación de CMake desde el código fuente hasta el archivo ejecutable final:
Resumen del proyecto
Construiremos un proyecto C++ que contiene un programa principal, una biblioteca estática y pruebas unitarias.
La biblioteca proporciona funciones de operaciones matemáticas (suma, resta, multiplicación), el programa principal llama a estas funciones y el código de prueba verifica su correctitud.
Estructura del directorio del proyecto
MyProject/
├── CMakeLists.txt # 根构建配置(项目定义、全局设置)
├── src/
│ ├── main.cpp # 主程序入口
│ ├── CMakeLists.txt # src 子目录构建配置(库 + 可执行文件)
│ ├── lib/
│ │ ├── module1.cpp # 数学模块一:加法、减法
│ │ └── module2.cpp # 数学模块二:乘法
│ └── include/
│ └── mylib.h # 公共头文件(函数声明)
└── tests/
├── test_main.cpp # 单元测试用例
└── CMakeLists.txt # 测试子目录构建配置
Out-of-Source Build (compilación fuera del directorio de fuentes)Es la mejor práctica de CMake: todos los artefactos de compilación (Makefile, archivos objeto, ejecutables) se colocan en un directorio build independiente, sin contaminar el directorio del código fuente.
Crear el archivo CMakeLists.txt
CMakeLists.txt es el archivo de configuración central de CMake, con una estructura jerárquica; hay uno en el directorio raíz y uno en cada subdirectorio.
CMakeLists.txt del directorio raíz
La configuración raíz define la información a nivel de proyecto y gestiona los subdirectorios mediante add_subdirectory.
Ejemplo
project(MyProject VERSION 1.0) # Definir el nombre del proyecto y el número de versión
# Establecer el estándar C++ como C++11 y marcarlo como obligatorio
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Agregar ruta de búsqueda global de archivos de cabecera (PROJECT_SOURCE_DIR es el directorio raíz del proyecto)
include_directories(${PROJECT_SOURCE_DIR}/src/include)
# Agregar subdirectorios; CMake procesará sus CMakeLists.txt en orden
add_subdirectory(src) # Procesar bibliotecas y ejecutables
add_subdirectory(tests) # Procesar pruebas
CMakeLists.txt del directorio src
Este archivo define el objetivo de biblioteca (MyLib) y el objetivo de ejecutable (MyExecutable), y establece las dependencias.
Ejemplo
add_library(MyLib STATIC
lib/module1.cpp
lib/module2.cpp
)
# Especificar la ruta pública de archivos de cabecera para MyLib (PUBLIC indica que los usuarios también heredarán esta ruta)
target_include_directories(MyLib PUBLIC ${CMAKE_SOURCE_DIR}/src/include)
# Crear el objetivo de ejecutable MyExecutable
add_executable(MyExecutable main.cpp)
# Enlazar MyLib al ejecutable (PRIVATE indica que solo MyExecutable lo necesita)
target_link_libraries(MyExecutable PRIVATE MyLib)
Diferencia entre PRIVATE y PUBLIC:PRIVATEIndica que la dependencia solo la necesita el objetivo actual,PUBLICIndica que la dependencia se transmite a otros objetivos que enlazan con el objetivo actual.
CMakeLists.txt del directorio tests
La configuración de pruebas utiliza el framework Google Test; primero se debe buscar e incluir el paquete GTest.
Ejemplo
# Imprimir un mensaje informativo después de que MyExecutable se compile
add_custom_command(
TARGET MyExecutable
POST_BUILD # Ejecutar después de la compilación
COMMAND ${CMAKE_COMMAND} -E echo "¡MyExecutable compilado!"
COMMENT "Imprimir información de compilación completada" # Descripción que se muestra en el registro de compilación
)
Objetivo personalizado (add_custom_target)
Un objetivo personalizado es un objetivo independiente que no produce archivos de salida y se utiliza para ejecutar tareas específicas.
Ejemplo
# Crear el objetivo run, que ejecuta el programa compilado
add_custom_target(run
COMMAND ${CMAKE_BINARY_DIR}/src/MyExecutable
DEPENDS MyExecutable # Depende de MyExecutable para asegurar que se compile primero
COMMENT "Ejecutar MyExecutable"
)
Ejecutar el objetivo personalizado:
# 在 build 目录下执行 make run # o cmake --build . --target run
Diferencia entre comando personalizado y objetivo personalizado:add_custom_commandSe adjunta a un objetivo y se activa en una etapa específica de la compilación de ese objetivo;add_custom_targetEs un objetivo independiente que solo se ejecuta si se especifica explícitamente.
Multiplataforma y compilación cruzada
Una de las principales ventajas de CMake es el soporte multiplataforma, que permite cambiar fácilmente la plataforma y la arquitectura objetivo.
Especificar la plataforma objetivo
La plataforma objetivo se puede especificar mediante la variable CMAKE_SYSTEM_NAME.
# 为 Linux 平台交叉编译 cmake -DCMAKE_SYSTEM_NAME=Linux .. # 为 Windows 平台交叉编译 cmake -DCMAKE_SYSTEM_NAME=Windows ..
Uso del archivo de toolchain
Para escenarios complejos de compilación cruzada, se recomienda utilizar un archivo de toolchain para gestionar de forma centralizada toda la configuración relacionada con la plataforma.
Primero, cree el archivo de toolchain toolchain.cmake:
Ejemplo
# Configuración del toolchain de compilación cruzada ARM Linux
# Especificar el sistema objetivo
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
# Especificar la ruta del compilador cruzado (ajustar según el entorno real)
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
# Especificar la ruta del sistema de archivos raíz del sistema objetivo (opcional, para buscar bibliotecas y archivos de cabecera)
set(CMAKE_FIND_ROOT_PATH /path/to/arm/sysroot)
# Buscar programas solo en el sysroot objetivo (no en el sistema host)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Buscar archivos de biblioteca solo en el sysroot objetivo
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
# Buscar archivos de cabecera solo en el sysroot objetivo
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
Compilar usando el archivo de toolchain:
# 在 build 目录下执行 cmake -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake .. cmake --build .
El archivo de toolchain centraliza la gestión de configuraciones relacionadas con la plataforma, como el compilador, el enlazador y las rutas de bibliotecas, de modo que cambiar de plataforma objetivo solo requiere especificar un archivo de toolchain diferente, sin modificar CMakeLists.txt.
Tipos de compilación y opciones comunes
CMake admite varios tipos de compilación, controlados mediante la variable CMAKE_BUILD_TYPE.
| Tipo de compilación | Opciones de compilación | Escenario aplicable |
|---|---|---|
| Debug | -g (símbolos de depuración) | Etapa de desarrollo y depuración, se requieren puntos de interrupción e inspección de variables |
| Release | -O2 -DNDEBUG (optimización + eliminación de aserciones) | Publicación en producción, se busca velocidad de ejecución |
| RelWithDebInfo | -O2 -g (optimización + símbolos de depuración) | Versión de publicación que requiere análisis de rendimiento u optimización de depuración |
| MinSizeRel | -Os (optimización de tamaño) | Sistemas embebidos o escenarios sensibles al tamaño |
Especificar el tipo de compilación:
# Debug 构建 cmake -DCMAKE_BUILD_TYPE=Debug .. # Release 构建 cmake -DCMAKE_BUILD_TYPE=Release ..Otras extensiones