การใช้งานคือ find_package () ถ้าคุณต้องการระบุ CMAKE_MODULE_PATH แล้วล่ะ?


167

ฉันกำลังพยายามทำให้ cross-plattform build system ทำงานโดยใช้ 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)

คำสั่งแรกกำหนดที่ CMake ค้นหาหลังจากFind*.cmakeและฉันเพิ่มไดเรกทอรีของSomeLibที่FindSomeLib.cmakeสามารถพบได้จึงfind_package()ทำงานตามที่คาดไว้

แต่นี่เป็นสิ่งที่แปลกเพราะหนึ่งในเหตุผลที่find_package()มีอยู่คือการหลีกเลี่ยงเส้นทางที่มีการเข้ารหัสแบบไม่ข้าม - แพลตฟอร์ม

มักจะทำเช่นนี้ได้อย่างไร ฉันควรคัดลอกcmake/ไดเรกทอรีSomeLibไปยังโครงการของฉันและตั้งค่าCMAKE_MODULE_PATHค่อนข้าง


รูปแบบนั้นดูแปลกสำหรับฉันมาก ไลบรารี่ที่ใช้ CMake ไม่ควรเปิดเผยโมดูล 'find' ของพวกเขาด้วยวิธีนี้ คุณคิดวิธีที่จะหาคำว่า "SomeLib" ได้อย่างไร และมันคือ lib อะไร?
SirDarius

2
สิ่งที่คล้ายกันจะทำในcmake.org/Wiki/... และมันเป็นผีปอบ
MarcDefiant

2
ส่วนที่คุณเชื่อมโยงเพื่อกล่าวถึงสิ่งนี้: "เนื่องจาก CMake (ปัจจุบัน) ไม่ได้จัดส่งคุณจะต้องจัดส่งภายในโครงการของคุณ" นี่คือสิ่งที่ฉันทำใน flvmeta เพื่อค้นหา LibYAML (ดูgithub.com/noirotm/flvmeta/tree/master/cmake/modules ) เส้นทางโมดูลชี้ไปที่ไดเรกทอรีนี้ภายในโครงการของฉัน
SirDarius

3
ฉันมักจะคัดลอกโมดูล FindXXX กับโครงการและการตั้งค่าของฉัน CMAKE_MODULE_PATH (ถ้าโมดูลเหล่านั้นไม่ได้อยู่ใน CMake แน่นอน) ผมยังได้เห็นรูปแบบนี้หลายครั้งในโครงการอื่น ๆ
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.cmakeกับเป้าหมายที่นำเข้าfoo:

> 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
คำตอบของคุณยอดเยี่ยม อย่างไรก็ตามตัวอย่างที่ GitHub มีความซับซ้อนมากขึ้นที่สามารถเป็น IMO ในกรณีทั่วไปที่ไดเรกทอรีย่อย (โมดูล) ส่งออกสิ่งประดิษฐ์เดียวให้พูด lib พร้อมกับส่วนหัวคุณไม่จำเป็นต้องสร้าง * Config.cmake แบบกำหนดเอง เป็นผลให้การกำหนดค่าสามารถลดลงอย่างมีนัยสำคัญ ฉันคิดว่าฉันจะทำตัวอย่างที่คล้ายกันด้วยตัวเอง
Dimitris

2
@Dimitris ใช่มันสามารถทำให้เข้าใจได้ง่ายขึ้นเล็กน้อย ผมได้ปรับปรุงตัวอย่าง GitHub configure_package_config_fileดังนั้นตอนนี้มันไม่ได้ใช้ โดยวิธีการถ้าคุณมีข้อเสนอแนะอื่น ๆ คุณสามารถส่งคำขอดึง

1
@rusio นี่คือฉันตัวอย่างเช่น สนับสนุนการสร้างเสาหิน (โมดูลทั้งหมดจากโฟลเดอร์รูท) หรือบิวด์อิสระ (แต่ละโมดูลแยกกันต้องติดตั้ง)
Dimitris

1
@Dimitris โอเคตอนนี้ฉันเห็นแล้ว โดยปกติแฟ้มที่คุณ "เพิ่มประสิทธิภาพออกไป" ให้บริการสำหรับการโหลดสิ่งที่พิเศษเช่นfind_dependency ฉันคิดว่ามันเป็นเทมเพลตที่ดีในการเริ่มต้นดังนั้นฉันจะเก็บไว้แม้จะไม่ได้ใช้จริง ส่วนที่เหลือของรหัสดูง่ายขึ้นเพราะคุณขาดฟังก์ชั่นบางอย่างเช่นรุ่นส่งออกสำหรับ dll, เลย์เอาต์ด้วยbin/lib(ลองติดตั้งไฟล์ที่เรียกใช้งานได้และรันบน Windows) และเนมสเปซก็ดูสวยมากดังนั้นฉันจะเก็บมันไว้ด้วย :) ฉันได้เพิ่มงานmonolithicสร้างด้วย

1
ตัวอย่างของคุณแต่ละอย่างมีประโยชน์กับฉันมาก ขอบคุณทั้งคู่!
zmb

2

ถ้าคุณใช้cmakeในการสร้างSomeLibตัวเอง (พูดเป็นส่วนหนึ่งของ superbuild ก) พิจารณาใช้ผู้ใช้แพคเกจ Registry สิ่งนี้ไม่จำเป็นต้องใช้เส้นทางที่กำหนดรหัสยากและเป็นแพลตฟอร์มข้าม บน Windows (รวมถึง mingw64) ทำงานผ่านรีจิสตรี หากคุณตรวจสอบวิธีการสร้างรายการคำนำหน้าการติดตั้งโดยCONFIGโหมดของคำสั่งfind_packages ()คุณจะเห็นว่า User Package Registry เป็นหนึ่งในองค์ประกอบ

บทสรุปวิธีการ

เชื่อมโยงเป้าหมายSomeLibที่คุณต้องการภายนอกโครงการภายนอกนั้นโดยเพิ่มไปยังชุดการส่งออกในCMakeLists.txtไฟล์ที่สร้างขึ้น:

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

สร้างXXXConfig.cmakeไฟล์SomeLibใน${CMAKE_CURRENT_BUILD_DIR}และเก็บตำแหน่งนี้ใน User Package Registry โดยเพิ่มการเรียกสองครั้งเพื่อส่งออก ()ไปยังส่วนที่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ไฟล์ของโปรเจ็กต์ที่ขึ้นอยู่กับSomeLibไม่มี "เส้นทางแบบฮาร์ดโค้ดแบบไม่ข้ามแพลตฟอร์ม" ที่มีการCMAKE_MODULE_PATHติดตั้ง

เมื่อมันอาจเป็นแนวทางที่ถูกต้อง

วิธีนี้เหมาะที่สุดสำหรับสถานการณ์ที่คุณไม่เคยใช้ซอฟต์แวร์ดาวน์สตรีมของไดเรกทอรีการสร้าง (เช่นคุณกำลังคอมไพล์คอมไพล์และไม่ต้องติดตั้งอะไรบนเครื่องของคุณหรือคุณกำลังสร้างซอฟต์แวร์เพื่อทำการทดสอบ ไดเร็กทอรี build) เนื่องจากสร้างลิงก์ไปยังไฟล์. cmake ในเอาต์พุต "build" ของคุณซึ่งอาจเป็นชั่วคราว

แต่ถ้าคุณไม่เคยติดตั้งจริงSomeLibในเวิร์กโฟลว์การโทรEXPORT(PACKAGE <name>)จะช่วยให้คุณหลีกเลี่ยงเส้นทางที่มีการเข้ารหัสยาก และแน่นอนถ้าคุณกำลังติดตั้งSomeLibคุณอาจรู้จักแพลตฟอร์มของคุณCMAKE_MODULE_PATHฯลฯ ดังนั้นคำตอบที่ยอดเยี่ยมของ @ user2288008 จะครอบคลุม


1

คุณทำไม่ได้ ต้องการระบุเส้นทางโมดูลต่อ se CMake มาพร้อมกับชุดสคริปต์ find_package ในตัวและตำแหน่งของมันอยู่ในค่าเริ่มต้น CMAKE_MODULE_PATH

กรณีการใช้งานปกติมากขึ้นสำหรับโครงการที่ขึ้นต่อกันที่ได้รับ CMakeified จะใช้คำสั่ง external_project ของ CMake แล้วรวมไฟล์ Use [Project] .cmake จากโครงการย่อย หากคุณต้องการสคริปต์ Find [Project] .cmake ให้คัดลอกมันออกจากโครงการย่อยและลงในซอร์สโค้ดของโครงการของคุณเองจากนั้นคุณไม่จำเป็นต้องเพิ่ม CMAKE_MODULE_PATH เพื่อค้นหาโครงการย่อยที่ระดับระบบ


12
their location is in the default CMAKE_MODULE_PATHโดยค่าเริ่มต้นCMAKE_MODULE_PATHว่างเปล่า

สามารถยืนยันความคิดเห็นของ @ user2288008 ในปี 2561 CMAKE_MODULE_PATHได้บน Windows
Jeroen

มันเป็นตัวแปรเฉพาะโครงการสำหรับโมดูลที่จัดส่งกับโครงการของคุณ "โดยค่าเริ่มต้นมันว่างเปล่ามีวัตถุประสงค์เพื่อตั้งค่าโดยโครงการ" cmake.org/cmake/help/latest/variable/CMAKE_MODULE_PATH.html
Farway

1

มักจะทำเช่นนี้ได้อย่างไร ฉันควรคัดลอกcmake/ไดเรกทอรีของ SomeLib ไปยังโครงการของฉันและตั้งค่า CMAKE_MODULE_PATH ให้เป็นธรรม

หากคุณไม่ไว้วางใจ CMake ให้มีโมดูลนั้น - ใช่ทำเช่นนั้น - เรียงลำดับจาก:คัดลอกfind_SomeLib.cmakeและการอ้างอิงลงในcmake/ไดเรกทอรีนั่นคือสิ่งที่ฉันทำเป็นทางเลือก มันเป็นวิธีแก้ปัญหาที่น่าเกลียด

โปรดทราบว่าFindFoo.cmakeโมดูลแต่ละประเภทเป็นบริดจ์ระหว่างการพึ่งพาแพลตฟอร์มและแพลตฟอร์มอิสระ - พวกเขามองหาสถานที่เฉพาะแพลตฟอร์มต่าง ๆ เพื่อรับเส้นทางในตัวแปรที่มีชื่อเป็นอิสระจากแพลตฟอร์ม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.