अगर आपको CMAKE_MODULE_PATH को निर्दिष्ट करने की आवश्यकता है, तो find_package () का क्या उपयोग है?


167

मैं CMake का उपयोग करके एक क्रॉस-प्लैटफॉर्म बिल्ड सिस्टम प्राप्त करने की कोशिश कर रहा हूं। अब सॉफ्टवेयर में कुछ निर्भरताएं हैं। मैंने उन्हें खुद संकलित किया और उन्हें अपने सिस्टम पर स्थापित किया।

कुछ उदाहरण फ़ाइलें जो स्थापित हो गईं:

-- Installing: /usr/local/share/SomeLib/SomeDir/somefile
-- Installing: /usr/local/share/SomeLib/SomeDir/someotherfile
-- Installing: /usr/local/lib/SomeLib/somesharedlibrary
-- Installing: /usr/local/lib/SomeLib/cmake/FindSomeLib.cmake
-- Installing: /usr/local/lib/SomeLib/cmake/HelperFile.cmake

अब CMake के पास एक फाइल है जो सिस्टम पर लाइब्रेरी के बाद find_package()एक Find*.cmakeफाइल खोलता है और खोजता है और कुछ चर जैसे SomeLib_FOUNDआदि को परिभाषित करता है ।

मेरे CMakeLists.txt में कुछ इस तरह है:

set(CMAKE_MODULE_PATH "/usr/local/lib/SomeLib/cmake/;${CMAKE_MODULE_PATH}")
find_package(SomeLib REQUIRED)

पहला कमांड परिभाषित करता है कि सीएमके खोज करने के बाद कहां जाता है Find*.cmakeऔर मैंने उस निर्देशिका को जोड़ा SomeLibजहां FindSomeLib.cmakeपाया जा सकता है, इसलिए find_package()उम्मीद के मुताबिक काम करता है।

लेकिन यह अजीब तरह का है क्योंकि find_package()मौजूद कारणों में से एक गैर-क्रॉस-प्लैटफॉर्म हार्ड कोडेड पथ से दूर होना है।

यह आमतौर पर कैसे किया जाता है? क्या मुझे अपनी परियोजना की cmake/निर्देशिका को कॉपी करना चाहिए SomeLibऔर CMAKE_MODULE_PATHअपेक्षाकृत सेट करना चाहिए ?


वह पैटर्न मुझे बहुत अजीब लगता है। CMake का उपयोग करने वाले पुस्तकालयों को इस तरह से अपने 'खोज' मॉड्यूल को उजागर नहीं करना चाहिए। आप इस तरह से कैसे "SomeLib" खोजने के लिए आए थे? और यह किस परिवाद में है?
सिडरिक डेस

2
ऐसा ही कुछ cmake.org/Wiki/… में भी किया गया है । और यह OGRE है।
मार्कडेफिएंट

2
आप जिस अनुभाग का उल्लेख करते हैं, वह इस प्रकार है: "चूंकि CMake (वर्तमान में) इसे शिप नहीं करता है, इसलिए आपको इसे अपने प्रोजेक्ट में शिप करना होगा।" मैंने लिबायामल ( github.com/noirotm/flvmeta/tree/master/cmake/modules देखें ) को खोजने के लिए फ्लेवमेट में यही किया है । मॉड्यूल पथ इस निर्देशिका को इंगित करता है, मेरी परियोजना के अंदर।
सिराद्रि

3
मैं आमतौर पर अपने प्रोजेक्ट में फाइंडएक्सएक्सएक्स मॉड्यूल की प्रतिलिपि बनाता हूं और CMAKE_MODULE_PATH को सेट करता हूं (यदि वे मॉड्यूल पाठ्यक्रम के सीएमके में मौजूद नहीं हैं), मैंने इस पैटर्न को कई बार अन्य परियोजनाओं में भी देखा है
szx

जवाबों:


214

कमांड find_packageमें दो मोड हैं: Moduleमोड और Configमोड। आप Moduleमोड का उपयोग करने की कोशिश कर रहे हैं जब आपको वास्तव में Configमोड की आवश्यकता होती है।

मॉड्यूल मोड

Find<package>.cmakeअपने प्रोजेक्ट के भीतर स्थित फ़ाइल । कुछ इस तरह:

CMakeLists.txt
cmake/FindFoo.cmake
cmake/FindBoo.cmake

CMakeLists.txt सामग्री:

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
find_package(Foo REQUIRED) # FOO_INCLUDE_DIR, FOO_LIBRARIES
find_package(Boo REQUIRED) # BOO_INCLUDE_DIR, BOO_LIBRARIES

include_directories("${FOO_INCLUDE_DIR}")
include_directories("${BOO_INCLUDE_DIR}")
add_executable(Bar Bar.hpp Bar.cpp)
target_link_libraries(Bar ${FOO_LIBRARIES} ${BOO_LIBRARIES})

ध्यान दें कि CMAKE_MODULE_PATHउच्च प्राथमिकता है और उपयोगी हो सकता है जब आपको मानक Find<package>.cmakeफ़ाइल को फिर से लिखना होगा ।

विन्यास मोड (स्थापित करें)

<package>Config.cmakeफ़ाइल बाहर स्थित है और install अन्य प्रोजेक्ट ( Fooउदाहरण के लिए) के कमांड द्वारा निर्मित है ।

foo पुस्तकालय:

> cat CMakeLists.txt 
cmake_minimum_required(VERSION 2.8)
project(Foo)

add_library(foo Foo.hpp Foo.cpp)
install(FILES Foo.hpp DESTINATION include)
install(TARGETS foo DESTINATION lib)
install(FILES FooConfig.cmake DESTINATION lib/cmake/Foo)

कॉन्फ़िगर फ़ाइल का सरलीकृत संस्करण:

> cat FooConfig.cmake 
add_library(foo STATIC IMPORTED)
find_library(FOO_LIBRARY_PATH foo HINTS "${CMAKE_CURRENT_LIST_DIR}/../../")
set_target_properties(foo PROPERTIES IMPORTED_LOCATION "${FOO_LIBRARY_PATH}")

डिफ़ॉल्ट प्रोजेक्ट CMAKE_INSTALL_PREFIXनिर्देशिका में स्थापित करके :

> cmake -H. -B_builds
> cmake --build _builds --target install
-- Install configuration: ""
-- Installing: /usr/local/include/Foo.hpp
-- Installing: /usr/local/lib/libfoo.a
-- Installing: /usr/local/lib/cmake/Foo/FooConfig.cmake

विन्यास मोड (उपयोग)

आयातित लक्ष्य के साथ find_package(... CONFIG)शामिल करने के लिए उपयोग करें :FooConfig.cmakefoo

> cat CMakeLists.txt 
cmake_minimum_required(VERSION 2.8)
project(Boo)

# import library target `foo`
find_package(Foo CONFIG REQUIRED)

add_executable(boo Boo.cpp Boo.hpp)
target_link_libraries(boo foo)
> cmake -H. -B_builds -DCMAKE_VERBOSE_MAKEFILE=ON
> cmake --build _builds
Linking CXX executable Boo
/usr/bin/c++ ... -o Boo /usr/local/lib/libfoo.a

ध्यान दें कि आयातित लक्ष्य अत्यधिक विन्यास योग्य है। मेरा जवाब देखिए ।

अपडेट करें


1
आपका जवाब बहुत अच्छा है। हालांकि, जीथब में उदाहरण अधिक जटिल है कि यह आईएमओ हो सकता है। सामान्य मामले में जहां एक उपनिर्देशिका (मॉड्यूल) एकल विरूपण साक्ष्य को निर्यात करता है, हेडर के साथ एक परिवाद कहता है, आपको कस्टम * config.cmake उत्पन्न करने की आवश्यकता नहीं है। परिणामस्वरूप कॉन्फ़िगरेशन में काफी कटौती की जा सकती है। मुझे लगता है कि मैं खुद इसी तरह का उदाहरण दूंगा।
दिमित्रिस

2
@ डिटर्मिस हाँ, इसे थोड़ा सरल किया जा सकता है। मैंने github का उदाहरण अपडेट किया है इसलिए अब यह उपयोग नहीं करता है configure_package_config_file। वैसे यदि आपके पास कोई अन्य सुझाव है तो आप मुझे पुल अनुरोध भेज सकते हैं।

1
@rusio यहाँ मेरा उदाहरण है । यह अखंड निर्माण (रूट फ़ोल्डर से सभी मॉड्यूल) या स्वायत्त बिल्ड (प्रत्येक मॉड्यूल अलग से, इंस्टॉल की आवश्यकता है) का समर्थन करता है।
दिमित्रिस

1
@Dimitris ठीक है, अब मैं देखता हूं। आमतौर पर फ़ाइल जिसे आप "ऑप्टिमाइज़ करते हैं" अतिरिक्त सामान लोड करने के लिए ढूंढते हैं जैसे find_d dependency । मुझे लगता है कि यह शुरू करने के लिए एक अच्छा खाका है, इसलिए मैं इसे बनाए रखूंगा, यहां तक ​​कि यह वास्तव में उपयोग नहीं किया जाता है। बाकी कोड अधिक सरल लगते हैं क्योंकि आप संस्करण, जैसे डीएल के लिए निर्यात, लेआउट के साथ कुछ कार्यक्षमता bin/lib(निष्पादन योग्य स्थापित करने और इसे विंडोज़ पर चलाने की कोशिश) को याद कर रहे हैं । और नाम स्थान बहुत सुंदर लगते हैं, इसलिए मैं उन्हें भी रखूंगा :) इसके अलावा मैंने monolithicबिल्ड भी जोड़ा है ।

1
आपके प्रत्येक उदाहरण मेरे लिए बहुत सहायक थे। तुम दोनों को धन्यवाद!
अंचल

2

यदि आप खुद cmakeको उत्पन्न करने के लिए दौड़ रहे हैं SomeLib(एक सुपरबिल्ट के हिस्से के रूप में कहें), उपयोगकर्ता पैकेज रजिस्ट्री का उपयोग करने पर विचार करें । इसके लिए किसी हार्ड-कोडेड पथ की आवश्यकता नहीं है और यह क्रॉस-प्लेटफ़ॉर्म है। विंडोज पर (mingw64 सहित) यह रजिस्ट्री के माध्यम से काम करता है। यदि आप यह जांचते हैं कि स्थापना उपसर्गों की सूची find_packages () कमांड के CONFIGमोड द्वारा कैसे बनाई गई है , तो आप देखेंगे कि उपयोगकर्ता पैकेज रजिस्ट्री तत्वों में से एक है।

संक्षिप्त कैसे

SomeLibउस बाह्य प्रोजेक्ट के बाहर के उन लक्ष्यों को संबद्ध करें, CMakeLists.txtजहाँ वे बनाई गई फ़ाइलों में निर्यात सेट में जोड़कर :

add_library(thingInSomeLib ...)
install(TARGETS thingInSomeLib Export SomeLib-export DESTINATION lib)

एक बनाएँ XXXConfig.cmakeके लिए फ़ाइल SomeLibअपने में ${CMAKE_CURRENT_BUILD_DIR}और करने के लिए दो कॉल जोड़कर उपयोगकर्ता पैकेज रजिस्ट्री में इस स्थान की दुकान निर्यात () के CMakeLists.txtसाथ जुड़े SomeLib:

export(EXPORT SomeLib-export NAMESPACE SomeLib:: FILE SomeLibConfig.cmake) # Create SomeLibConfig.cmake
export(PACKAGE SomeLib)                                                    # Store location of SomeLibConfig.cmake

"गैर-क्रॉस-प्लेटफ़ॉर्म हार्ड कोडेड पथ" के बिना निर्भर करता है कि परियोजना find_package(SomeLib REQUIRED)की CMakeLists.txtफ़ाइल में अपना कमंड जारी करें ।SomeLibCMAKE_MODULE_PATH

जब यह सही दृष्टिकोण हो सकता है

यह दृष्टिकोण संभवतः उन परिस्थितियों के लिए सबसे उपयुक्त है जहाँ आप अपने सॉफ़्टवेयर का निर्माण निर्देशिका में कभी नहीं करेंगे (उदाहरण के लिए, आप क्रॉस-संकलन कर रहे हैं और कभी भी अपनी मशीन पर कुछ भी स्थापित नहीं करते हैं, या आप केवल परीक्षण चलाने के लिए सॉफ़्टवेयर का निर्माण कर रहे हैं बिल्ड डायरेक्टरी), क्योंकि यह आपके "बिल्ड" आउटपुट में एक .cmake फ़ाइल का लिंक बनाता है, जो अस्थायी हो सकता है।

लेकिन अगर आप वास्तव SomeLibमें कभी भी अपने वर्कफ़्लो में स्थापित नहीं होते हैं , तो कॉलिंग EXPORT(PACKAGE <name>)आपको हार्ड-कोडेड पथ से बचने की अनुमति देता है। और, ज़ाहिर है, यदि आप स्थापित कर रहे हैं SomeLib, तो आप शायद अपने मंच CMAKE_MODULE_PATH, आदि को जानते हैं , इसलिए @ user2288008 का उत्कृष्ट उत्तर आपको कवर करेगा।


1

आपको प्रति से मॉड्यूल पथ निर्दिष्ट करने की आवश्यकता नहीं है। सीएमके जहाज में निर्मित find_package स्क्रिप्ट के अपने सेट के साथ, और उनका स्थान डिफ़ॉल्ट CMAKE_MODULE_PATH में है।

सीएमकेफाइड पर निर्भर परियोजनाओं के लिए अधिक सामान्य उपयोग का मामला सीएमके के बाहरी_प्रोजेक्ट कमांड का उपयोग करना होगा और फिर उपप्रोजेक्ट से उपयोग [प्रोजेक्ट] .cmake फ़ाइल शामिल करें। यदि आपको केवल [Project] .cmake स्क्रिप्ट की आवश्यकता है, तो उसे सबप्रोजेक्ट से और अपने प्रोजेक्ट के सोर्स कोड में कॉपी करें, और फिर सिस्टम स्तर पर सबप्रोजेक्ट को खोजने के लिए आपको CMAKE_MODULE_PATH को बढ़ाने की आवश्यकता नहीं होगी।


12
their location is in the default CMAKE_MODULE_PATHडिफ़ॉल्ट रूप CMAKE_MODULE_PATHसे खाली है

2018 में @ user2288008 की टिप्पणी की पुष्टि कर सकते हैं CMAKE_MODULE_PATH। विंडोज पर खाली है।
जेरेन

यह आपकी परियोजना के साथ मॉड्यूल शिपिंग के लिए एक परियोजना विशिष्ट चर है। "डिफ़ॉल्ट रूप से यह खाली है, इसे परियोजना द्वारा निर्धारित किया जाना है।" cmake.org/cmake/help/latest/variable/CMAKE_MODULE_PATH.html
Farway

1

यह आमतौर पर कैसे किया जाता है? क्या मुझे cmake/अपने प्रोजेक्ट में SomeLib की निर्देशिका को कॉपी करना चाहिए और CMAKE_MODULE_PATH को अपेक्षाकृत निर्धारित करना चाहिए?

यदि आप उस मॉड्यूल को रखने के लिए सीएमके पर भरोसा नहीं करते हैं, तो - हाँ, ऐसा करें - जैसे:find_SomeLib.cmake अपनी cmake/निर्देशिका में और उसके निर्भरता की प्रतिलिपि बनाएँ । यही मैं एक कमबैक के रूप में करता हूं। हालांकि यह एक बदसूरत समाधान है।

ध्यान दें कि FindFoo.cmakeमॉड्यूल प्लेटफ़ॉर्म-निर्भरता और प्लेटफ़ॉर्म-स्वतंत्रता के बीच एक पुल का एक प्रकार है - वे विभिन्न प्लेटफ़ॉर्म-विशिष्ट स्थानों पर चर में पथ प्राप्त करने के लिए देखते हैं जिनके नाम प्लेटफ़ॉर्म-स्वतंत्र हैं।

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