J'ai écrit une bibliothèque que j'avais l'habitude de compiler en utilisant un Makefile auto-écrit, mais je souhaite maintenant passer à cmake. L'arbre ressemble à ceci (j'ai supprimé tous les fichiers non pertinents):
.
├── include
│ ├── animation.h
│ ├── buffers.h
│ ├── ...
│ ├── vertex.h
│ └── world.h
└── src
├── animation.cpp
├── buffers.cpp
├── ...
├── vertex.cpp
└── world.cpp
Donc, ce que j'essaie de faire est simplement de compiler le source dans une bibliothèque partagée, puis de l'installer avec les fichiers d'en-tête.
La plupart des exemples que j'ai trouvés compilent des exécutables avec certaines bibliothèques partagées, mais jamais avec une simple bibliothèque partagée. Il serait également utile que quelqu'un me dise simplement une bibliothèque très simple qui utilise cmake afin que je puisse utiliser ceci à titre d'exemple.
Toujours spécifier la version minimale requise de cmake
_cmake_minimum_required(VERSION 3.9)
_
Vous devriez déclarer un projet. cmake
indique qu'il est obligatoire et qu'il définira des variables pratiques _PROJECT_NAME
_, _PROJECT_VERSION
_ et _PROJECT_DESCRIPTION
_ (cette dernière variable nécessite cmake 3.9):
_project(mylib VERSION 1.0.1 DESCRIPTION "mylib description")
_
Déclarez une nouvelle cible de bibliothèque. Veuillez éviter d'utiliser file(GLOB ...)
. Cette fonctionnalité ne permet pas de maîtriser le processus de compilation. Si vous êtes paresseux, copiez-coller la sortie de _ls -1 sources/*.cpp
_:
_add_library(mylib SHARED
sources/animation.cpp
sources/buffers.cpp
[...]
)
_
Définissez la propriété VERSION
(facultatif mais c'est une bonne pratique):
_set_target_properties(mylib PROPERTIES VERSION ${PROJECT_VERSION})
_
Vous pouvez également définir SOVERSION
sur un nombre majeur de VERSION
. Donc _libmylib.so.1
_ sera un lien symbolique vers _libmylib.so.1.0.0
_.
_set_target_properties(mylib PROPERTIES SOVERSION 1)
_
Déclarez l'API publique de votre bibliothèque. Cette API sera installée pour une application tierce. Il est recommandé de l'isoler dans votre arborescence de projet (par exemple, en plaçant le répertoire _include/
_). Notez que les en-têtes privés ne doivent pas être installés et je suggère fortement de les placer avec les fichiers source.
_set_target_properties(mylib PROPERTIES PUBLIC_HEADER include/mylib.h)
_
Si vous travaillez avec des sous-répertoires, il n’est pas très pratique d’inclure un chemin relatif tel que _"../include/mylib.h"
_. Donc, passez le répertoire principal dans les répertoires inclus:
_target_include_directories(mylib PRIVATE .)
_
ou
_target_include_directories(mylib PRIVATE include)
target_include_directories(mylib PRIVATE src)
_
Créer une règle d'installation pour votre bibliothèque. Je suggère d'utiliser les variables _CMAKE_INSTALL_*DIR
_ définies dans GNUInstallDirs
:
_include(GNUInstallDirs)
_
Et déclarez les fichiers à installer:
_install(TARGETS mylib
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
_
Vous pouvez également exporter un fichier _pkg-config
_. Ce fichier permet à une application tierce d'importer facilement votre bibliothèque:
pkg-config
PKG_CHECK_MODULES
pkg_check_modules
Créez un fichier modèle nommé _mylib.pc.in
_ (consultez la page de manuel pc (5) ):
_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}
_
Dans votre _CMakeLists.txt
_, ajoutez une règle pour développer les _@
_ macros (_@ONLY
_ demander à cmake de ne pas développer les variables de la forme _${VAR}
_):
_configure_file(mylib.pc.in mylib.pc @ONLY)
_
Et enfin, installez le fichier généré:
_install(FILES ${CMAKE_BINARY_DIR}/mylib.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)
_
Vous pouvez aussi utiliser cmake EXPORT
feature . Cependant, cette fonctionnalité est uniquement compatible avec cmake
et je trouve cela difficile à utiliser.
Enfin, l'intégralité de _CMakeLists.txt
_ devrait ressembler à ceci:
_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)
_
Ce fichier CMakeLists.txt minimal compile une bibliothèque partagée 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)
Cependant, je n'ai aucune expérience de la copie de fichiers vers une autre destination avec CMake. La commande de fichier avec la signature COPY/INSTALL semble utile.
J'essaie d'apprendre à le faire moi-même, et il semble que vous puissiez installer la bibliothèque comme ceci:
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})
Premièrement, voici la structure de répertoire que j'utilise:
.
├── include
│ ├── class1.hpp
│ ├── ...
│ └── class2.hpp
└── src
├── class1.cpp
├── ...
└── class2.cpp
Après quelques jours passés à regarder cela, voici ma façon préférée de le faire grâce à CMake moderne:
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)
Après avoir exécuté CMake et installé la bibliothèque, il n’est pas nécessaire d’utiliser les fichiers Find ***. Cmake, il peut être utilisé comme ceci:
find_package(MyLib REQUIRED)
#No need to perform include_directories(...)
target_link_libraries(${TARGET} mylib)
C'est ça, s'il a été installé dans un répertoire standard, il sera trouvé et il n'y a pas besoin de faire autre chose. S'il a été installé dans un chemin non standard, rien de plus simple, il suffit d'indiquer à CMake où trouver MyLibConfig.cmake à l'aide de:
cmake -DMyLib_DIR=/non/standard/install/path ..
J'espère que cela aide tout le monde autant que cela m'a aidé. Les vieilles façons de faire étaient assez lourdes.