it-swarm-es.com

¿Cómo crear una biblioteca compartida con cmake?

He escrito una biblioteca que solía compilar usando un Makefile auto-escrito, pero ahora quiero cambiar a cmake. El árbol se ve así (eliminé todos los archivos irrelevantes):

.
├── include
│   ├── animation.h
│   ├── buffers.h
│   ├── ...
│   ├── vertex.h
│   └── world.h
└── src
    ├── animation.cpp
    ├── buffers.cpp
    ├── ...
    ├── vertex.cpp
    └── world.cpp

Entonces, lo que estoy tratando de hacer es simplemente compilar la fuente en una biblioteca compartida y luego instalarla con los archivos de encabezado.

La mayoría de los ejemplos que he encontrado compilan archivos ejecutables con algunas bibliotecas compartidas pero nunca solo una biblioteca compartida simple. También sería útil que alguien me dijera una biblioteca muy simple que usa cmake, de modo que pueda usar esto como ejemplo.

98
Florian M

Especifique siempre la versión mínima requerida de cmake

cmake_minimum_required(VERSION 3.9)

Debes declarar un proyecto. cmake dice que es obligatorio y definirá las variables convenientes PROJECT_NAME, PROJECT_VERSION y PROJECT_DESCRIPTION (esta última variable necesita cmake 3.9):

project(mylib VERSION 1.0.1 DESCRIPTION "mylib description")

Declara un nuevo objetivo de la biblioteca. Por favor, evite el uso de file(GLOB ...). Esta característica no proporciona un dominio asistido del proceso de compilación. Si es perezoso, copie y pegue la salida de ls -1 sources/*.cpp:

add_library(mylib SHARED
    sources/animation.cpp
    sources/buffers.cpp
    [...]
)

Establezca la propiedad VERSION (opcional pero es una buena práctica):

set_target_properties(mylib PROPERTIES VERSION ${PROJECT_VERSION})

También puede configurar SOVERSION al número mayor de VERSION. Entonces libmylib.so.1 será un enlace simbólico a libmylib.so.1.0.0.

set_target_properties(mylib PROPERTIES SOVERSION 1)

Declara la API pública de tu biblioteca. Esta API se instalará para aplicaciones de terceros. Es una buena práctica aislarlo en el árbol de su proyecto (como colocarlo en el directorio include/). Tenga en cuenta que, los encabezados privados no deben instalarse y sugiero encarecidamente colocarlos con archivos de origen.

set_target_properties(mylib PROPERTIES PUBLIC_HEADER include/mylib.h)

Si trabaja con subdirectorios, no es muy conveniente incluir una ruta relativa como "../include/mylib.h". Entonces, pase el directorio superior en los directorios incluidos:

target_include_directories(mylib PRIVATE .)

o

target_include_directories(mylib PRIVATE include)
target_include_directories(mylib PRIVATE src)

Crea una regla de instalación para tu biblioteca. Sugiero usar las variables CMAKE_INSTALL_*DIR definidas en GNUInstallDirs:

include(GNUInstallDirs)

Y declara archivos para instalar:

install(TARGETS mylib
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

También puede exportar un archivo pkg-config. Estos archivos permiten que una aplicación de terceros importe fácilmente su biblioteca:

Cree un archivo de plantilla llamado mylib.pc.in (vea pc (5) manpage para más información):

[email protected][email protected]
[email protected][email protected]
libdir=${exec_prefix}/@[email protected]
includedir=${prefix}/@CMAKE_INSTALL_INCLUDED[email protected]

Name: @[email protected]
Description: @[email protected]
Version: @[email protected]

Requires:
Libs: -L${libdir} -lmylib
Cflags: -I${includedir}

En su CMakeLists.txt, agregue una regla para expandir las macros de @ (@ONLY pida a cmake que no expanda las variables de la forma ${VAR}):

configure_file(mylib.pc.in mylib.pc @ONLY)

Y finalmente, instalar el archivo generado:

install(FILES ${CMAKE_BINARY_DIR}/mylib.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)

También puede usar cmake EXPORT feature . Sin embargo, esta función solo es compatible con cmake y me resulta difícil de usar.

Finalmente, todo el CMakeLists.txt debería verse como:

cmake_minimum_required(VERSION 3.9)
project(mylib VERSION 1.0.1 DESCRIPTION "mylib description")
include(GNUInstallDirs)
add_library(mylib SHARED src/mylib.c)
set_target_properties(mylib PROPERTIES
    VERSION ${PROJECT_VERSION}
    SOVERSION 1
    PUBLIC_HEADER api/mylib.h)
configure_file(mylib.pc.in mylib.pc @ONLY)
target_include_directories(mylib PRIVATE .)
install(TARGETS mylib
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES ${CMAKE_BINARY_DIR}/mylib.pc
    DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)
118
Jezz

Este archivo CMakeLists.txt mínimo compila una biblioteca compartida simple:

cmake_minimum_required(VERSION 2.8)

project (test)
set(CMAKE_BUILD_TYPE Release)

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(test SHARED src/test.cpp)

Sin embargo, no tengo experiencia en copiar archivos a un destino diferente con CMake. El comando de archivo con la firma COPY/INSTALL parece que podría ser útil.

70
Robert Franke

Estoy tratando de aprender cómo hacerlo yo mismo, y parece que puedes instalar la biblioteca de esta manera:

cmake_minimum_required(VERSION 2.4.0)

project(mycustomlib)

# Find source files
file(GLOB SOURCES src/*.cpp)

# Include header files
include_directories(include)

# Create shared library
add_library(${PROJECT_NAME} SHARED ${SOURCES})

# Install library
install(TARGETS ${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME})

# Install library headers
file(GLOB HEADERS include/*.h)
install(FILES ${HEADERS} DESTINATION include/${PROJECT_NAME})
20
gromit190

Primero, este es el diseño del directorio que estoy usando:

.
├── include
│   ├── class1.hpp
│   ├── ...
│   └── class2.hpp
└── src
    ├── class1.cpp
    ├── ...
    └── class2.cpp

Después de un par de días de ver esto, esta es mi forma favorita de hacerlo gracias a la moderna CMake:

cmake_minimum_required(VERSION 3.5)
project(mylib VERSION 1.0.0 LANGUAGES CXX)

set(DEFAULT_BUILD_TYPE "Release")

if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
  message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.")
  set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE STRING "Choose the type of build." FORCE)
  # Set the possible values of build type for cmake-gui
  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()

include(GNUInstallDirs)

set(SOURCE_FILES src/class1.cpp src/class2.cpp)

add_library(${PROJECT_NAME} ...)

target_include_directories(${PROJECT_NAME} PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
    PRIVATE src)

set_target_properties(${PROJECT_NAME} PROPERTIES
    VERSION ${PROJECT_VERSION}
    SOVERSION 1)

install(TARGETS ${PROJECT_NAME} EXPORT MyLibConfig
    ARCHIVE  DESTINATION ${CMAKE_INSTALL_LIBDIR}
    LIBRARY  DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME  DESTINATION ${CMAKE_INSTALL_BINDIR})
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})

install(EXPORT MyLibConfig DESTINATION share/MyLib/cmake)

export(TARGETS ${PROJECT_NAME} FILE MyLibConfig.cmake)

Después de ejecutar CMake e instalar la biblioteca, no es necesario usar los archivos Find ***. Cmake, se puede usar así:

find_package(MyLib REQUIRED)

#No need to perform include_directories(...)
target_link_libraries(${TARGET} mylib)

Eso es todo, si se ha instalado en un directorio estándar, se encontrará y no hay necesidad de hacer nada más. Si se ha instalado en una ruta no estándar, también es fácil, simplemente dile a CMake dónde encontrar MyLibConfig.cmake usando:

cmake -DMyLib_DIR=/non/standard/install/path ..

Espero que esto ayude a todos tanto como me ha ayudado. Las viejas formas de hacer esto eran bastante engorrosas.

8
Luis