Як створити спільну бібліотеку за допомогою cmake?


141

Я написав бібліотеку, яку я збирав за допомогою самостійно написаного Makefile, але тепер я хочу перейти на cmake. Дерево виглядає так (я видалив усі неактуальні файли):

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

Тож те, що я намагаюся зробити, це просто зібрати джерело в спільну бібліотеку і потім встановити його з файлами заголовка.

Більшість прикладів, що я знайшов компільовані виконувані файли з деякими спільними бібліотеками, але ніколи не просто звичайною спільною бібліотекою. Було б також корисно, якби хтось міг просто сказати мені дуже просту бібліотеку, яка використовує cmake, тому я можу використовувати це як приклад.



Це ж питання, але чи є спосіб підтримувати мої джерела змішаними (.h ans .cpp у тому самому каталозі джерел), але дозволити Cmake виробляти каталог, що включає, продукт своєї роботи?
Сандберг

Відповіді:


204

Завжди вказуйте мінімально необхідну версію cmake

cmake_minimum_required(VERSION 3.9)

Вам слід заявити про проект. cmakeговорить, що це обов'язково, і він визначатиме зручні змінні PROJECT_NAME, PROJECT_VERSIONі PROJECT_DESCRIPTION(ця остання змінна потребує cmake 3.9):

project(mylib VERSION 1.0.1 DESCRIPTION "mylib description")

Оголосіть нову ціль бібліотеки. Будь ласка, уникайте використання file(GLOB ...). Ця особливість не забезпечує відвідування процесу компіляції. Якщо ви ліниві, скопіюйте вихід ls -1 sources/*.cpp:

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

Встановити VERSIONвластивість (необов’язково, але це є хорошою практикою):

set_target_properties(mylib PROPERTIES VERSION ${PROJECT_VERSION})

Ви також можете встановити SOVERSIONосновну кількість VERSION. Так libmylib.so.1буде символьним посиланням на libmylib.so.1.0.0.

set_target_properties(mylib PROPERTIES SOVERSION 1)

Декларуйте загальнодоступний API своєї бібліотеки. Цей API буде встановлено для стороннього додатка. Ізолювати його у вашому дереві проектів (наприклад, розміщуючи його в include/каталозі). Зауважте, приватні заголовки не повинні встановлюватися, і я настійно пропоную розмістити їх у вихідних файлах.

set_target_properties(mylib PROPERTIES PUBLIC_HEADER include/mylib.h)

Якщо ви працюєте з підкаталогами, не дуже зручно включати відносні шляхи на зразок "../include/mylib.h". Отже, передайте головний каталог у включені каталоги:

target_include_directories(mylib PRIVATE .)

або

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

Створіть правило встановлення для вашої бібліотеки. Я пропоную використовувати змінні, CMAKE_INSTALL_*DIRвизначені в GNUInstallDirs:

include(GNUInstallDirs)

І оголосити файли для встановлення:

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

Ви також можете експортувати pkg-configфайл. Цей файл дозволяє сторонній програмі легко імпортувати вашу бібліотеку:

Створіть файл шаблону з ім'ям mylib.pc.in(див шт (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}

У свій CMakeLists.txtдодайте правило для розширення @макросів ( @ONLYпопросіть cmake не розширювати змінні форми ${VAR}):

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

І нарешті, встановіть створений файл:

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

Ви також можете використовувати функцію cmakeEXPORT . Однак ця функція сумісна лише cmakeі мені важко користуватися.

Нарешті, все CMakeLists.txtмає виглядати так:

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)

10
Просто доповнюючи дивовижне пояснення @ Jezz: після всіх вищезазначених кроків програміст може створити та встановити бібліотеку mkdir build && cd build/ && cmake .. && sudo make install(або sudo make install/stripвстановити смугасту версію бібліотеки).
silvioprog

1
Чи є у вас техніка для усунення залежностей від бібліотеки? Наприклад, якби mylib залежав від liblog4cxx, який би був хороший спосіб передачі цього шляху до mylib.pc?
квітня

1
@mpr Якщо liblog4cxx надає .pcфайл, додайте його Requires: liblog4cxxдо mylib.pcіншого, ви можете просто додати -llog4cxxйого Libs:.
Jérôme Pouiller

Як я можу використовувати цю бібліотеку в іншому проекті? Чи можете ви продовжити свій приклад?
Дамір Поробич

І чи додаєте ви всі заголовки до PUBLIC_HEADER або лише той, який повинні бачити інші користувачі? Якщо ні, що ви робите з усіма іншими заголовками?
Дамір Поробич

84

Цей мінімальний CMakeLists.txtфайл збирає просту спільну бібліотеку:

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)

Однак у мене немає досвіду копіювання файлів на інше призначення за допомогою CMake. Файлова команда з підписом COPY / INSTALL виглядає, що може бути корисною.


34
CMAKE_BUILD_TYPE слід опустити, тому рішення залежить від того, хто компілює.
ManuelSchneid3r

Чи є вказівка ${CMAKE_CURRENT_SOURCE_DIR}/в include_directoriesкорисній?
Jérôme Pouiller

@Jezz Я не думаю, що в той самий каталог входить без префікса. Було б важливо, якщо ви знаходитесь у підкаталозі.
Арнав Борбора

А що робити, якщо я хочу змішати свої джерела та свої заголовки у загальному каталозі "джерела"? Чи є можливість "після покоління" створити каталог "заголовка" з моїх джерел? (Установка команд може бути)
Сендберга

24

Я намагаюся навчитися робити це самостійно, і, здається, ви можете встановити бібліотеку так:

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})

12

По-перше, це макет каталогу, який я використовую:

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

Через пару днів, щоб розібратися в цьому, це мій улюблений спосіб зробити це завдяки сучасним 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)

Після запуску CMake та встановлення бібліотеки не потрібно використовувати Find ***. Cmake файли, це можна використовувати так:

find_package(MyLib REQUIRED)

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

Це все, якщо він був встановлений у стандартному каталозі, він буде знайдений, і більше нічого робити не потрібно. Якщо він був встановлений нестандартним шляхом, це також просто, просто скажіть CMake, де знайти MyLibConfig.cmake, використовуючи:

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

Я сподіваюся, що це допомагає всім так само, як і мені. Старі способи цього були досить громіздкими.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.