CMake: Construir una librería estática y/o dinámica

Arco

Esta receta explica cómo construir librerías estáticas y dinámicas con CMake.

Software necesario

Los ejemplos que vamos a utilizar puedes descargarlos utilizando subversion desde:

$ svn co https://arco.esi.uclm.es/svn/public/samples/cmake/libstatic
$ svn co https://arco.esi.uclm.es/svn/public/samples/cmake/libshared

Los directorios contienen una librería con una definición de la clase Dummy (dummy.h y dummy.cpp) con un único método. También se incluye el CMakeLists.txt correspondiente.

Librería estática

Para crear una librería estática nada más fácil que lo que sigue:

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
 
PROJECT(libstatic)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
 
ADD_LIBRARY(dummy STATIC dummy.cpp)

El comando INCLUDE_DIRECTORIES permite añadir los directorios donde se encuentran las cabeceras del proyecto. En este caso, el fichero dummy.h se encuentra en el mismo directorio donde están los fuentes (el mismo en el que se encuentra CMakeLists.txt). La variable global CMAKE_CURRENT_SOURCE_DIR tiene esta ruta.

De una forma similar a como se añadían ejecutables, para crear una librería se debe utilizar ADD_LIBRARY que acepta, entre muchos otros, los siguientes parámetros:

  • objetivo: se trata un nombre simbólico para el objetivo que representa el crear la librería. Si no se especifica otra cosa, este nombre se utilizará para crear el fichero final de la forma libobjetivo.a.
  • STATIC: modo de la librería.
  • dependencias: de qué ficheros u objetivos depende la librería para su construcción

Librería dinámica

A la vista de lo anterior, parece sencillo crear una librería dinámica. ¿Bastará cambiar STATIC por SHARED?. Sí, con eso bastaría. Pero vamos a añadir algo más para hacer que la librería sea más "distribuible":

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
 
PROJECT(libshared)
 
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
 
ADD_LIBRARY(dummy SHARED dummy.cpp)
SET_PROPERTY(TARGET dummy PROPERTY SOVERSION 1.0)

En CMake, los targets (objetivos) tienen propiedades que, a priori, tienen ciertos valores por defecto. La propiedad SOVERSION de los objetivos que son librerías dinámicas, por defecto, no tiene valor. Sin embargo, es conveniente versionar las librerías para que aquellas plataformas que lo soporten (como por ejemplo, GNU) puedan gestionar las versiones.

Con el comando SET_PROPERTY se puede modificar las propiedades de las estructuras que se van creando en el programa. En este caso, para el objetivo 'dummy' se cambia el valor de la propiedad SOVERSION a 1.0. De esta forma, al compilar la librería CMake generará los enlaces simbólicos necesarios de forma automática.

Librería estática y dinámica

Es posible que para una misma librería quieras que pueda ser enlazada de forma estática y también dinámica. Veamos una posible solución:

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
 
PROJECT(libstaticshared)
 
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
 
ADD_LIBRARY(dummy_shared SHARED dummy.cpp)
SET_PROPERTY(TARGET dummy_shared PROPERTY SOVERSION 1.0)
 
ADD_LIBRARY(dummy_static STATIC dummy.cpp)
 
SET_TARGET_PROPERTIES(dummy_shared dummy_static PROPERTIES OUTPUT_NAME dummy)

No se puede utilizar dummy como objetivos de ambas librerías. Lo que se puede hacer, por tanto, es crear un objetivo por cada tipo de librería (dummy_shared y dummy_static). Si no decimos nada más, CMake nos generará los archivos libdummy_static.a, libdummy_shared.so,... Y eso, obviamente, no es lo deseable.

Por ello utilizamos SET_TARGET_PROPERTIES que sirve para configurar propiedades de objetivos (es un caso particular de SET_PROPERTY).

Referencias

  • Documentación CMake2.8: consultar las propiedades genéricas y de los diferentes tipos abstractos (directorios, objetivos, ficheros fuente...).