Apa gunanya find_package () jika Anda harus menentukan CMAKE_MODULE_PATH?

167

Saya mencoba agar sistem build lintas-plattform bekerja menggunakan CMake. Sekarang perangkat lunak memiliki beberapa dependensi. Saya mengkompilasinya sendiri dan menginstalnya di sistem saya.

Beberapa contoh file yang terinstal:

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

Sekarang CMake memiliki find_package()yang membuka Find*.cmakefile dan mencari pustaka pada sistem dan mendefinisikan beberapa variabel seperti SomeLib_FOUNDdll.

CMakeLists.txt saya berisi sesuatu seperti ini:

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

Perintah pertama menentukan di mana CMake mencari Find*.cmakedan saya menambahkan direktori di SomeLibmana FindSomeLib.cmakedapat ditemukan, jadi find_package()berfungsi seperti yang diharapkan.

Tapi ini agak aneh karena salah satu alasan mengapa find_package()ada adalah untuk menjauh dari jalur kode keras non-cross-plattform.

Bagaimana ini biasanya dilakukan? Haruskah saya menyalin cmake/direktori SomeLibke dalam proyek saya dan mengatur CMAKE_MODULE_PATHrelatif?

MarcDefiant
sumber
Pola itu tampaknya sangat aneh bagi saya. Perpustakaan yang menggunakan CMake tidak seharusnya mengekspos modul 'find' mereka dengan cara ini. Bagaimana Anda menemukan cara untuk menemukan "SomeLib" itu? Dan lib yang mana?
SirDarius
2
Hal serupa dilakukan di cmake.org/Wiki/… . Dan itu bagus.
MarcDefiant
2
Bagian yang Anda tautkan dengan menyebutkan ini: "Karena CMake (saat ini) tidak mengirimkannya, Anda harus mengirimkannya dalam proyek Anda." Inilah yang telah saya lakukan di flvmeta untuk menemukan LibYAML (lihat github.com/noirotm/flvmeta/tree/master/cmake/modules ). Jalur modul menunjuk ke direktori ini, di dalam proyek saya.
SirDarius
3
Saya biasanya menyalin modul FindXXX ke proyek saya dan mengatur CMAKE_MODULE_PATH (jika modul-modul itu tidak ada di CMake tentu saja), saya juga telah melihat pola ini berkali-kali di proyek lain
szx

Jawaban:

214

Perintah find_packagememiliki dua mode: Modulemode dan Configmode. Anda mencoba menggunakan Modulemode ketika Anda benar-benar membutuhkan Configmode.

Mode modul

Find<package>.cmakefile yang terletak di dalam proyek Anda. Sesuatu seperti ini:

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

CMakeLists.txt kandungan:

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

Catatan yang CMAKE_MODULE_PATHmemiliki prioritas tinggi dan mungkin berguna ketika Anda perlu menulis ulang Find<package>.cmakefile standar .

Mode konfigurasi (instal)

<package>Config.cmakefile yang terletak di luar dan diproduksi oleh install perintah proyek lain ( Foomisalnya).

foo Perpustakaan:

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

Versi file konfigurasi yang disederhanakan:

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

Secara default proyek diinstal dalam CMAKE_INSTALL_PREFIXdirektori:

> 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

Mode konfigurasi (gunakan)

Gunakan find_package(... CONFIG)untuk memasukkan FooConfig.cmakedengan target yang diimpor 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

Perhatikan bahwa target yang diimpor sangat dapat dikonfigurasi. Lihat jawaban saya .

Memperbarui

Komunitas
sumber
1
Jawaban Anda bagus. Namun, contoh di github lebih kompleks daripada IMO. Dalam kasus umum di mana subdirektori (modul) mengekspor artefak tunggal, misalkan lib bersama dengan header, Anda tidak perlu membuat custom * Config.cmake. Akibatnya konfigurasi dapat dikurangi secara signifikan. Saya pikir saya akan membuat contoh serupa sendiri.
Dimitris
2
@ Dimitris Ya, itu bisa disederhanakan sedikit. Saya telah memperbarui contoh github jadi sekarang tidak digunakan configure_package_config_file. Omong-omong, jika Anda memiliki saran lain, Anda dapat mengirim saya permintaan tarik.
1
@rusio Ini contoh saya . Ini mendukung build monolitik (semua modul dari folder root) atau build otonom (setiap modul secara terpisah, memerlukan instalasi).
Dimitris
1
@ Dimitris Oke, sekarang saya mengerti. Biasanya file yang Anda "optimalkan" melayani untuk memuat hal-hal tambahan seperti find_dependency . Saya pikir ini adalah template yang bagus untuk memulai jadi saya akan menyimpannya meskipun sebenarnya tidak digunakan. Sisa kode terlihat lebih sederhana karena Anda kehilangan beberapa fungsi seperti versi, ekspor untuk dll, tata letak dengan bin/lib(coba instal yang dapat dieksekusi dan jalankan di windows). Dan ruang nama terlihat sangat cantik, jadi saya akan menyimpannya juga :) Saya juga menambahkan monolithicbuild.
1
Setiap contoh Anda sangat membantu saya. Terima kasih semuanya!
zmb
2

Jika Anda menjalankan cmakeuntuk menghasilkan SomeLibdiri sendiri (katakanlah sebagai bagian dari superbuild), pertimbangkan untuk menggunakan Registry Paket Pengguna . Ini tidak memerlukan jalur kode keras dan merupakan lintas-platform. Pada Windows (termasuk mingw64) berfungsi melalui registri. Jika Anda memeriksa bagaimana daftar awalan pemasangan dibangun oleh CONFIGmode perintah find_packages () , Anda akan melihat bahwa Registry Paket Pengguna adalah salah satu elemen.

Petunjuk singkat

Kaitkan target SomeLibyang Anda butuhkan di luar proyek eksternal dengan menambahkannya ke set ekspor di CMakeLists.txtfile di mana mereka dibuat:

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

Buat XXXConfig.cmakefile untuk SomeLibdi dalamnya ${CMAKE_CURRENT_BUILD_DIR}dan simpan lokasi ini di Registry Paket Pengguna dengan menambahkan dua panggilan untuk mengekspor () ke yang CMakeLists.txtterkait dengan SomeLib:

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

Keluarkan find_package(SomeLib REQUIRED)commmand Anda dalam CMakeLists.txtfile proyek yang tergantung SomeLibtanpa "non-cross-platform hard coded paths" CMAKE_MODULE_PATH.

Saat itu mungkin pendekatan yang tepat

Pendekatan ini mungkin paling cocok untuk situasi di mana Anda tidak akan pernah menggunakan perangkat lunak hilir dari direktori build (misalnya, Anda melakukan kompilasi silang dan tidak pernah menginstal apa pun pada mesin Anda, atau Anda sedang membangun perangkat lunak hanya untuk menjalankan tes di direktori build), karena ia membuat tautan ke file .cmake di output "build" Anda, yang mungkin bersifat sementara.

Tetapi jika Anda tidak pernah benar-benar menginstal SomeLibdi alur kerja Anda, panggilan EXPORT(PACKAGE <name>)memungkinkan Anda untuk menghindari jalur kode keras. Dan, tentu saja, jika Anda menginstal SomeLib, Anda mungkin tahu platform Anda CMAKE_MODULE_PATH,, dll, jadi jawaban yang sangat baik dari @ user2288008 akan membantu Anda.

Ryan Feeley
sumber
1

Anda tidak perlu menentukan jalur modul per se. CMake mengirim dengan skrip find_package bawaannya sendiri, dan lokasinya berada di CMAKE_MODULE_PATH default.

Kasus penggunaan yang lebih normal untuk proyek dependen yang telah CMakeified akan menggunakan perintah external_project CMake dan kemudian memasukkan file Use [Project] .cmake dari subproyek. Jika Anda hanya memerlukan skrip Find [Project] .cmake, salinlah dari sub proyek dan ke dalam kode sumber proyek Anda sendiri, dan kemudian Anda tidak perlu menambah CMAKE_MODULE_PATH untuk menemukan sub proyek di tingkat sistem.

zjm555
sumber
12
their location is in the default CMAKE_MODULE_PATHsecara default CMAKE_MODULE_PATHkosong
Dapat mengonfirmasi komentar @ user2288008 pada 2018. CMAKE_MODULE_PATHkosong pada Windows.
Jeroen
Ini adalah variabel spesifik proyek, untuk pengiriman modul dengan proyek Anda. "Secara default kosong, ini dimaksudkan untuk ditetapkan oleh proyek." cmake.org/cmake/help/latest/variable/CMAKE_MODULE_PATH.html
Farway
1

Bagaimana ini biasanya dilakukan? Haruskah saya menyalin cmake/direktori SomeLib ke proyek saya dan menetapkan CMAKE_MODULE_PATH relatif?

Jika Anda tidak percaya CMake memiliki modul itu, maka - ya, lakukan itu - semacam: Salin find_SomeLib.cmakedan ketergantungannya ke cmake/direktori Anda . Itulah yang saya lakukan sebagai mundur. Ini solusi yang jelek.

Perhatikan bahwa FindFoo.cmakemasing-masing modul merupakan semacam jembatan antara ketergantungan platform dan independensi platform - modul-modul tersebut mencari di berbagai tempat platform spesifik untuk mendapatkan jalur dalam variabel yang namanya independen terhadap platform.

einpoklum
sumber