Inicio > Programación, Tutoriales > Tutorial CMake

Tutorial CMake

Lunes, 7 de diciembre de 2009 Dejar un comentario Ir a comentarios

cmake

Última actualización: 16/09/2010

Hasta ahora he estado usando las autotools (autoconf, automake, libtool, etc.) para llevar a cabo la configuración de todos mis proyectos, y la verdad que me ha ido muy bien con estas herramientas una vez conseguí desentrelazar alguna que otra cosilla que me llevó más de un quebradero de cabeza. Pero ya sabéis, uno no se cansa de aprender cosas y ampliar conocimientos, y desde hace tiempo vengo observando que cmake recibe muy buenas críticas, sobre todo debido a la posibilidad que nos ofrece para poder configurar nuestros proyectos software para distintas plataformas (GNU/Linux, Windows, Mac OS/X, etc) y distintos compiladores (GnuC, Visual C++, Borland, MinGW, etc). En esta entrada os hablaré sobre cmake, explicaré algunas de sus principales características y os dejaré algunos ejemplos útiles.

¿Qué es CMake?

CMake es un sistema extensible y abierto que controla el proceso de construcción de manera independiente en diferentes sistemas operativos y compiladores. CMake está diseñado para ser usado en conjunto con el sistema de construcción nativo de un entorno. Se utilizan ficheros simples de configuración en cada directorio fuente (llamados CMakeLists.txt) para generar ficheros de estándar de construcción (Makefiles en Unix y proyectos en Windows MSVC) que se usan de la manera usual. CMake puede compilar código fuente, crear librerías, generar wrappers y construit ejecutables en combinaciones arbitrarias. CMake soporta construcciones in-place y out-of-place, y por lo tanto se puede realizar múltiples construcciones a partir de un único árbol fuente. CMake también soporta la construcción de librerías estáticas y dinámicas. Otra buena característica de CMake es que genera un fichero cache que es diseñado para ser usado con un editor gráfico. Por ejemplo, cuando ejecutamos CMake, este localiza los ficheros de inclusión, librerías, ejecutables y puede encontrar otras directivas de construcción opcionales. Dicha información es reunida en la cache, que puede ser cambiada por el usuario antes de que se generen los ficheros de construcción nativos.

Cmake está diseñado para soportar complejas jerarquías de directorios y aplicaciones que dependen de varias librerías. Por ejemplo, CMake soporta proyectos consistentes en multiples toolkits (es decir, librerías) donde cada toolkit puede contener varios directorios, y la aplicación depende de los toolkits además de un código adicional. CMake también puede manejar situaciones donde se deben construir ejecutables para poder generar código que es después compilado y linkado en una aplicación final.

Usar CMake es simple. El proceso de construcción es controlado por la creación de uno o más ficheros CMakeLists.txt en cada directorio (incluyendo subdirectorios) que forma un proyecto. Cada CMakeLists.txt consiste en uno o más comandos. Cada comando tiene la forma COMANDO(argumentos). CMake proporciona varios comandos predefinidos, pero si lo necesitas puedes añadir tus propios comandos. Además, el usuario avanzado puede añadir otros generadores de Makefiles para una combinación particular de compilador/S.O.

Después de haber estado unos cuantos días trabajando con CMake puedo decir que es bastante más rápido de aprender que las autotools, aunque está afirmación puede estar influenciada por mi desconocimiento en algunos estándares en la programación de proyectos que he ido adquiriendo con el tiempo. También son necesarias, en general, menos líneas de código para un mismo proyecto con autotools.

HelloWorld con CMake

Este primer ejemplo que vamos a ver lo he sacado de la siguiente dirección donde podéis encontrar un breve tutorial en pdf de CMake, eso si, en inglés. He realizado unas breves modificaciones a la estructura de directorios y al contenido de los ficheros para que se ajuste más a cómo suelo trabajar yo. La estructura de directorios con la que vamos a trabajar es la siguiente la podéis ver en la siguiente imagen:

cmake-template2

Directorio raíz

Dentro de nuestro directorio de trabajo raíz vamos a crear la carpeta src donde almacenaremos los ficheros fuente que formarán una librería a la que llamaremos Hello, y otra carpeta test donde crearemos un programa que hará uso de la librería creada. Por otro lado, necesitaremos un directorio de nombre aleatorio (en este caso build) donde se crearán los archivos necesarios para generar el proyecto, independientemente del sistema operativo y compilador que usemos.  Por último, y más importante, crearemos el archivo CMakeLists.txt el cuál describirá como se organiza el proyecto en el que estamos trabajando. El contenido de dicho fichero será el siguiente:

PROJECT(HELLO)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

ADD_SUBDIRECTORY(src)
ADD_SUBDIRECTORY(test)

El significado de los comandos que aparecen en este fichero son los siguientes:

  • PROJECT: asigna un nombre que identifica al proyecto. Ahora los ficheros del proyecto podrán referirse al directorio fuente raíz del proyecto como ${NOMBREPROYECTO_SOURCE_DIR} y al directorio binario raíz del proyecto como ${NOMBREPROYECTO_BINARY_DIR}
  • CMAKE_MINIMUM_REQUIRED: Establece una versión mínima de cmake para poder generar el proyecto. Si no se especifica puede que cmake nos muestre algún warning al intentar configurar el proyecto.
  • ADD_SUBDIRECTORY: Añade un nuevo subdirectorio a la lista de subdirectorios del proyecto independientemente del contenido de este.

Directorio src

En el directorio src vamos a incluir el código fuente de una librería que solamente va a consistir en una clase Hello compuesta por los ficheros hello.h y hello.cpp. El código de los ficheros es el siguiente:

// hello.h
#ifndef  HELLO_INC
#define  HELLO_INC

class Hello
{
   public:
   void Print();
}; // -----  end of class Hello  -----

#endif   // ----- #ifndef HELLO_INC  -----
#include	"hello.h"
#include	<iostream>

using namespace std;

void Hello:: Print()
{
   cout << "Hello, World!" << endl;
}

Por último, el contenido del fichero CMakeLists.txt que está dentro del directorio src sería simplemente:

#Añade una librería llamada Hello (libHello.a bajo linux) a partir del fichero fuente hello.cpp
ADD_LIBRARY(Hello hello)

Este comando por defecto nos creará una librería llamada Hello con el fichero que le hemos especificado. Aquí también se puede especificar si queremos que la librería sea dinámica o estática, pero ya hablaré sobre eso más adelante.

Directorio test

En el directorio test vamos a incluir el típico programa “Hola Mundo” haciendo uso de la librería Hello que hemos creado en el directorio src. El código fuente de dicho programa es el siguiente:

// test.cpp
#include	<iostream>
#include	"hello.h"

int main()
{
	Hello().Print();
	return 0;
}

Y el contenido del fichero CMakeLists.txt que está dentro del directorio test es:

#Asegurarse de que el compilador puede encontrar los ficheros de nuestra librería Hello
INCLUDE_DIRECTORIES(${HELLO_SOURCE_DIR}/src)

#Añade un binario llamado "helloWorld" que es construido del fichero fuente "test.cpp"
#La extensión se encuentra automáticamente
ADD_EXECUTABLE(helloWorld test)

#Enlaza el ejecutable con la librería Hello
TARGET_LINK_LIBRARIES(helloWorld Hello)

Construyendo el proyecto

Ya tenemos todo el código de nuestro proyecto y los ficheros CMakeLists.txt necesarios para poder construirlos mediante cmake. Normalmente para construir un proyecto crearemos una nueva carpeta (en nuestro caso la hemos llamado build) para generar una compilación del proyecto específica para un determinado S.O. (Windows, Linux, Mac), un determinado compilador (gcc, MSVC, icc, mingw, etc), o una determinada configuración (Debug, Release, etc). Esta característica es verdaderamente útil para no tener que re-generar proyectos enteros según la configuración deseada cuando hagamos pequeños cambios en el código. Yo lo que suelo hacer es crearme dos directorios: build-debug y build-release. El primero lo uso durante la fase más intensa de desarrollo, para corregir todos los posibles warning que me lance el compilador y activar el soporte de depuración para los depuradores. El segundo lo uso una vez que estoy seguro (o tengo una gran certeza) de que el código no contiene ningún error, y quiero compilar la librería con todas las optimizaciones posibles y sin soporte de depuración.

Para la generación del proyecto ingresaremos en una terminal de texto y accederemos al directorio build. Una vez allí ejecutaremos el comando:

cmake ..

Básicamente, al comando cmake tenemos que pasarle como argumento la ruta al directorio donde se almacena el proyecto. Si todo ha ido bien tendremos, cmake hará las comprobaciones oportunas y tendremos una salida parecida a la siguiente:

pipo@pipo-laptop:~/cmake-template/build$ cmake ../
-- The C compiler identification is GNU
-- The CXX compiler identification is GNU
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/pipo/cmake-template/build

Una vez hecho esto, se habrán generado una serie de directorios y archivos para que realizar la compilación e instalación del proyecto sea tan sencillo como ejecutar los siguientes comandos:

make
sudo make install
También disponemos de una interfaz gráfica para cmake mediante el comando cmake-gui. A la hora de configurar el proyecto en plataformas Windows es mucho más cómodo trabajar mediante esta interfaz para seleccionar el tipo de configuración según el compilador o IDE que usemos.

También disponemos de una interfaz gráfica para cmake mediante el comando cmake-gui. A la hora de configurar el proyecto en plataformas Windows es mucho más cómodo trabajar mediante esta interfaz para seleccionar el tipo de configuración según el compilador o IDE que usemos. Para los que no dispongan de dicha interfaz gráfica (por ejemplo los que usen debian), pueden utilizar el comando ccmake que habilita una interfaz en modo texto con la que podemos modificar los valores de las variables fácilmente desde la consola.

Opciones avanzadas

El ejemplo Hola-Mundo que os acabo de mostrar, es solo la punta del Iceberg de posibilidades que nos presenta CMake. Podemos tener ficheros de configuración tan sencillos como los que acabo de mostrar o otros más complejos donde tendremos en cuenta la portabilidad de nuestro código a diferentes sistemas operativos, compiladores, uso de programas externos, etc. A continuación os hablaré de algunas cosas más, sobre el uso básico de Cmake, además de otros problemas con los que me he ido encontrando y a los que he ido dando solución.

Cambiando las variables en la cache de cmake

Cuando ejecutamos por consola el comando

cmake ..

Le estamos diciendo a Cmake que realice la configuración estándar del proyecto que estemos compilando. Pero CMake cuenta con algunas variables y opciones  internas, además de las que definamos nosotros que podemos modificar fácilmente a la hora de configurar el proyecto. Para cambiar el valor de dichas variables u opciones tenemos que utilizar la opción -D. Por otra parte, la opción -i hará que cmake nos pregunte interactivamente por algunos ajustes a realizar. Por ejemplo, si queremos configurar nuestro proyecto para depuración ejecutaremos el siguiente comando:

cmake -D CMAKE_BUILD_TYPE=Debug ..

De todos modos, si no os sentís cómodos trabajando desde una terminal, siempre podéis modificar las variable y opciones desde la interfaz gráfica de cmake (cmake-gui).

Variables

El uso de variables es fundamental en cmake y una buena comprensión en la manipulación de estas nos facilitará la elaboración de otras arduas tareas. Quizás las características más destacables del manejo de variables con cmake sean las siguientes:

  • No necesitamos declararlas. No existe diferencia entre crear y modificar una variable.
  • Normalmente no se necesita definir su tipo.
  • El comando SET crea y modifica variables.
  • El comando SET puede hacer de todo con las variables pero LIST hace que algunas operaciones sean más sencillas (ver función APPEND).
  • El comando FILE nos permite definir listas de variables de una forma tan rápida y sencilla como la siguiente: FILE(GLOB hdrs “*.h” ). Con dicho comando asignamos a la variable hdrs todos los ficheros con extensión .h del directorio actual.
  • Además de trabajar con variables que puedan ser de distintos tipos podemos tratar con opciones que solo pueden tomar los valores ON y OFF. Por ejemplo podemos establecer la siguiente opción para determinar más adelante si tratar los warning en nuestro código como erróres.
    set(WARNINGS_ARE_ERRORS OFF CACHE BOOL "Treat warnings as errors" )
    
  • La opción definida en el punto anterior se almacena en el fichero de cache y su valor puede modificarse posteriormente modificando dicho fichero.

Cambiando los parámetros de construcción

Cmake utiliza parámetros por defecto para el preprocesador, compilador y linkador, pero estos pueden ser modificados sin demasiadas complicaciones. Podemos:

  • Modificar la configuración del preprocesador con ADD_DEFINITIONS y REMOVE_DEFINITIONS. Estos cambios se aplican tanto para C como para C++.
  • Modificar la configuración del compilador modificando las variables CMAKE_C_FLAGS y CMAKE_CXX_FLAGS.
  • Modificar la configuración del linkador mediante

Normalmente en todos los proyectos con los que trabajo siempre incluyo flags de compilación según el modo de trabajo en el que me encuentre (Release y Debug). Para ello podemos modificar las siguientes variables:

SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -Wall -Werror -W -Wno-return-type" )
SET(CMAKE_CXX_FLAGS_RELEASE "-O2 -pipe -march=core2" )

Además podemos establecer diferentes tipos de construcciones de nuestros proyectos. Las configuraciones más usuales son “Debug” y “Release”. La configuración Debug es útil cuando estamos en pleno proceso de desarrollo y queremos que el compilador nos avise de cualquier posible error o warning que pueda haber en nuestro código. Además no se incluyen optimizaciones en el código para que la depuración del mismo sea más sencilla. La configuración Release tiene una funcionalidad totalmente inversa. Es una configuración que incluye optimizaciones en el código, elimina el soporte de depuración y evita la comprobación agresiva de errores y warnings en el código, para que tanto la compilación como la ejecución del mismo sea lo más rápida posible.

Para configurar el proyecto de una forma u otra solo tenemos que modificar la variable CMAKE_BUILD_TYPE mediante el comando SET. Una forma de establecer el modo de construcción del proyecto por defecto a “Release” podría ser:

IF(NOT CMAKE_BUILD_TYPE )
   SET( CMAKE_BUILD_TYPE "Release" )
ENDIF()

De esta forma si no le indicamos a cmake lo contrario compilaremos nuestro proyecto para una versión de lanzamiento. Si queremos indicarle a cmake mediante un argumento que tipo de construcción queremos, ejecutaremos el comando de la siguiente forma:

cmake -­D CMAKE_BUILD_TYPE=Release ../
 

En cuanto a las flags de compilación, se pueden hacer cosas mucho más avanzadas como determinar el tipo de procesador que hay en el sistema y activar unas flags u otras según el mismo. Podéis echar un vistazo al CMakelists.txt que incluye la última versión de OpenCV para comprobar por vuestra cuenta el poder de CMake.

Usando pkg-config con cmake

A pesar de que en la documentación de cmake no recomiendan usar pkg-config ya que puede que no esté instalado en los equipos de los usuarios finales, es innegable que muchos proyectos software actuales en plataformas GNU/Linux, sobre todo los que están relacionados con las librerías GTK+, hacen uso de pkg-config para manejar sus dependencias. Lo cierto es que hasta ahora, todas las librerías que he manejado y desarrollado hacen uso de pkg-config, y vería imposible mi migración a cmake si este no incluyese un modo de manejar las dependencias por medio de una interfaz a pkg-config.

Para hacer uso de esta interfaz vamos a tener que incluir el módulo FindPkgConfig.cmake en nuestro proyecto y después utilizarlo mediante el comando PKG_CHECK_MODULES. Podéis encontrar una amplia documentación sobre dicho comando en el propio fichero del módulo, el cual si usáis Ubuntu se encuentra en: /usr/share/cmake-2.6/Modules/FindPkgConfig.cmake. A continuación un sencillo ejemplo de como comprobar si en nuestro sistema está instalada la librería matio.

FIND_PACKAGE(PkgConfig)	#Enable PKG-CONFIG suport
PKG_CHECK_MODULES(MATIO  matio>=1.3.3)

En este caso pido que la versión de la librería sea al menos la 1.3.3, y el prefijo que voy a usar para las variables definidas con cmake es el mismo nombre de la librería pero con mayúsculas. La función PKG_CHECK_MODULES define las siguientes variables, entre otras, en caso de que la librería se encuentre:

<PREFIX>_LIBRARIES      ... only the libraries (w/o the '-l')
<PREFIX>_LIBRARY_DIRS   ... the paths of the libraries (w/o the '-L')
<PREFIX>_LDFLAGS        ... all required linker flags
<PREFIX>_LDFLAGS_OTHER  ... all other linker flags
<PREFIX>_INCLUDE_DIRS   ... the '-I' preprocessor flags (w/o the '-I')
<PREFIX>_CFLAGS         ... all required cflags
<PREFIX>_CFLAGS_OTHER   ... the other compiler flags

Y para determinar si se ha encontrado la librería podemos usar la variable _FOUND. Por ejemplo, en el desarrollo de una librería podemos decidir si incluir ciertos ficheros o no dependiendo de que se encuentre una dependencia.

IF (MATIO_FOUND)
	SET(hdrs ${hdrs} gumatio.h)
	SET(srcs ${srcs} gumatio.cpp)
	INCLUDE_DIRECTORIES(${MATIO_INCLUDE_DIRS})
	SET(libraries ${libraries} ${MATIO_LIBRARIES})
ENDIF()

Generando documentación con Doxygen

Al igual que con las autotools, podemos hacer que mediante un simple comando “make doc” generemos la documentación con doxygen de nuestro código fuente apropiadamente documentado. Para CMake me encontré el siguiente fichero de Jan Woetzel, el cual he modificado a mi antojo definiendo algunas variables al principio de la macro para decidir si habilitar ciertas características o no:

# -helper macro to add a "doc" target with CMake build system.
# and configure doxy.config.in to doxy.config
#
# target "doc" allows building the documentation with doxygen/dot on WIN32 and Linux
# Creates .chm windows help file if MS HTML help workshop
# (available from http://msdn.microsoft.com/workshop/author/htmlhelp)
# is installed with its DLLs in PATH.
#
#
# Please note, that the tools, e.g.:
# doxygen, dot, latex, dvips, makeindex, gswin32, etc.
# must be in path.
#
# Note about Visual Studio Projects:
# MSVS hast its own path environment which may differ from the shell.
# See "Menu Tools/Options/Projects/VC++ Directories" in VS 7.1
#
# author Jan Woetzel 2004-2006
# www.mip.informatik.uni-kiel.de/~jw
#
# Modified by Luis Díaz 2009
# http://plagatux.es

MACRO(GENERATE_DOCUMENTATION DOX_CONFIG_FILE)
FIND_PACKAGE(Doxygen)
IF (DOXYGEN_FOUND)
#Define variables
SET(SRCDIR "${PROJECT_SOURCE_DIR}/src" )
SET(TAGFILE "${PROJECT_BINARY_DIR}/doc/${PROJECT_NAME}.tag" )

IF (USE_CHM AND WIN32)
SET(WIN_CHM "YES" )
SET(CHM_FILE "${PROJECT_SOURCE_DIR}/doc/help.chm" )
SET (BINARY_TOC "YES" )
SET (TOC_EXPAND "YES" )
ELSE()
SET(WIN_CHM "NO" )
SET (BINARY_TOC "NO" )
SET (TOC_EXPAND "NO" )
ENDIF()

IF (USE_LATEX)
SET(GENERATE_PDF "YES" )
SET(GENERATE_LATEX "YES" )
SET(LATEXOUT "latex" )
ELSE()
SET(GENERATE_PDF "NO" )
SET(GENERATE_LATEX "NO" )
ENDIF()

IF (NOT USE_DOT)
SET(DOXYGEN_DOT_FOUND "NO" )
ENDIF()

#click+jump in Emacs and Visual Studio (for doxy.config) (jw)
IF    (CMAKE_BUILD_TOOL MATCHES "(msdev|devenv)" )
SET(DOXY_WARN_FORMAT "\"$file($line) : $text \"" )
ELSE  (CMAKE_BUILD_TOOL MATCHES "(msdev|devenv)" )
SET(DOXY_WARN_FORMAT "\"$file:$line: $text \"" )
ENDIF (CMAKE_BUILD_TOOL MATCHES "(msdev|devenv)" )

# we need latex for doxygen because of the formulas
FIND_PACKAGE(LATEX)
IF    (NOT LATEX_COMPILER)
MESSAGE(STATUS "latex command LATEX_COMPILER not found but usually required. You will probably get warnings and user inetraction on doxy run." )
ENDIF (NOT LATEX_COMPILER)
IF    (NOT MAKEINDEX_COMPILER)
MESSAGE(STATUS "makeindex command MAKEINDEX_COMPILER not found but usually required." )
ENDIF (NOT MAKEINDEX_COMPILER)
IF    (NOT DVIPS_CONVERTER)
MESSAGE(STATUS "dvips command DVIPS_CONVERTER not found but usually required." )
ENDIF (NOT DVIPS_CONVERTER)

# Check config file
IF   (EXISTS "${DOX_CONFIG_FILE}" )
CONFIGURE_FILE(${DOX_CONFIG_FILE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.config @ONLY ) #OUT-OF-PLACE LOCATION
SET(DOXY_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/doxy.config" )
ELSE ()
MESSAGE(SEND_ERROR "Please create configuration file for doxygen in ${CMAKE_CURRENT_SOURCE_DIR}" )
ENDIF()

# Add target
ADD_CUSTOM_TARGET(doc ${DOXYGEN_EXECUTABLE} ${DOXY_CONFIG})

IF (WIN32 AND GENERATE_WIN_CHM STREQUAL "YES" )
FIND_PACKAGE(HTMLHelp)
IF   (HTML_HELP_COMPILER)
ADD_CUSTOM_TARGET(winhelp ${HTML_HELP_COMPILER} ${HHP_FILE})
ADD_DEPENDENCIES (winhelp doc)
IF   (EXISTS ${CHM_FILE})
IF   (PROJECT_NAME)
SET(OUT "${PROJECT_NAME}" )
ELSE ()
SET(OUT "Documentation" ) # default
ENDIF()
IF   (${PROJECT_NAME}_VERSION_MAJOR)
SET(OUT "${OUT}-${${PROJECT_NAME}_VERSION_MAJOR}" )
IF   (${PROJECT_NAME}_VERSION_MINOR)
SET(OUT  "${OUT}.${${PROJECT_NAME}_VERSION_MINOR}" )
IF   (${PROJECT_NAME}_VERSION_PATCH)
SET(OUT "${OUT}.${${PROJECT_NAME}_VERSION_PATCH}" )
ENDIF()
ENDIF()
ENDIF()
SET(OUT  "${OUT}.chm" )
INSTALL(FILES ${CHM_FILE} DESTINATION "doc" RENAME "${OUT}" )
ENDIF()
ELSE()
MESSAGE(FATAL_ERROR "You have not Microsoft Help Compiler" )
ENDIF()
ENDIF ()

INSTALL(DIRECTORY "${PROJECT_BINARY_DIR}/doc/html/" DESTINATION "share/doc/lib${PROJECT_NAME}" )

ENDIF(DOXYGEN_FOUND)
ENDMACRO(GENERATE_DOCUMENTATION)

Para hacer uso de dicho fichero, además de activar o desactivar algunas opciones en el fichero principal de configuración CMake, hay que añadir las siguientes líneas, teniendo en cuenta que el fichero anterior tiene como nombre generateDoc.cmake:

OPTION(INSTALL_DOC                     "Set to OFF to skip build/install Documentation"             ON)
OPTION(USE_DOT                         "Set to ON to perform diagram generation with graphviz"     OFF)
OPTION(USE_LATEX                    "Set to ON to build latex documentation"                     OFF)
OPTION(USE_CHM                        "Set to ON to build CHM Windows documentation"                 OFF)

IF (INSTALL_DOC)
INCLUDE("${PROJECT_SOURCE_DIR}/generateDoc.cmake" )
GENERATE_DOCUMENTATION(${PROJECT_SOURCE_DIR}/lib${PROJECT_NAME}.dox.in)
ENDIF()

El fichero libPROJECT.dox.in será el fichero plantilla de configuración doxygen, donde tendremos que hacer uso de algunas de las variables definidas en el fichero generateDoc.cmake encerrando entre arrobas el nombre de dichas variables (p.e. @PROJECT_NAME@, @DOXYGEN_DOT_FOUND@, etc).

Añadir una opción “make uninstall”

Cuando añadimos los comandos INSTALL en nuestro proyecto, se generan comandos install para los makefiles pero sin embargo no se generan los comandos necesarios para desinstalar cada uno de los objetivos del proyecto. Examinando otros proyectos (En concreto el soporte para CMake en OpenCV) me encontré con el siguiente fichero que nos permite desinstalar todo lo que instale nuestro proyecto.

# -----------------------------------------------
# - cmake_uninstall.cmake.in
# File that provides "make uninstall" target
#  We use the file 'install_manifest.txt'
# -----------------------------------------------
IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" )
  MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"" )
ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" )

FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
STRING(REGEX REPLACE "\n" ";" files "${files}" )
FOREACH(file ${files})
  MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"" )
  IF(EXISTS "$ENV{DESTDIR}${file}" )
    EXEC_PROGRAM(
      "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
      OUTPUT_VARIABLE rm_out
      RETURN_VALUE rm_retval
      )
    IF(NOT "${rm_retval}" STREQUAL 0)
      MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"" )
    ENDIF(NOT "${rm_retval}" STREQUAL 0)
  ELSE(EXISTS "$ENV{DESTDIR}${file}" )
    MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist." )
  ENDIF(EXISTS "$ENV{DESTDIR}${file}" )
ENDFOREACH(file)

Incluyendo en el fichero CMakelists.txt principal las siguientes líneas:

CONFIGURE_FILE( "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
ADD_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" )

Podremos desinstalar nuestro proyecto mediante el comando make uninstall.

Dependencias con otros proyectos

Para buscar librerías tenemos varias alternativas. En el caso de que las dependencias que requerimos incluyan un fichero de configuración pkg-config podemos tratar con ellas de la forma que hemos visto anteriormente. La manera más sencilla de resolver las dependencias con otras librerías es por medio del comando FIND_PACKAGE. Dicho comando busca entre los ficheros existentes en la carpeta de módulos de cmake, para resolver la dependencia deseada. Sin embargo, de momento hay pocos ficheros de configuración de paquetes cmake. Imaginemos que queremos indicar en nuestro proyecto que dependemos obligatoriamente de los paquetes JPEG y ZLIB. Bastaría con incluir estas líneas:

FIND_PACKAGE(JPEG REQUIRED)
FIND_PACKAGE(ZLIB REQUIRED)

La opción REQUIRED hace que se lance un error en caso de que no se encuentre el paquete. Si no se especifica dicha opción simplemente aparecerá un mensaje de aviso. Podemos ver en los ficheros que residen en /usr/share/cmake-2.6/Modules/ con la sintaxis FindPACKAGE.cmake que variables se definen en cada paquete. Casi siempre se definen las siguientes:

PACKAGE_INCLUDE_DIR -> Indica donde encontrar las cabeceras de la librería.
PACKAGE_LIBRARIES -> Indica que flags se le deben pasar al linkador.
PACKAGE_FOUND -> Indica si se ha encontrado la librería

Pero como dije anteriormente el comando FIND_PACKAGE solo trata con algunas librerías. Imaginemos que queremos comprobar si contamos en nuestro sistema con la librería pthread. No hay un módulo de cmake que se encargue de esta labor, por lo que tendremos que usar el comando para buscar librerías FIND_LIBRARY. Dicho comando busca en las rutas por defecto de librerías en el sistema (/usr/lib y /usr/local/lib) los nombres que se le indiquen. Para el caso de pthread podríamos utilizar las siguientes líneas:

FIND_LIBRARY(PTHREAD    NAMES pthread)
IF 	(NOT PTHREAD)
	MESSAGE(FATAL_ERROR "Could not find pthread library" )
ENDIF 	()

En la variable PTHREAD se definirá la ruta o flag de enlace que se usará en el linkador. En caso de que no se encuentre la librería, no se establecerá ningún valor a la variable por lo que podremos comprobar fácilmente si se ha encontrado o no la librería. En caso de que intentemos configurar nuestro proyecto en Windows las cosas se complican un poco. En mi caso, el comando FIND_PACKAGE(JPEG REQUIRED) no consiguió encontrar la librería jpeg que había instalado previamente en mi sistema. Por lo tanto lo que hago es lo siguiente, aunque seguro que existe una solución mejor.

ELSEIF	(WIN32)
	SET(GNULIBS_PATH ${PROJECT_SOURCE_DIR}/libraries)
	FIND_LIBRARY(JPEG	NAMES jpeg	 PATHS ${GNULIBS_PATH}/lib)
	IF	(NOT JPEG)
		MESSAGE(FATAL_ERROR "Could not find jpeg library" )
	ENDIF	()
	SET(GNULIBS_INCLUDE_DIR ${GNULIBS_PATH}/include)
ENDIF	()

Creo un directorio libraries, donde coloco las librerías de terceros (en este caso jpeg). Busco en el sub-directorio libs de dicho directorio si se encuentra la librería que busco y por último defino el directorio donde se encuentran las cabeceras para incluirlas posteriormente donde haga falta.

Generar fichero de configuración de proyecto “FindPACKAGE.cmake”

Como ya hemos visto antes, para buscar paquetes populares tenemos el comando FIND_PACKAGE. CMake instala por defecto en las distribuciones GNU/Linux una serie de ficheros en la ruta /usr/share/cmake-2.6/Modules/ con la sintaxis FindPACKAGE.cmake que son los que se encargan realmente de buscar si tenemos un cierto software instalado en nuestro equipo. Si nuestro proyecto software va a tomar cierta importancia y pensamos que puede ser usado por otras personas o por futuros proyectos que realicemos en el futuro, nos interesará generar un fichero de configuración de nuestro proyecto para facilitar el manejo de dependencias con nuestro paquete. Dicho de otra manera, lo que pretendemos es generar un fichero de configuración equivalente a los ficheros con extensión pc que se manejan con pkg-config.

A la hora de crear este fichero de configuración cmake tenemos dos opciones para nombrar el fichero:

1) Que tenga la forma: FindPACKAGE.cmake para que en futuros proyectos que requieran de este paquete podamos resolver la dependencia mediante los comandos  CMAKE_MODULE_PATH y FIND_PACKAGE.

2) Que tenga la forma: PACKAGEConfig.cmake para posteriormente definir donde se encuentra instalado dicho fichero mediante el comando PACKAGE_DIR y después poder usar FIND_PACKAGE.

A mi me gusta más trabajar con la primera opción ya que parece algo más “estándar”. Para generar este archivo lo ideal es crearse una plantilla con extensión .in que use variables del proyecto. En mi caso llamo a este fichero siempre config.cmake.in y tiene un contenido similar al ejemplo que os muestro a continuación (Reemplazar PACKAGE con el nombre de vuestro proyecto):

# ===================================================================================
#  PACKAGE CMake configuration file
#
#             ** File generated automatically, do not modify **
#
#  Usage from an external project:
#    In your CMakeLists.txt, add these lines:
#
#    FIND_PACKAGE(PACKAGE REQUIRED )
#    TARGET_LINK_LIBRARIES(MY_TARGET_NAME ${PACKAGE_LIBS})
#
#    This file will define the following variables:
#      - PACKAGE_LIBS          : The list of libraries to links against.
#      - PACKAGE_LIB_DIR       : The directory where lib files are. Calling LINK_DIRECTORIES
#                                with this path is NOT needed.
#      - PACKAGE_VERSION       : The  version of this gu build. Example: "1.2.0"
#      - PACKAGE_VERSION_MAJOR : Major version part of gu_VERSION. Example: "1"
#      - PACKAGE_VERSION_MINOR : Minor version part of gu_VERSION. Example: "2"
#      - PACKAGE_VERSION_PATCH : Patch version part of gu_VERSION. Example: "0"
#
# ===================================================================================

# Extract the directory where *this* file has been installed (determined at cmake run-time)
#  This variable may or may not be used below, depending on the parsing of PACKAGEConfig.cmake
get_filename_component(THIS_PACKAGE_CONFIG_PATH "${CMAKE_CURRENT_LIST_FILE}" PATH)

# ======================================================
# Include directories to add to the user project:
# ======================================================
INCLUDE_DIRECTORIES(@CMAKE_INCLUDE_DIRS_CONFIGCMAKE@)

# ======================================================
# Link directories to add to the user project:
# ======================================================
LINK_DIRECTORIES("@CMAKE_LIB_DIRS_CONFIGCMAKE@ " )
# Provide the libs directory anyway, it may be needed in some cases.
SET(PACKAGE_LIB_DIR "@CMAKE_LIB_DIRS_CONFIGCMAKE@ " )

# ====================================================================
# Link libraries
# ====================================================================
if (CMAKE_MAJOR_VERSION GREATER 2  OR  CMAKE_MINOR_VERSION GREATER 4) # CMake>=2.6 supports the notation "debug XXd optimized XX"
	SET(PACKAGE_LIBS debug PACKAGE@PACKAGE_DLLVERSION@@PACKAGE_DEBUG_POSTFIX@ optimized PACKAGE@PACKAGE_DLLVERSION@)
else() # Old CMake:
	SET(PACKAGE_LIBS PACKAGE@PACKAGE_DLLVERSION@)
endif()

# ======================================================
#  Version variables:
# ======================================================
SET(PACKAGE_VERSION 		  @PACKAGE_VERSION@)
SET(PACKAGE_VERSION_MAJOR  @PACKAGE_VERSION_MAJOR@)
SET(PACKAGE_VERSION_MINOR  @PACKAGE_VERSION_MINOR@)
SET(PACKAGE_VERSION_PATCH  @PACKAGE_VERSION_PATCH@)

Para que se genere este fichero debemos añadir las siguientes líneas a nuestro CMakeLists.txt del directorio raíz, para incluir algunas variables de las que hacemos uso en dicho fichero y para generar el fichero FindPACKAGE.cmake a partir del mismo:

set(${PROJECT_NAME}_VERSION "1.0.0" )
string(REGEX MATCHALL "[0-9]" ${PROJECT_NAME}_VERSION_PARTS "${${PROJECT_NAME}_VERSION}" )
list(GET ${PROJECT_NAME}_VERSION_PARTS 0 ${PROJECT_NAME}_VERSION_MAJOR)
list(GET ${PROJECT_NAME}_VERSION_PARTS 1 ${PROJECT_NAME}_VERSION_MINOR)
list(GET ${PROJECT_NAME}_VERSION_PARTS 2 ${PROJECT_NAME}_VERSION_PATCH)
set(${PROJECT_NAME}_SOVERSION "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}" )

set(CMAKE_INCLUDE_DIRS_CONFIGCMAKE ${CMAKE_INSTALL_PREFIX}/include/${PROJECT_NAME})
set(CMAKE_LIB_DIRS_CONFIGCMAKE ${CMAKE_INSTALL_PREFIX}/lib CACHE PATH "Output directory for libraries" )

CONFIGURE_FILE("${PROJECT_SOURCE_DIR}/config.cmake.in" "${PROJECT_BINARY_DIR}/Find${PROJECT_NAME}.cmake" )
INSTALL(FILES
	"${PROJECT_BINARY_DIR}/Find${PROJECT_NAME}.cmake"
	DESTINATION share/cmake/ )

Por último para usar en otro proyecto el que estamos desarrollando actualmente tendremos que incluir las siguientes líneas en el fichero CMakeLists.txt:

SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} /usr/local/share/cmake/)
FIND_PACKAGE(PACKAGE REQUIRED)
IF (PACKAGE_VERSION VERSION_LESS X.X.X)
    MESSAGE(FATAL_ERROR "Unable to use version ${PACKAGE_VERSION} of PACKAGE ..." )
ENDIF ()

Por supuesto tendremos que cambiar las X por el número de versión que queramos usar, y en caso de haber instalado los ficheros en otra ruta modificar la ruta que establecemos para CMAKE_MODULE_PATH.

En caso de que escogiésemos la 2ª opción para el nombre del fichero de configuración habría que modificar ligeramente algunas de las líneas anteriores, pero no quiero extenderme demasiado con este tema.

Portabilidad Windows & Linux en nuestros proyectos

Si hay una razón de peso por la que haya migrado a cmake ha sido sin duda por las ventajas que presenta para configurar los proyectos en diferentes plataformas. Programando en GNU/Linux y usando las autotools no he tenido ningún problema este último año, pero no he visto la manera de poder configurar los mis proyectos para poder ser compilados con el compilador Visual C++. En todo caso, si que podía trabajar con MinGW + MSYS, pero la idea es poder configurar un proyecto en cualquier compilador y/o IDE sin morir en el intento.

En CMake existen algunas variables predefinidas que se activan según el sistema operativo que estemos usando y/o el compilador que vayamos a usar para compilar el proyecto (este se detecta automáticamente si hacemos uso del comando cmake o podemos especificar el compilador a usar si usamos el comando cmake-gui). Las variables que suelo utilizar son las siguientes:

  • Para distinguir entre sistemas operativos:
    • WIN32 -> Detecta si usas windows (32 o 64 bits).
    • UNIX -> Sistemas Unix o GNU/Linux.
    • APPLE -> Mac OS.
  • Para distinguir entre diferentes compiladores:
    • MSVC -> Microsoft visual c. Esta variable contiene un número de versión que distingue entre distintas versiones de visual studio.
    • CMAKE_COMPILER_IS_GNUCXX -> gcc.
    • MINGW ->  gcc para windows.

Por otra parte en nuestro código C/C++ seguramente también tendremos que escribir algunas directivas del procesador para diferenciar entre partes de código específicas para sistemas Windows y otras partes de código específicas para sistemas Unix. En gcc y mingw se definen las siguientes variables (tener cuidado con los dobles guiones bajos):

  • __GNUC__ : Detecta la versión mayor del compilador gcc. Si tenemos por ejemplo la versión 4.4.2, nos retornará el primer 4.
  • __GNUC_MINOR__ : Detecta el segundo número de la versión.
  • __GNUC_PATCHLEVEL__: Detecta la versión del parche aplicado sobre la versión de gcc.
  • linux y __linux: Detecta si estamos usando un sistema basado en GNU/Linux.
  • macintosh , __APPLE__ y __MACH__ : Detecta si estamos usando un sistema apple.
  • _WIN32 y _WIN64: Detecta si estamos usando un sistema windows. La primera variable se activa tanto si usamos un sistema de 32 como de 64 bits. La segunda variable solo lo hace en el segundo caso.

Conociendo la existencia de estas variables podremos configurar nuestros proyectos fácilmente para que sean portables a distintas plataformas y configurables desde distintos compiladores.

Referenciar recursos en el SOURCE_DIR

Cuando en nuestro código hacemos referencia a recursos tales como imágenes, sonidos o cualquier otro tipo de fichero binario tenemos que tener mucho cuidado con las rutas que especificamos a tales recursos. Esta sección la he creado en respuesta al comentario de Hook, ya que también he tenido que tratar muchas veces con casos similares.
La solución que yo aplico a estos casos (que no tiene porque ser la mejor ni mucho menos) es generar un fichero config.h donde se establezca una variable con la ruta a donde se encuentran los recursos. Para ello aplico las ideas que expliqué en su día en este post. Os pongo un ejemplo para que entendáis como aplico mi idea. En el directorio principal del proyecto creo un fichero config.h.cmake con el siguiente contenido:
/* Define to the full path to resources. */
#define  RESOURCES_PATH "${RESOURCES_PATH}"

Después dentro del CMakeLists.txt del directorio raíz del proyecto agrego las siguientes líneas:

SET(RESOURCES_PATH ${PROJECT_SOURCE_DIR}/resources)
CONFIGURE_FILE("${PROJECT_SOURCE_DIR}/config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config.h")

La primera establece el valor de la variable, y la segunda genera el correspondiente archivo config.h con la variable ya definida. Por último en el CMakeLists.txt del directorio donde tengamos nuestro código (por ejemplo src, utils, o similares) añadimos lo siguiente para que se incluya el directorio donde estamos generando el proyecto (y donde residirá el fichero config.h) al compilar :

INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR})

De este modo en nuestros programas podemos incluir el fichero config.h y usar la variable RESOURCES_PATH para localizar nuestros recursos.

Enlaces de interés

GD Star Rating
loading...
Tutorial CMake, 10.0 out of 10 based on 8 ratings
Share
  1. Hook
    Jueves, 16 de septiembre de 2010 a las 16:09 | #1
    GD Star Rating
    loading...

    Hola, he credo un CMakeList que funciona perfectamente, el problema lo tengo que en mi proyecto tengo una carpeta con imágenes para los iconos, fondo etc.. El problema lo tengo al compilar, que al realizarlo en la carpeta build no se me copia la carpeta de imágenes, es decir que el ejecutable no encuentra las imágenes. Espero haberme explicado correctamente.
    Sabrías decirme como relacionar esa carpeta en Cmake para que me la copie al compilar.
    Saludos

  2. Jueves, 16 de septiembre de 2010 a las 18:06 | #2
    GD Star Rating
    loading...

    Hola Hook. He actualizado la entrada para responderte. La solución la tienes en la última sección ;). Si tienes alguna duda al respecto solo tienes que preguntar.

    Saludos!

  3. Hook
    Viernes, 17 de septiembre de 2010 a las 18:43 | #3
    GD Star Rating
    loading...

    Bueno ya puestos a preguntar, ahora si realizo un make install se me instala todo en /usr/local, librería y ejecutables me refiero, dudas que me surgen. Si quisiera crear un icono y en enlace en el menú (en Ubuntu mi caso) al ejecutable puedo realizarlo desde cmake??
    Básicamente tengo un programa echo y quería poder compilarlo e instalarlo, y poder acceder a el directamente desde el menú y no en forma de comando.

  4. Hook
    Viernes, 17 de septiembre de 2010 a las 18:45 | #4
    GD Star Rating
    loading...

    Bueno ya puestos a preguntar, ahora si realizo un make install se me instala todo en /usr/local, librería y ejecutables me refiero, dudas que me surgen. Si quisiera crear un icono y en enlace en el menú (en Ubuntu mi caso) al ejecutable puedo realizarlo desde cmake??
    Básicamente tengo un programa echo y quería poder compilarlo e instalarlo, y poder acceder a el directamente desde el menú y no en forma de comando.
    PD. Cualquier manual o enlace que tenga algo al respecto me vendría de perlas

  5. Viernes, 17 de septiembre de 2010 a las 18:47 | #5
    GD Star Rating
    loading...

    @Hook
    Pues de eso ya si que no tengo ni idea Hook. Lo único que se es que eso está relacionado con ficheros de configuración de Gnome que tienen que instalarse en alguna carpeta en concreto, pero no recuerdo nada más. Por cierto, dices que se te instala todo en /usr/local … pero todo directamente ahí o las librerías en /usr/local/lib y los programas en /usr/local/bin ? Debes tener cuidado con eso y colocar cada cosa en su sitio.

    Saludos.

  6. Hook
    Martes, 21 de septiembre de 2010 a las 19:05 | #6
    GD Star Rating
    loading...

    Buenas ya estoy aquí de nuevo a ver si me puedes sacar unas dudas. Que no quiero irme al Makefile sin a verlo intentado con el CMake
    1. Trabajo con las librerías POSIX para crear una conexion serial, funciona correctamente compilando con Code::Blocks, pero al realizarlo con Cmake no se establece la conexión, es como si le faltara permisos o algo raro. ( A todo esto no he tenido problemas de compilacion ni nada por el estilo con CMake).
    2. A lo mejor tambien esta relacionado con el 1. Tambien trabajo con la libreria OpenCV para acceder a la CamWeb, sorpresa mia que no funciona en el ejecutable. Pero probe llamar al ejecutable en modo super root (sudo) y funciona correctamente (el 1 sigue fallando)
    En conclusión siempre tendré que ejecutar de este modo o hay alguna opción con la que pueda poner en el CMake los permisos para accesos a todos??
    Y bueno el fallo de la conexión serial que no se realmente cual es la solución

  7. Martes, 21 de septiembre de 2010 a las 19:34 | #7
    GD Star Rating
    loading...

    Empezaré con la 2ª pregunta.
    Si has probado a ejecutar con sudo y te funciona ya te has respondido a ti mismo :), es un problema de permisos. Normalmente los dispositivos de vídeo aparecen en /dev/ como videoX donde X es un identificador. Por ejemplo mi webcam tiene estos permisos:

    $ ls /dev/video0 -lh
    crw-rw—-+ 1 root video 81, 0 2010-09-21 20:23 /dev/video0

    Por lo que solo puede acceder a ella el superusuario y los usuarios que pertenezcan al grupo vídeo. Probablemente tu cámara tiene los mismos permisos, por lo que bastará con añadirte al grupo vídeo para poder acceder a ella con tu usuario normal.

    Hablemos ahora sobre la primera cuestión:
    Code::Blocks es una IDE no un compilador. Nunca he trabajado con esta IDE, pero por lo que veo en la página web puede trabajar con los compiladores GCC (MingW / GNU GCC) y MSVC++ entre otros.
    CMake de hecho tampoco es un compilador, sino un sistema para construir proyectos que también interacciona con muchísimos compiladores (por no decir casi todos :P).
    Si dices que el programa te funciona al compilar desde Code::Blocks y con CMake no … probablemente es que la compilación final que se esté realizando no sea idéntica. Me huele a mi que a lo mejor se te está olvidando linkar con alguna librería, aunque en este caso lo más normal es que te diese algún error de compilación. Cuando configuras el proyecto con CMake y posteriormente compilas con make, si pones :

    make VERBOSE=1

    puedes ver las líneas exactas de compilación para ver si te da alguna pista.

    También en estos casos lo mejor es acudir a un depurador y ver exactamente que es lo que está ocurriendo. Yo uso Nemiver en gnome que me va de maravilla, aunque si usas Code::Block este ya contiene un depurador propio.

    Espero que te sean de ayuda estos comentarios, sobre el primer problema no puedo ayudarte más porque no he trabajado nunca con las POSIX en profundidad.

    Saludos!

  8. EliasM
    Miércoles, 3 de octubre de 2012 a las 03:38 | #8
    GD Star Rating
    loading...

    Hola, Verás tengo una duda sobre el Programa, quisiera saber si es recomendable usar el Cmake para aplicaciones 3D, Verás estoy creando un Motor Gráfico 3D con OpenGL y OpenCL, Quisiera que sea MultiPlataforma, que pueda correr en Windows,Unix,Linux,FreeBSD,BSD,etc o al menos casi en todos. Y estoy buscando un programa como Cmake, Que he visto que es el más Multiplataforma que hay, Además quisiera saber si es posible con el Cmake agregar nuevos lenguajes de Programación con Cmake como Lua,Javascript,etc para lenguaje Script. O me recomiendas otra manera de hacer Multiplataforma mi Motor,
    Otra cosa también estoy con un grupo creando nuevos software para crear Instaladores como los más conocidos InstallShield,Inno Setup,NSIS y que sea Multiplataforma casi en que pueda ser ejecutado en todo sistema de PC quisiera que corra como mi Motor 3D también me recomiendas usar Cmake?. y por último sobre los tutoriales para aprender más sobre este gran programa.
    Por Favor espero tu respuesta.

  9. Miércoles, 3 de octubre de 2012 a las 06:38 | #9
    GD Star Rating
    loading...

    Hola @EliasM , efectivamente con CMake podrás configurar proyectos que dependa de casi cualquier librería (cómo OpenGL y OpenCL) y que la configuración te sirva para cualquier plataforma (aunque esto requerirá más trabajo por tu parte). En cuanto a lo de los lenguajes de scripting como Lua y Javascript no estoy muy seguro de si los soporta nativamente … pero siempre puedes hacer llamadas a comandos específicos desde CMake, y no supone mucho problema.

    En cuanto a los instaladores también se puede gestionar con CMake, con un módulo llamado CPack. Para Linux se que empaqueta en tar.bz2, deb y rpm. Para Windows estoy seguro de que lo hacía al menos con NSIS, pero no se hasta que punto … ya que no suelo desarrollar para windows.

    En cuanto tutoriales … CMake es tan complejo que lo mejor es que vayas aprendiendo según las necesidades. Yo te aconsejaría que te descargases la librería OpenCV (http://opencv.org/), y le echases un vistazo a como hacen ellos las cosas con CMake … así fue como aprendí a usarlo yo :)

  10. EliasM
    Miércoles, 3 de octubre de 2012 a las 13:53 | #10
    GD Star Rating
    loading...

    Muchísimas Gracias, piponazo gracias por los consejos y la ayuda.
    Sobre Instaladores estoy tratando de crear un nuevo tipo de instalador para que una persona cree su propio instalador sin importar el sistema de PC que tenga algo que para mí esta faltando bastante principalmente a gente que trabaja con varios sistemas operativos.

  11. Jueves, 21 de agosto de 2014 a las 19:45 | #11
    GD Star Rating
    loading...

    So if you know what suits you and makes you feel and look comfortable, you have cracked the code for looking
    stylish. Baby Phat tees were launched in 1998 and quickly grew
    to become hot favorites of the celebrities. To deal with these fashion difficulties you look
    at a celebrity fashions.

  12. Sábado, 23 de agosto de 2014 a las 03:25 | #12
    GD Star Rating
    loading...

    The surgeon will recommend the style that is medically proper for your condition. The Kegelmaster is a device which is especially designed to increase the
    strength of your pelvic muscle which is done by
    providing resistance. I always do pelvic floor exercises when I’m sat at my desk.

  13. Sábado, 23 de agosto de 2014 a las 16:13 | #13
    GD Star Rating
    loading...

    There are also several mundane electronic items that should be added to a college dorm shopping list to make a dorm feel more like
    home. This is to slough off any dead or excess skin before applying the lotion. Every
    bride wants to look their radiant best on their wedding day.

  14. Jueves, 4 de septiembre de 2014 a las 21:22 | #14
    GD Star Rating
    loading...

    Another useful outlet is to join a forum where you can ask question and gather information on certain RV from those that already own the particular motorhome.
    The old art of stealth in carp fishing seems to be
    as dead as the dodo on most carp waters today and it seems to
    me that the average carp angler is so manically driven to get in a swim
    set up and cast out they have no idea it is far better to think like the fish and not an angler first and truly give carp the respect
    they deserve that will guarantee to give them the catches their strive
    so manically for. Of course, lots of methods have evolved to try to avoid spooking fish out of
    your swim or even stop them from stopping feeding in the possible case of some line shy fish,
    and having them leave your swim.

  15. Lunes, 15 de septiembre de 2014 a las 12:33 | #15
    GD Star Rating
    loading...

    You may use a simple drain snake to reach into your pipe.
    Generally, two types of storage facilities available for you, which are self storage
    and mobile storage. You can easily find condos,
    so there is no need to stay in hotels and lose
    your luxury beach experience. This is to indicate all kinds of defects regardless of how simple or complex they are; way before the defects result into serious problem.
    In order to over come this, a telescope in space
    was necessary. This 30 day timeframe can be both good and bad for
    a seller. As we all knows that engine is a powerful machine infect we
    can say that our vehicle is nothing without engine so engine
    play an important role in our vehicle. The services that clients can hire are:
    packing, loading, unloading, rearrangement, unpacking, transportation, storage, insurance,
    warehouses, etc. The World is a small place now and they can keep in touch with old friends
    should they choose to. Time zones, office locations, will no longer
    be a deciding factor in the claims handling process.

  1. Domingo, 10 de enero de 2010 a las 17:31 | #1
  2. Miércoles, 15 de agosto de 2012 a las 21:48 | #2
  3. Martes, 11 de diciembre de 2012 a las 00:41 | #3