Tutorial de CMake
22/06/2015
Llevo varios años usando de forma habitual CMake. Sin embargo me doy cuenta que alguien que quiera empezar a usar este sistema va a encontrarse con documentación confusa.
CMake se trata de una herramienta multiplataforma para generar instrucciones de compilación del código. No sustituye a las herramientas de compilación como Make o MSBuild, sino que nos proporciona un único lenguaje que será transformado a las instrucciones del sistema operativo donde nos encontremos. Sería un sustituto de Autotools.
Las ventajas son que no tenemos que tener varios archivos para gestionar la compilación. Usando CMake podemos generar el resto. Actualmente CMake (3.2.3) soporta:
En CMake las configuraciones estan centralizadas por defecto en un archivo llamado CMakeLists.txt. Este se encuentra en la carpeta central del proyecto. Normalmente con CMake los proyectos se construyen en una carpeta diferente de la que tenemos el código fuente. Es corriente crear una carpeta build en lo alto del proyecto. Así si tenemos un proyecto con CMake ya descomprimido haríamos lo siguiente.
También puedes usar la aplicación gráfica. Muy cómoda cuando debamos modificar las configuraciones.
Podemos ajustar las variables de CMake desde la interfaz de usuario, usando el modo interactivo de la línea de comandos (`cmake .. -i`) o usando flags cuando llamamos a CMake (`cmake .. -DCMAKE_CXX_FLAGS=-std=c++11`)
Ya estamos listos para crear nuestro primer archivo de configuración de CMake.
Vamos a ir viendo distintas versiones del archivo donde voy a ir añadiendo diferentes tareas. Estate atento a los comentarios de los archivos
Y ya está.
CMake permite ajustar muchas opciones como hemos visto con el asistente gráfico de CMake. Sin embargo no todas las variables se muestran ahí. Solo son modificables las que nosotros marquemos explícitamente. Se usa OPTION()
Usar SET para los archivos es muy fácil de entender, pero es posible que no queramos mantener una lista explícita del código fuente.
Esto tiene un inconveniente y es que CMake no detecta automáticamente si hay nuevos archivos que cumplen la característica, por lo que hay que forzar la recarga.
A veces es necesario incluir archivos de cabecera en localizaciones no estándar
CMake es extensible a través de módulos. La instalación por defecto de CMake trae unos cuantos módulos, no obstante, podemos añadir módulos solo para nuestro proyecto. Los módulos tienen extensión .cmake. Normalmente se dejan en una carpeta llamada `cmake`.
En ciertas situaciones querremos que no se pueda compilar el proyecto. MESSAGE es la solución.
CMake usa un único archivo, pero quizá nos conviene repartir la configuración de CMake por varias carpetas entre zonas diferenciadas.
Una de las características más interesantes de CMake es que es capaz de encontrar librerías externas que necesite nuestro programa. Esta característica se implementa con plugins de CMake. Aquí voy a necesitar wxWidgets.
Podemos añadir directivas del preprocesador de C++ con CMake
Se pueden crear árboles de dependencias en CMake
Ejemplo práctico usando CMake y Qt5 que es capaz de usar QML. Soporta archivos QRC de recursos. Requiere los plugins de Qt5.
CMake soporta Java, aunque no maneja dependencias como Maven o Gradle.
A partir de CMake 3.1, podemos definir el estándar de C y C++ que vamos a usar
En CMake podemos crear comandos personalizados. Por ejemplo, generar documentación con Doxygen
En Autotools es común usar un archivo con configuraciones en tiempo de compilación. Normalmente se trata de una cabecera con soporte para plantillas. En CMake se puede hacer.
config.hpp.in
CMake permite instalar también los programas
Pero `make install` es un poco incómodo. No se puede distribuir fácilmente. Aquí CMake presenta CPack, que genara instaladores. Yo soy reacio a usarlos pues son de mala calidad pero soporta:
CPack necesita que usemos el comando `cpack` en vez de `cmake`
CMake soporta correctamente GNU ASM. Nasm requiere más trabajo.
|CMAKE_CURRENT_SOURCE_DIR|La ruta completa a la carpeta donde se encuentra CMakeLists.txt|
|CMAKE_MODULE_PATH|Las rutas para buscar plugins de CMake|
|PROJECT_BINARY_DIR|La carpeta que se está usando para guardar los resultados de la compilación|
|CMAKE_INCLUDE_PATH|Las carpetas de búsqueda de cabeceras|
|CMAKE_VERSION|Versión de CMake|
|CMAKE_SYSTEM|El nombre del sistema|
|CMAKE_SYSTEM_NAME|El sistema operativo|
|CMAKE_SYSTEM_PROCESSOR|El procesador|
|CMAKE_GENERATOR|El generador usado en ese momento|
|UNIX|Si estamos en Linux, OS X, BSD o Solaris será cierto|
|WIN32|Si estamos en Windows|
|APPLE|En OS X|
|MINGW| Usando MinGW|
|MSYS| Usando MSYS|
|BORLAND| Usando Borland|
|CYGWIN| Usando Cygwin|
|WATCOM| Usando OpenWatcom|
|MSVC| Usando Visual Studio|
|MSVC10| Usando Visual Studio 10|
|CMAKE_C_COMPILER_ID| El identificador de compilador de C|
|CMAKE_CXX_COMPILER_ID| El identificador de compilador de C++|
|CMAKE_COMPILER_IS_GNUCC| El compilador de C es una variante de GNU GCC|
|CMAKE_COMPILER_IS_GNUCXX| El compilador de C++ es una variante de GNU G++|
|CMAKE_BUILD_TYPE| La configuración Debug/Release que estamos usando|
|CMAKE_C_COMPILER| La ruta al compilador de C|
|CMAKE_C_FLAGS| La configuración del compilador de C|
|CMAKE_C_FLAGS_DEBUG| La configuración del compilador de C solo si estamos en la configuración Debug|
|CMAKE_C_FLAGS_RELEASE| La configuración del compilador de C solo si estamos en la configuración Release|
|CMAKE_SHARED_LINKER_FLAGS| La configuración del compilador para librerías compartidas|
|BUILD_SHARED_LIBS| Por defecto en ADD_LIBRARY, las librerías son compartidas. Podemos cambiar esto|
Muchas más en la wiki de CMake
El RPath es importante en los sistemas UNIX. Se trata de cargar librerías dinámicas que no están en directorios estándar.
Esto hará que los ejecutables construidos en UNIX puedan cargar librerías desde la carpeta donde se encuentran. Al estilo Windows.
1º Regla de CMake. La documentación puede ser confusa
¿Qué es CMake?
CMake se trata de una herramienta multiplataforma para generar instrucciones de compilación del código. No sustituye a las herramientas de compilación como Make o MSBuild, sino que nos proporciona un único lenguaje que será transformado a las instrucciones del sistema operativo donde nos encontremos. Sería un sustituto de Autotools.
Las ventajas son que no tenemos que tener varios archivos para gestionar la compilación. Usando CMake podemos generar el resto. Actualmente CMake (3.2.3) soporta:
- Unix Make
- Ninja
- CodeBlocks
- Eclipse CDT
- KDevelop
- Sublime Text 2
- Borland Make
- MSYS Make
- MinGW Make
- NMake
- NMake JOM
- Watcom WMake
- Kate
- CodeLite
- Xcode
- Visual Studio (desde el 6 hasta 2013)
Usando CMake
En CMake las configuraciones estan centralizadas por defecto en un archivo llamado CMakeLists.txt. Este se encuentra en la carpeta central del proyecto. Normalmente con CMake los proyectos se construyen en una carpeta diferente de la que tenemos el código fuente. Es corriente crear una carpeta build en lo alto del proyecto. Así si tenemos un proyecto con CMake ya descomprimido haríamos lo siguiente.
mkdir build
cd build
cmake ..
# make o ninja o nmake o lo que toque
También puedes usar la aplicación gráfica. Muy cómoda cuando debamos modificar las configuraciones.
Podemos ajustar las variables de CMake desde la interfaz de usuario, usando el modo interactivo de la línea de comandos (`cmake .. -i`) o usando flags cuando llamamos a CMake (`cmake .. -DCMAKE_CXX_FLAGS=-std=c++11`)
El archivo CMakeLists.txt
Ya estamos listos para crear nuestro primer archivo de configuración de CMake.
Vamos a ir viendo distintas versiones del archivo donde voy a ir añadiendo diferentes tareas. Estate atento a los comentarios de los archivos
Compilar como programa main.cpp
PROJECT(MiProyecto)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
# Indicamos la versión mínima que necesitamos de CMake
SET(MiProyecto_SRC "src/main.cpp")
# Creamos la variable MiProyecto_SRC y le asignamos el valor "src/main.cpp" que es la ubicación de nuestro archivo.
# Por defecto las variables son listas o arrays
# Si tenemos dos archivos sería SET(MiProyecto_SRC "src/main.cpp"
"src/segundo.cpp")
# Se permite multilínea
ADD_EXECUTABLE(MiProyecto ${MiProyecto_SRC})
# Se creará un ejecutable llamado MiProyecto en Linux o MiProyecto.exe en Windows.
# Se hace referencia a las variables con ${NOMBRE_VARIABLE}.
Y ya está.
Trabajar con opciones y condicionales
CMake permite ajustar muchas opciones como hemos visto con el asistente gráfico de CMake. Sin embargo no todas las variables se muestran ahí. Solo son modificables las que nosotros marquemos explícitamente. Se usa OPTION()
PROJECT(MiProyecto)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
SET(MiProyecto_SRC "src/main.cpp")
OPTION(EXPERIMENTAL_FEATURE "Activar característica experimental" OFF)
# OPTION(NOMBRE_VARIABLE DESCRIPCION_LEGIBLE VALOR_POR_DEFECTO)
# ON/OFF es la pareja de valores booleanos en CMake. TRUE/FALSE también es correcto
IF(EXPERIMENTAL_FEATURE) # El condicional más básico
LIST(APPEND MiProyecto_SRC "experimental_feature.cpp")
# Añadimos un elemento a la lista
# También se puede hacer con
# SET(MiProyecto_SRC ${MiProyecto_SRC} "experimental_feature.cpp")
ENDIF()
ADD_EXECUTABLE(MiProyecto ${MiProyecto_SRC})
Usar librería estática
PROJECT(MiProyecto C CXX)
# Podemos marcar opcionalmente los lenguajes para que CMake busque los compiladores
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
SET(MiProyecto_SRC "src/main.cpp")
SET(Lib_SRC "lib/lib.cpp")
ADD_LIBRARY(Lib STATIC ${Lib_SRC})
# El comando es exactamente igual que ADD_EXECUTABLE, pero marcamos si STATIC o SHARED
ADD_EXECUTABLE(MiProyecto ${MiProyecto_SRC})
TARGET_LINK_LIBRARIES(MiProyecto ${Lib})
# Necesitamos "unir" la librería con nuestro ejecutable
# Si necesitamos una librería tal cual usamos su nombre
# TARGET_LINK_LIBRARIES(MiProyecto pthread)
# Se pueden hacer las llamadas que se quiera a TARGET_LINK_LIBRARIES
Usar librería dinámica
PROJECT(MiProyecto)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
SET(MiProyecto_SRC "src/main.cpp")
SET(Lib_SRC "lib/lib.cpp")
ADD_LIBRARY(Lib SHARED ${Lib_SRC})
ADD_EXECUTABLE(MiProyecto ${MiProyecto_SRC})
TARGET_LINK_LIBRARIES(MiProyecto ${MiProyecto_SRC})
Seleccionar archivos de forma masiva
Usar SET para los archivos es muy fácil de entender, pero es posible que no queramos mantener una lista explícita del código fuente.
PROJECT(MiProyecto)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
FILE(GLOB MiProyecto_SRC "src/*.cpp")
# FILE GLOB selecciona todos los archivos que cumplan la característica y los almacena en MiProyecto_SRC
# GLOB no es recursivo. Si lo necesitas, usa GLOB_RECURSE
ADD_EXECUTABLE(MiProyecto ${MiProyecto_SRC})
Esto tiene un inconveniente y es que CMake no detecta automáticamente si hay nuevos archivos que cumplen la característica, por lo que hay que forzar la recarga.
Copiar, crear, eliminar y descargar archivos
PROJECT(MiProyecto)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
FILE(GLOB MiProyecto_SRC "src/*.cpp")
# Copiar archivos
FILE(COPY "MiArchivo.cpp" DESTINATION mi-carpeta)
# COPY usa como destino siempre una carpeta
# Se puede crear con FILE(MAKE_DIRECTORY mi-carpeta)
# Crear archivos
FILE(WRITE "Generado.txt" "Este archivo ha sido generado por CMake\nLos archivos son: ${MiProyecto_SRC}")
# Borrar archivos
FILE(REMOVE "Generado.txt")
# No es recursivo, REMOVE_RECURSE sí lo es
# Descargar archivos
FILE(DOWNLOAD http://mi-servidor.com/archivo.tar.gz archivo.tar.gz)
# Podemos mostrar el progreso
# FILE(DOWNLOAD http://mi-servidor.com/archivo.tar.gz archivo.tar.gz SHOW_PROGRESS)
# Comprobar la suma MD5
# FILE(DOWNLOAD http://mi-servidor.com/archivo.tar.gz archivo.tar.gz EXPECTED_MD5 LaSumaMD5)
# Usar SSL
# FILE(DOWNLOAD http://mi-servidor.com/archivo.tar.gz archivo.tar.gz TLS_VERIFY ON)
# Guardar la información en un archivo de log
# FILE(DOWNLOAD http://mi-servidor.com/archivo.tar.gz archivo.tar.gz LOG descarga.log)
# Calcular suma de control
FILE(SHA256 archivo.tar.gz VARIABLE_CON_EL_HASH)
ADD_EXECUTABLE(MiProyecto ${MiProyecto_SRC})
Incluir archivos de cabecera
A veces es necesario incluir archivos de cabecera en localizaciones no estándar
PROJECT(MiProyecto)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
SET(MiProyecto_SRC
"src/main.cpp"
"src/algo_mas.cpp")
INCLUDE_DIRECTORIES("src/includes")
# Se añade el directorio a la ruta de búsqueda del compilador de turno
ADD_EXECUTABLE(MiProyecto ${MiProyecto_SRC})
Plugins de CMake
CMake es extensible a través de módulos. La instalación por defecto de CMake trae unos cuantos módulos, no obstante, podemos añadir módulos solo para nuestro proyecto. Los módulos tienen extensión .cmake. Normalmente se dejan en una carpeta llamada `cmake`.
PROJECT(MiProyecto)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
LIST(APPEND CMAKE_PLUGIN_PATH "cmake")
# Simplemente añadimos un nuevo lugar a buscar. Veremos como se usan los módulos más adelante
ADD_EXECUTABLE(MiProyecto_SRC "src/main.cpp")
Mostrar información y generar errores
En ciertas situaciones querremos que no se pueda compilar el proyecto. MESSAGE es la solución.
PROJECT(MiProyecto)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
MESSAGE("Información relevante")
MESSAGE(STATUS "Información sin relevancia")
MESSAGE(WARNING "Alerta, continúa la configuración y generación")
MESSAGE(SEND_ERROR "Error, continúa la configuración pero no generará")
MESSAGE(FATAL_ERROR "Error grave, detiene la configuración")
ADD_EXECUTABLE(MiProyecto "src/main.cpp")
Condicionales avanzados
PROJECT(MiProyecto)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
## Con variables booleanas, es decir, ON/OFF, TRUE/FALSE
IF(NOMBRE_VARIABLE)
MESSAGE("Algo es cierto")
ENDIF()
IF(NOT NOMBRE_VARIABLE)
MESSAGE("Algo no es cierto")
ENDIF()
# La estructura completa es algo así
IF(CONDICION)
ELSEIF(CONDICION_2)
ELSE()
ENDIF()
# Se pueden aplicar operadores lógicos
IF(CONDICION AND CONDICION_2)
IF(CONDICION OR CONDICION_2)
# Con números y texto
IF(VAR_1 LESS VAR_2) # VAR_1 < VAR_2 IF(VAR_1 GREATER VAR_2) # VAR_1 > VAR_2
IF(VAR_1 EQUAL VAR_2) # VAR_1 === VAR_2
IF(VAR_1 MATCHES REGEX) # Se comprueba la expresión regular
# Además, CMake provee operadores para trabajar directamente con archivos, comandos y ejecutables
IF(DEFINED VAR_1) # ¿Está definida VAR_1?
IF(COMMAND CMD_1) # ¿CMD_1 es un comando de CMake?
IF(POLICY POL_1) # ¿La directiva POL_1 está activada?
IF(TARGET MiProyecto) # ¿Está definido el ejecutable MiProyecto?
IF(EXISTS src/main.cpp) # ¿Existe el archivo src/main.cpp?
IF(src/main.cpp IS_NEWER_THAN src/old/main.cpp) # ¿Es src/main.cpp más nuevo que src/old/main.cpp?
IF(IS_DIRECTORY src/includes) # ¿src/includes es un archivo o una carpeta?
Bucles
PROJECT(MiProyecto)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
SET(MiProyecto_SRC
"src/main.cpp"
"src/list.cpp"
"src/algomas.cpp")
FOREACH(Archivo_SRC IN MiProyecto_SRC)
MESSAGE(STATUS "Procesando archivo ${Archivo_SRC}")
ENDFOREACH()
Submódulos
CMake usa un único archivo, pero quizá nos conviene repartir la configuración de CMake por varias carpetas entre zonas diferenciadas.
PROJECT(MiProyecto)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
ADD_SUBDIRECTORY(lib)
ADD_SUBDIRECTORY(src)
# src y lib tienen un CMakeLists.txt cada uno
Librerías externas
Una de las características más interesantes de CMake es que es capaz de encontrar librerías externas que necesite nuestro programa. Esta característica se implementa con plugins de CMake. Aquí voy a necesitar wxWidgets.
PROJECT(MiProyecto)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
FIND_PACKAGE(wxWidgets)
# El plugin debe llamarse FindPackagewxWidgets.cmake, este esta incluido en la distribución estándar de CMake
# En grandes librerías como wxWidgets, podemos pedir solo ciertos componentes
# FIND_PACKAGE(wxWidgets COMPONENTS core gl html base net)
# Podemos hacer que CMake no continúe si no encuentra la librería
# FIND_PACKAGE(wxWidgets REQUIRED)
# Si todo va bien, tenemos las variables wxWidgets_FOUND, wxWidgets_LIBRARIES y wxWidgets_INCLUDE_DIR
INCLUDE_DIRECTORIES(${wxWidgets_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(MiProyecto ${wxWidgets_LIBRARIES})
Definiciones
Podemos añadir directivas del preprocesador de C++ con CMake
PROJECT(MiProyecto)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
ADD_DEFINITIONS(-DPREMIUM_SUPPORT)
# Ahora #ifdef PREMIUM_SUPPORT en el código evaluará como cierto
ADD_EXECUTABLE(MiProyecto "src/main.cpp")
Dependencias
Se pueden crear árboles de dependencias en CMake
PROJECT(MiProyecto)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
ADD_EXECUTABLE(MiProyecto "src/main.cpp")
ADD_EXECUTABLE(NecesitaMiProyecto "src/otro.cpp")
ADD_DEPENDENCY(NecesitaMiProyecto MiProyecto)
# NecesitaMiProyecto ahora depende de MiProyecto
Usando Qt
Ejemplo práctico usando CMake y Qt5 que es capaz de usar QML. Soporta archivos QRC de recursos. Requiere los plugins de Qt5.
PROJECT(ProyectoQt)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
SET(CMAKE_AUTOMOC ON)
SET(CMAKE_INCLUDE_CURRENT_DIR ON)
FILE(GLOB ProyectoQt_SRC "src/*.cpp")
FIND_PACKAGE(Qt5Core REQUIRED)
FIND_PACKAGE(Qt5Widgets REQUIRED)
FIND_PACKAGE(Qt5Qml REQUIRED)
FIND_PACKAGE(Qt5Quick REQUIRED)
qt5_add_resources(Res_SRC "src/res.qrc")
ADD_EXECUTABLE(ProyectoQt ${ProyectoQt_SRC} ${Res_SRC})
qt5_use_modules(ProyectoQt Widgets Qml Quick)
Usando Java
CMake soporta Java, aunque no maneja dependencias como Maven o Gradle.
PROJECT(ProyectoJava)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
FIND_PACKAGE(Java REQUIRED)
INCLUDE(UseJava)
SET(CMAKE_JAVA_COMPILE_FLAGS "-source" "1.6" "-target" "1.6")
FILE(GLOB JAVA_SRC "src/*.java")
SET(DEPS_JAR "deps/appengine.jar")
add_jar(ProyectoJava ${JAVA_SRC} INCLUDE_JARS ${DEPS_JAR} ENTRY_POINT "PuntoDeEntrada")
Usar C++11
A partir de CMake 3.1, podemos definir el estándar de C y C++ que vamos a usar
SET_PROPERTY(TARGET Ejecutable PROPERTY CXX_STANDARD 11) # Para C++11. Solo afecta al target Ejecutable
SET(CMAKE_CXX_STANDARD 11) # Para C++11. Afecta globalmente al proyecto.
Comandos personalizados, Doxygen
En CMake podemos crear comandos personalizados. Por ejemplo, generar documentación con Doxygen
PROJECT(Doxy)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
ADD_CUSTOM_TARGET(doxygen doxygen ${PROJECT_SOURCE_DIR}/Doxyfile DEPENDS MiProyectoEjecutable WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMENT "Generando documentación" VERBATIM )
# Ahora puedes usar "make doxygen"
# Como es un TARGET cualquiera de CMake, puedes usar ADD_DEPENDENCY
# También puedes usar el plugin FindDoxygen para más portabilidad
Archivos de configuración
En Autotools es común usar un archivo con configuraciones en tiempo de compilación. Normalmente se trata de una cabecera con soporte para plantillas. En CMake se puede hacer.
config.hpp.in
#ifndef CONFIG_HPP
#define CONFIG_HPP
#cmakedefine PREMIUM_SUPPORT
/* Si PREMIUM_SUPPORT está definido en CMakeLists.txt, se definirá aquí */
#define AUTHOR @AUTHOR@
/* Se definirá AUTHOR con el valor que tenga CMakeLists.txt de la variable AUTHOR */
#endif
PROJECT(MiProyecto)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
SET(AUTHOR "\"Adrian Arroyo Calle\"")
CONFIGURE_FILE(src/config.hpp.in src/config.hpp)
Instalar
CMake permite instalar también los programas
PROJECT(MiProyecto)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
ADD_EXECUTABLE(MiProyecto "src/main.cpp")
INSTALL(TARGETS MiProyecto DESTINATION bin/)
# Instala un ejecutable o librería en la carpeta seleccionada. Tenemos que tener en cuenta los prefijos, que son configurables en CMake.
# Si un programa en Linux suele ir en /usr/local/bin, debemos usar bin, pues /usr/local será añadido por CMake automáticamente
INSTALL(FILES ${ListaDeArchivos} DESTINATION .)
# Archivos normales
INSTALL(DIRECTORY mi-carpeta DESTINATION .)
# Copia la carpeta entera, conservando el nombre
# Se permiten expresiones regulares y wildcards
# INSTALL(DIRECTORY mi-carpeta DESTINATION . FILES_MATCHING PATTERN "*.png")
INSTALL(SCRIPT install-script.cmake)
# Un archivo de CMake que se ejecutará en la instalación
INCLUDE(InstallRequiredSystemLibraries)
# Importante si usas Windows y Visual Studio
# Y con esto se puede usar 'make install'
CPack
Pero `make install` es un poco incómodo. No se puede distribuir fácilmente. Aquí CMake presenta CPack, que genara instaladores. Yo soy reacio a usarlos pues son de mala calidad pero soporta:
- ZIP
- TAR.GZ
- TAR.BZ2
- TZ
- STGZ - Genera un script de Bash que ejecutará la descompresión y hará la instalación
- NSIS
- DragNDrop
- PackageMaker
- OSXX11
- Bundle
- Cygwin BZ2
- DEB
- RPM
CPack necesita que usemos el comando `cpack` en vez de `cmake`
PROJECT(MiProyecto)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
ADD_EXECUTABLE(MiProyecto "src/main.cpp")
INSTALL(TARGETS MiProyecto DESTINATION bin)
INCLUDE(CPack)
# Esto servirá para ZIP, TAR.GZ, TAR.BZ2, STGZ y TZ
# Para el resto deberás configurar manualmente unas cuantas variables necesarias
# http://www.cmake.org/Wiki/CMake:CPackPackageGenerators
Usando ensamblador
CMake soporta correctamente GNU ASM. Nasm requiere más trabajo.
PROJECT(gnu-asm ASM C)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
ENABLE_LANGUAGE(ASM-ATT)
FILE(GLOB ASM_SOURCES "*.asm")
FILE(GLOB C_SOURCES "*.c")
ADD_LIBRARY(asm STATIC ${ASM_SOURCES})
ADD_EXECUTABLE(gnu-asm ${C_SOURCES})
TARGET_LINK_LIBRARIES(gnu-asm asm)
Algunas variables interesantes
|CMAKE_CURRENT_SOURCE_DIR|La ruta completa a la carpeta donde se encuentra CMakeLists.txt|
|CMAKE_MODULE_PATH|Las rutas para buscar plugins de CMake|
|PROJECT_BINARY_DIR|La carpeta que se está usando para guardar los resultados de la compilación|
|CMAKE_INCLUDE_PATH|Las carpetas de búsqueda de cabeceras|
|CMAKE_VERSION|Versión de CMake|
|CMAKE_SYSTEM|El nombre del sistema|
|CMAKE_SYSTEM_NAME|El sistema operativo|
|CMAKE_SYSTEM_PROCESSOR|El procesador|
|CMAKE_GENERATOR|El generador usado en ese momento|
|UNIX|Si estamos en Linux, OS X, BSD o Solaris será cierto|
|WIN32|Si estamos en Windows|
|APPLE|En OS X|
|MINGW| Usando MinGW|
|MSYS| Usando MSYS|
|BORLAND| Usando Borland|
|CYGWIN| Usando Cygwin|
|WATCOM| Usando OpenWatcom|
|MSVC| Usando Visual Studio|
|MSVC10| Usando Visual Studio 10|
|CMAKE_C_COMPILER_ID| El identificador de compilador de C|
|CMAKE_CXX_COMPILER_ID| El identificador de compilador de C++|
|CMAKE_COMPILER_IS_GNUCC| El compilador de C es una variante de GNU GCC|
|CMAKE_COMPILER_IS_GNUCXX| El compilador de C++ es una variante de GNU G++|
|CMAKE_BUILD_TYPE| La configuración Debug/Release que estamos usando|
|CMAKE_C_COMPILER| La ruta al compilador de C|
|CMAKE_C_FLAGS| La configuración del compilador de C|
|CMAKE_C_FLAGS_DEBUG| La configuración del compilador de C solo si estamos en la configuración Debug|
|CMAKE_C_FLAGS_RELEASE| La configuración del compilador de C solo si estamos en la configuración Release|
|CMAKE_SHARED_LINKER_FLAGS| La configuración del compilador para librerías compartidas|
|BUILD_SHARED_LIBS| Por defecto en ADD_LIBRARY, las librerías son compartidas. Podemos cambiar esto|
Muchas más en la wiki de CMake
RPath
El RPath es importante en los sistemas UNIX. Se trata de cargar librerías dinámicas que no están en directorios estándar.
SET(CMAKE_SKIP_BUILD_RPATH FALSE)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
SET(CMAKE_INSTALL_RPATH "$ORIGIN")
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
Esto hará que los ejecutables construidos en UNIX puedan cargar librerías desde la carpeta donde se encuentran. Al estilo Windows.