c++ - ¿Cómo crear una biblioteca compartida con cmake?
compilation shared-libraries (4)
He escrito una biblioteca que utilicé para compilar utilizando un Makefile autocomprimido, 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 intento hacer es compilar el código fuente en una biblioteca compartida y luego instalarlo con los archivos de encabezado.
La mayoría de los ejemplos que he encontrado compilan ejecutables con algunas bibliotecas compartidas, pero nunca solo una biblioteca simple compartida. También sería útil si alguien pudiera decirme una biblioteca muy simple que usa cmake, así que puedo usar esto como ejemplo.
Este archivo minimal CMakeLists.txt 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.
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})
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 echando un vistazo a esto, esta es mi forma favorita de hacerlo gracias al CMake moderno:
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)
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 Buscar *** .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 es necesario 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.
Siempre especifique la versión mínima requerida de cmake
cmake_minimum_required(VERSION 3.9)
Deberías declarar un proyecto. cmake
dice que es obligatorio y definirá las variables convenientes PROJECT_NAME
, PROJECT_VERSION
y PROJECT_DESCRIPTION
(esta última variable requiere cmake 3.9):
project(mylib VERSION 1.0.1 DESCRIPTION "mylib description")
Declarar un nuevo objetivo de biblioteca. Evite el uso de file(GLOB ...)
. Esta característica no proporciona el dominio asistido del proceso de compilación. Si es flojo, 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 establecer SOVERSION
en el número principal 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 su árbol de proyecto (como colocarlo include/
directory). Tenga en cuenta que los encabezados privados no deberían estar instalados y le sugiero que los coloque con los archivos de fuentes.
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"
. Por lo tanto, 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 declare los 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 le permiten a una aplicación de terceros importar fácilmente su biblioteca:
- con Makefile, ver
pkg-config
- con Autotools, vea
PKG_CHECK_MODULES
- con cmake, consulte
pkg_check_modules
Cree un archivo de plantilla llamado mylib.pc.in
(consulte la página de mylib.pc.in
pc (5) para obtener más información):
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
Name: @PROJECT_NAME@
Description: @PROJECT_DESCRIPTION@
Version: @PROJECT_VERSION@
Requires:
Libs: -L${libdir} -lmylib
Cflags: -I${includedir}
En su CMakeLists.txt
, agregue una regla para expandir @
macros ( @ONLY
pida cmake para no expandir variables del formulario ${VAR}
):
configure_file(mylib.pc.in mylib.pc @ONLY)
Y finalmente, instale el archivo generado:
install(FILES ${CMAKE_BINARY_DIR}/mylib.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)
También puede usar la función cmake EXPORT
. Sin embargo, esta característica solo es compatible con cmake
y me resulta difícil de usar.
Finalmente, todo el CMakeLists.txt
debería CMakeLists.txt
así:
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)