सेमीके साथ एक साझा पुस्तकालय कैसे बनाएं?


141

मैंने एक पुस्तकालय लिखा है जिसे मैं एक स्व-लिखित मेकफाइल का उपयोग करके संकलित करता था, लेकिन अब मैं सीमेक पर स्विच करना चाहता हूं। पेड़ ऐसा दिखता है (मैंने सभी अप्रासंगिक फाइलें हटा दी हैं):

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

इसलिए मैं जो करने की कोशिश कर रहा हूं वह स्रोत को एक साझा लाइब्रेरी में संकलित करना है और फिर इसे हेडर फ़ाइलों के साथ इंस्टॉल करना है।

अधिकांश उदाहरण जिन्हें मैंने कुछ साझा पुस्तकालयों के साथ संकलित निष्पादन योग्य पाया है, लेकिन कभी भी एक साझा साझा पुस्तकालय नहीं है। यह भी उपयोगी होगा यदि कोई मुझे सिर्फ एक बहुत ही सरल पुस्तकालय बता सकता है जो सीमेक का उपयोग करता है, इसलिए मैं इसे एक उदाहरण के रूप में उपयोग कर सकता हूं।



एक ही सवाल है, लेकिन क्या मेरे स्रोतों को मिश्रित बनाए रखने का एक तरीका है (एक ही स्रोतों की निर्देशिका में। 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)

अपने पुस्तकालय के सार्वजनिक एपीआई की घोषणा करें। यह एपीआई तीसरे पक्ष के आवेदन के लिए स्थापित किया जाएगा। अपने प्रोजेक्ट ट्री में इसे अलग करना एक अच्छा अभ्यास है 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फॉर्म के वेरिएबल्स का विस्तार न करने के लिए पूछें ${VAR}):

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

और अंत में, जनरेट की गई फ़ाइल इंस्टॉल करें:

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

आप cmake EXPORTफीचर का भी इस्तेमाल कर सकते हैं । हालांकि, यह सुविधा केवल संगत है 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को स्थापित करने के लिए )।
सिल्वियोप्रोग

1
क्या आपके पास पुस्तकालय निर्भरता को कम करने के लिए एक तकनीक है? उदाहरण के लिए यदि mylib liblog4cxx पर निर्भर करता है, तो mylib.pc के माध्यम से सभी तरह से बहने का एक अच्छा तरीका क्या होगा?
mr

1
@mpr liblog4cxx एक प्रदान करते हैं .pcफ़ाइल, जोड़ने Requires: liblog4cxxअपने को mylib.pc, बाकी, तुम बस जोड़ सकते हैं -llog4cxxकरने के लिए Libs:
जेरेम पोइलर

मैं किसी अन्य प्रोजेक्ट में इस लाइब्रेरी का उपयोग कैसे करूंगा? क्या आप अपना उदाहरण दे सकते हैं?
दामिर पोरोबिक

और क्या आप सभी शीर्षकों को 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)

हालाँकि, मुझे सीएमके साथ एक अलग गंतव्य पर फ़ाइलों की प्रतिलिपि बनाने का कोई अनुभव नहीं है। COPY / INSTALL हस्ताक्षर के साथ फाइल कमांड ऐसा लगता है कि यह उपयोगी हो सकता है।


34
CMAKE_BUILD_TYPE को छोड़ दिया जाना चाहिए, इसलिए निर्णय उसी पर निर्भर करता है जो संकलन करता है।
मैनुएल श्नाइड 3r

निर्दिष्ट करने है ${CMAKE_CURRENT_SOURCE_DIR}/में include_directoriesउपयोगी है?
जेरेम पोइलर

@ जेजे मुझे ऐसा नहीं लगता, वही निर्देशिका उपसर्ग के बिना शामिल हो जाती है। यदि आप एक उपनिर्देशिका में थे, तो यह मायने रखेगा।
अर्नव बोरबोरह

और क्या होगा अगर मैं एक सामान्य "स्रोत" निर्देशिका में अपने स्रोतों और मेरे हेडर को मिश्रण करना चाहता हूं? क्या मेरे स्रोतों से "हेडर" निर्देशिका बनाने के लिए "पोस्ट जेनरेशन" की संभावना है? (शायद कमांड स्थापित करें)
सैंडबर्ग

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

यही है, अगर इसे एक मानक निर्देशिका में स्थापित किया गया है तो यह मिल जाएगा और कुछ और करने की आवश्यकता नहीं है। यदि यह एक गैर-मानक पथ में स्थापित किया गया है, तो यह आसान भी है, बस सीएमके को बताएं कि कहां MyLibConfig.cmake का उपयोग करना है:

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

मुझे उम्मीद है कि यह हर किसी की मदद करता है जितना उसने मेरी मदद की है। ऐसा करने के पुराने तरीके काफी बोझिल थे।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.