`Cmake` से` pkg-config` का उपयोग करने का उचित तरीका क्या है?


87

नेट पर चारों ओर देखने पर मैंने इस तरह का बहुत कोड देखा है:

include(FindPkgConfig)
pkg_search_module(SDL2 REQUIRED sdl2)

target_include_directories(app SYSTEM PUBLIC ${SDL2_INCLUDE_DIRS}
target_link_libraries(app ${SDL2_LIBRARIES})

हालाँकि, ऐसा करने के बारे में यह गलत तरीका लगता है, क्योंकि इसमें केवल निर्देशिका और लाइब्रेरी शामिल हैं, लेकिन अनदेखा परिभाषित, पुस्तकालय पथ और अन्य झंडे जो कि वापस आ सकते हैं pkg-config

ऐसा करने का सही तरीका क्या होगा और यह सुनिश्चित करना होगा कि संकलित किए गए सभी संकलन और लिंक झंडे pkg-configका उपयोग किया जाता है app? और क्या इसे पूरा करने के लिए एक ही आदेश है, अर्थात ऐसा कुछ target_use(app SDL2)?

संदर्भ:

जवाबों:


33

यदि आप बहुत सामान्य तरीके से cmake और pkg-config का उपयोग कर रहे हैं, तो यह समाधान काम करता है।

यदि, हालांकि, आपके पास एक पुस्तकालय है जो कुछ विकास निर्देशिका (जैसे / घर / मुझे / हैक / परिवाद) में मौजूद है, तो यहां देखे गए अन्य तरीकों का उपयोग करके लिंकर पथों को कॉन्फ़िगर करने में विफल रहता है। आमतौर पर इंस्टॉल किए गए स्थानों के नीचे पाई जाने वाली लाइब्रेरियों में लिंकर की त्रुटियां होती हैं, जैसे /usr/bin/ld: cannot find -lmy-hacking-library-1.0। यह समाधान उस स्थिति के लिए लिंकर त्रुटि को ठीक करता है।

एक और मुद्दा यह हो सकता है कि pkg-config फ़ाइलें सामान्य स्थान पर स्थापित नहीं हैं, और प्रोजेक्ट के लिए pkg-config रास्तों को PKG_CONFIG_PATH परिवेश चर का उपयोग करके जोड़ा जाना चाहिए, जबकि cmake चल रहा है (देखें इसके बारे में स्टैक ओवरफ्लो प्रश्न देखें)। मान लें कि आपके पास सही pkg-config पथ सेट है, तो यह समाधान उस समस्या को भी ठीक करता है।

समाधान कार्यशील CMakeLists.txt के इस अंतिम संस्करण को उबालता है:

cmake_minimum_required(VERSION 3.14)
project(ya-project C)

# the `pkg_check_modules` function is created with this call
find_package(PkgConfig REQUIRED) 

# these calls create special `PkgConfig::<MODULE>` variables
pkg_check_modules(MY_PKG REQUIRED IMPORTED_TARGET any-package)
pkg_check_modules(YOUR_PKG REQUIRED IMPORTED_TARGET ya-package)

add_executable(program-name file.c ya.c)

target_link_libraries(program-name PUBLIC
        PkgConfig::MY_PKG
        PkgConfig::YOUR_PKG)

ध्यान दें कि target_link_librariesलिंकर कमांड को बदलने से अधिक है। यह निर्दिष्ट लक्ष्यों के अन्य सार्वजनिक गुणों को भी प्रचारित करता है जैसे: संकलक झंडे, संकलक परिभाषित, पथ शामिल हैं, आदि।


5
IMPORTED_TARGETसीएमके 3.6 या नए की आवश्यकता है।
क्राइस लुआंगो

1
यदि आपने इसे अस्वीकृत कर दिया है, तो कृपया सुनिश्चित करें और टिप्पणी करें कि आपने क्यों अस्वीकृत किया ताकि हम उत्तर को बेहतर बना सकें।
एक्टेडेकेय

मुझे लगता है कि gitlab.kitware.com/cmake/cmake/-/issues/19387 के कारण यह मेरे लिए विफल रहा ।
फ्लोरियन ज़ोवच

63

सबसे पहले, कॉल:

include(FindPkgConfig)

के साथ प्रतिस्थापित किया जाना चाहिए:

find_package(PkgConfig)

find_package()कॉल अधिक लचीला है और इस तरह के रूप विकल्प की अनुमति देता है REQUIRED, कि चीजें स्वचालित रूप से उस एक के साथ मैन्युअल रूप से करना होगा करना include()

दूसरे, मैन्युअल कॉलिंग pkg-config से जब संभव हो तो बचना चाहिए। CMake पैकेज परिभाषाओं के एक समृद्ध सेट के साथ आता है, लिनक्स में पाया जाता है /usr/share/cmake-3.0/Modules/Find*cmake। ये उपयोगकर्ता को कच्ची कॉल की तुलना में अधिक विकल्प और विकल्प प्रदान करते हैं pkg_search_module()

उल्लिखित काल्पनिक target_use()कमांड के लिए, सीएमके के पास पहले से ही अंतर्निहित एक तरह से सार्वजनिक | निजी | इंटरएक्टिव है। जैसे कॉल target_include_directories(mytarget PUBLIC ...)में शामिल निर्देशिकाओं को स्वचालित रूप से उपयोग किए जाने वाले प्रत्येक लक्ष्य में उपयोग करने का कारण होगा mytarget, जैसे target_link_libraries(myapp mytarget)। हालाँकि यह तंत्र केवल CMakeLists.txtफ़ाइल के भीतर बनाए गए पुस्तकालयों के लिए ही लगता है और इससे प्राप्त पुस्तकालयों के लिए काम नहीं करता है pkg_search_module()। इसके लिए कॉल add_library(bar SHARED IMPORTED)का उपयोग किया जा सकता है, लेकिन मैंने अभी तक उस पर ध्यान नहीं दिया है।

मुख्य प्रश्न के रूप में, यह यहाँ ज्यादातर मामलों में काम करता है:

find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
...
target_link_libraries(testapp ${SDL2_LIBRARIES})
target_include_directories(testapp PUBLIC ${SDL2_INCLUDE_DIRS})
target_compile_options(testapp PUBLIC ${SDL2_CFLAGS_OTHER})

SDL2_CFLAGS_OTHERपरिभाषित करता है और अन्य झंडे एक सफल संकलन के लिए आवश्यक होता है। झंडे SDL2_LIBRARY_DIRSऔर SDL2_LDFLAGS_OTHERफिर भी नजरअंदाज कर दिए जाते हैं, पता नहीं कितनी बार यह समस्या बन जाएगी।

यहाँ और अधिक प्रलेखन http://www.cmake.org/cmake/help/v3.0/module/FindPkgConfig.html


4
मैं सहमत हूं कि अगर एक * *। Cmake मौजूद है, तो pkg-config से बचा जाना चाहिए , लेकिन 2016 में cmake के नवीनतम संस्करण के लिए अभी भी ऐसा नहीं है।
Cubic

3
यह काम नहीं करता है यदि लायब्रेरी डिफ़ॉल्ट निर्देशिकाओं में नहीं हैं। link_directories () वर्कअराउंड हो सकता है, लेकिन यह वैश्विक है।
हेनरी हू

यह दृष्टिकोण vcpkg के लिए काम नहीं करता है । क्या मैं हार्ड-कोडिंग पथ के बिना SDL2_image का पता लगा सकता हूँ !?
user2023370

@HenryHu क्या आप यह दिखा सकते हैं कि यदि इस उत्तर में विन्यास की स्थिति होती तो यह कैसे होता?
टिम विज़े

2
दुनिया के हर पुस्तकालय को सूँघने के लिए सीएमके जैसे निर्माण उपकरण की आवश्यकता है, इससे कोई मतलब नहीं है, यह इसकी भूमिका नहीं है। Pkg-config को इसलिए बनाया गया है ताकि यह यूज़र्स को उपलब्ध कराने के लिए lib लेखक या pkg / distro अनुचर की जिम्मेदारी हो। और अगर इस योजना का पालन किया जाता है, तो एक लीब का उपयोग करने का सही तरीका हमेशा pkg-config कॉलिंग के माध्यम से होता है।
जोहान बोले

10

यह दुर्लभ है कि किसी को केवल SDL2 के साथ लिंक करना होगा। वर्तमान में लोकप्रिय उत्तर उपयोग करता है pkg_search_module()जो दिए गए मॉड्यूल की जांच करता है और पहले काम करने वाले का उपयोग करता है।

यह अधिक संभावना है कि आप SDL2 और SDL2_Mixer और SDL2_TTF, आदि के साथ लिंक करना चाहते हैं ... pkg_check_modules()सभी दिए गए मॉड्यूल के लिए चेक।

# sdl2 linking variables
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2 SDL2_ttf SDL2_mixer SDL2_image)

# your app
file(GLOB SRC "my_app/*.c")
add_executable(my_app ${SRC})
target_link_libraries(my_app ${SDL2_LIBRARIES})
target_include_directories(my_app PUBLIC ${SDL2_INCLUDE_DIRS})
target_compile_options(my_app PUBLIC ${SDL2_CFLAGS_OTHER})

डिस्क्लेमर: मैंने ग्रुम्बेल के स्वयं के जवाब पर बस टिप्पणी की होगी यदि मेरे पास स्टैकओवरफ्लो के साथ पर्याप्त सड़क क्रेडिट था।


2
ग्लोबिंग सोर्स फाइलें खराब प्रैक्टिस और हतोत्साहित करती हैं।
आजादी

1
मेरे लिए, target_link_libraries(my_app ${SDL2_LINK_LIBRARIES})बेहतर काम किया।
स्टीफन

2
@liberforce ग्लोबिंग सोर्स फाइल्स अच्छी प्रैक्टिस है, अगर यह छोटी गाड़ी है तो सीएमके की गलती है।
जोहान बोले

2
@ जोहान्बोल: नहीं, यह नहीं है। आपके पास डेवलपर के पास स्थानीय रूप से फ़ाइलों का एक गुच्छा हो सकता है और उनके कंप्यूटर पर सामान का काम हो सकता है, और आवश्यक सभी फ़ाइलों को शुरू नहीं करना चाहिए। फिर वे अपने परिवर्तनों को आगे बढ़ाते हैं और यह अन्य लोगों के लिए टूट जाता है। निश्चित रूप से, इसे कुछ निरंतर एकीकरण द्वारा पकड़ा जा सकता है, लेकिन यह केवल सबसे स्पष्ट समस्या है। Meson निर्माण प्रणाली फ़ाइल globing को लागू नहीं करने का फैसला किया है, और CMake डेवलपर्स स्पष्ट ग्लोबिंग हतोत्साहित । निहितार्थ की तुलना में स्पष्ट है।
मुक्त करें

1
@liberforce मैंने देखा है कि यह तर्क पहले से ही वास्तविक समस्या की तुलना में कई गुना अधिक है। मेसोन के खिलाफ, बिल्ड 2 के लिए। किसी के पास यह नहीं होगा, जैसे टैब बनाम स्पेस।
जोहान बोले

6

अधिकांश उपलब्ध उत्तर pkg-configलाइब्रेरी के हेडर को कॉन्फ़िगर करने में विफल होते हैं । FindPkgConfig के लिए डॉक्यूमेंटेशन पर ध्यान देने के बाद मैं एक समाधान लेकर आया हूं जो उन्हें भी प्रदान करता है:

include(FindPkgConfig)
if(NOT PKG_CONFIG_FOUND)
  message(FATAL_ERROR "pkg-config not found!" )
endif()
 
pkg_check_modules(<some-lib> REQUIRED IMPORTED_TARGET <some-lib>)
 
target_link_libraries(<my-target> PkgConfig::<some-lib>)

( तदनुसार, और उसके स्थान पर <my-target>जो भी लाइब्रेरी हो, अपने लक्ष्य को निर्धारित करें <some-lib> )

IMPORTED_TARGETविकल्प कुंजी हो रहा है और सब कुछ तो तहत उपलब्ध बनाता PkgConfig::नाम स्थान। यह सब है कि जरूरत पड़ी थी और यह भी कि सभी चाहिए की आवश्यकता हो।


TIP: pkg_check_modulesउपलब्ध vars stackoverflow.com/a/9328525/1211174
oak

0
  1. जैसी कोई आज्ञा नहीं है target_use। लेकिन मैं कई परियोजनाओं को जानता हूं जिन्होंने अपने आंतरिक उपयोग के लिए इस तरह की कमांड लिखी है। लेकिन हर परियोजना अतिरिक्त झंडे या परिभाषित करना चाहती है, इस प्रकार सामान्य सीएमके में इसका कोई मतलब नहीं है। एक और कारण यह नहीं है कि C ++ टेम्प्लेटेड लाइब्रेरी हैं जैसे कि Eigen, कोई लाइब्रेरी नहीं है लेकिन आपके पास केवल शामिल फ़ाइलों का एक गुच्छा है।

  2. वर्णित तरीका अक्सर सही होता है। यह कुछ पुस्तकालयों के लिए भिन्न हो सकता है, फिर आपको जोड़ना होगा _LDFLAGSया _CFLAGS। न होने का एक और कारण target_use। यदि यह आपके लिए काम नहीं करता है, तो एसडीएल 2 या जो भी पुस्तकालय आप उपयोग करना चाहते हैं, उसके बारे में एक नया प्रश्न पूछें।


-2

यदि आप लाइब्रेरी से परिभाषाएँ जोड़ना चाहते हैं, तो इसके लिए add_definitionsनिर्देश है। संकलक झंडे जोड़ने के लिए और अधिक तरीकों के साथ, यहां प्रलेखन पाया जा सकता है ।

निम्न कोड स्निपेट GTKGL को प्रोजेक्ट में जोड़ने के लिए इस निर्देश का उपयोग करता है:

pkg_check_modules(GTKGL REQUIRED gtkglext-1.0)
include_directories(${GTKGL_INCLUDE_DIRS})
link_directories(${GTKGL_LIBRARY_DIRS})
add_definitions(${GTKGL_CFLAGS_OTHER})
set(LIBS ${LIBS} ${GTKGL_LIBRARIES})

target_link_libraries([insert name of program] ${LIBS})

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