Jakie zastosowanie ma find_package (), jeśli mimo wszystko musisz określić CMAKE_MODULE_PATH?

167

Próbuję uzyskać system kompilacji na różnych platformach działający przy użyciu CMake. Teraz oprogramowanie ma kilka zależności. Sam je skompilowałem i zainstalowałem w swoim systemie.

Przykładowe pliki, które zostały zainstalowane:

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

Teraz CMake ma, find_package()który otwiera Find*.cmakeplik i wyszukuje bibliotekę w systemie i definiuje niektóre zmienne, takie jak SomeLib_FOUNDitp.

Mój CMakeLists.txt zawiera coś takiego:

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

Pierwsze polecenie określa, gdzie CMake wyszukuje po Find*.cmakei dodałem katalog, w SomeLibktórym FindSomeLib.cmakemożna znaleźć, więc find_package()działa zgodnie z oczekiwaniami.

Ale jest to trochę dziwne, ponieważ jednym z powodów, dla których find_package()istnieje, jest ucieczka od niekrzyżowych ścieżek zakodowanych na sztywno.

Jak to się zwykle robi? Czy powinienem skopiować cmake/katalog SomeLibdo mojego projektu i ustawić CMAKE_MODULE_PATHrelatywnie?

MarcDefiant
źródło
Ten wzór wydaje mi się bardzo dziwny. Biblioteki używające CMake nie powinny w ten sposób ujawniać swojego modułu „find”. Jak wpadłeś na taki sposób, aby znaleźć „SomeLib”? A która to biblioteka?
SirDarius,
2
Coś podobnego zostało zrobione w cmake.org/Wiki/… . I to jest OGRE.
MarcDefiant,
2
Sekcja, do której prowadzi łącze, wspomina o tym: „Ponieważ CMake (obecnie) go nie dostarcza, należy go wysłać w ramach projektu”. Oto, co zrobiłem we flvmeta, aby znaleźć LibYAML (patrz github.com/noirotm/flvmeta/tree/master/cmake/modules ). Ścieżka modułu wskazuje na ten katalog wewnątrz mojego projektu.
SirDarius,
3
Zwykle kopiuję moduły FindXXX do mojego projektu i ustawiam CMAKE_MODULE_PATH (jeśli tych modułów oczywiście nie ma w CMake), widziałem ten wzór wiele razy w innych projektach
szx

Odpowiedzi:

213

Polecenie find_packagema dwa tryby: Moduletryb i Configtryb. Próbujesz użyć Moduletrybu, kiedy faktycznie potrzebujesz Configtrybu.

Tryb modułu

Find<package>.cmakeplik znajdujący się w Twoim projekcie. Coś takiego:

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

CMakeLists.txt zadowolony:

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

Zwróć uwagę, że CMAKE_MODULE_PATHma to wysoki priorytet i może być przydatne, gdy musisz przepisać standardowy Find<package>.cmakeplik.

Tryb konfiguracji (instalacja)

<package>Config.cmakeplik znajdujący się na zewnątrz i utworzony na install polecenie innego projektu ( Foona przykład).

foo biblioteka:

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

Uproszczona wersja pliku konfiguracyjnego:

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

Domyślnie projekt instalowany w CMAKE_INSTALL_PREFIXkatalogu:

> 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

Tryb konfiguracji (użyj)

Służy find_package(... CONFIG)do dołączania FooConfig.cmakedo zaimportowanego celu 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

Zwróć uwagę, że importowany cel jest wysoce konfigurowalny. Zobacz moją odpowiedź .

Aktualizacja

Społeczność
źródło
1
Twoja odpowiedź jest świetna. Jednak przykład na github jest bardziej złożony, niż IMO. W typowym przypadku, gdy podkatalog (moduł) eksportuje pojedynczy artefakt, powiedzmy bibliotekę wraz z nagłówkami, nie musisz generować niestandardowego * Config.cmake. W rezultacie konfiguracja może zostać znacznie zmniejszona. Myślę, że sam zrobię podobny przykład.
Dimitris,
2
@Dimitris Tak, można to trochę uprościć. Zaktualizowałem przykład github, więc teraz nie używa configure_package_config_file. Przy okazji, jeśli masz inne sugestie, możesz wysłać mi prośbę o ściągnięcie.
1
@rusio Oto mój przykład . Obsługuje kompilację monolityczną (wszystkie moduły z folderu głównego) lub kompilację autonomiczną (każdy moduł osobno wymaga instalacji).
Dimitris
1
@Dimitris OK, teraz widzę. Zwykle plik, który „optymalizujesz”, służy do ładowania dodatkowych rzeczy, takich jak find_dependency . Myślę, że to dobry szablon na początek, więc zachowam go, nawet jeśli nie jest używany. Reszta kodu wygląda na prostszą, ponieważ brakuje Ci niektórych funkcji, takich jak wersja, eksport dla dll, układ z bin/lib(spróbuj zainstalować plik wykonywalny i uruchomić go w systemie Windows). Przestrzenie nazw wyglądają bardzo ładnie, więc też je zachowam :) Dodałem też monolithicbuild.
1
Każdy z twoich przykładów był dla mnie bardzo pomocny. Dziękuję wam obu!
zmb
2

Jeśli pracujesz, cmakeaby wygenerować SomeLibsiebie (powiedzmy jako część superkonstrukcji), rozważ użycie rejestru pakietów użytkowników . Nie wymaga to zakodowanych ścieżek i jest wieloplatformowe. W systemie Windows (w tym mingw64) działa za pośrednictwem rejestru. Jeśli przyjrzysz się, w jaki sposób lista prefiksów instalacyjnych jest tworzona przez CONFIGtryb polecenia find_packages () , zobaczysz, że jednym z elementów jest Rejestr pakietów użytkownika.

Krótkie instrukcje

Skojarz cele SomeLib, których potrzebujesz, poza tym zewnętrznym projektem, dodając je do zestawu eksportu w CMakeLists.txtplikach, w których zostały utworzone:

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

Utwórz XXXConfig.cmakeplik dla SomeLibjego ${CMAKE_CURRENT_BUILD_DIR}i zapisz tę lokalizację w Rejestrze pakietu użytkownika, dodając dwa wywołania export () do CMakeLists.txtpowiązanego z SomeLib:

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

Wydaj find_package(SomeLib REQUIRED)komendę w CMakeLists.txtpliku projektu, od którego zależy, SomeLibbez "niezakodowanych na stałe ścieżek nie międzyplatformowych" majstrowania przy rozszerzeniu CMAKE_MODULE_PATH.

Kiedy to może być właściwe podejście

To podejście jest prawdopodobnie najbardziej odpowiednie w sytuacjach, w których nigdy nie będziesz używać oprogramowania poniżej katalogu kompilacji (np. Kompilujesz krzyżowo i nigdy nie instalujesz niczego na swoim komputerze lub tworzysz oprogramowanie tylko po to, aby uruchomić testy w katalog budowania), ponieważ tworzy dowiązanie do pliku .cmake w wyjściu "build", które może być tymczasowe.

Ale jeśli w rzeczywistości nigdy nie instalujesz SomeLibw swoim przepływie pracy, wywołanie EXPORT(PACKAGE <name>)pozwala uniknąć zakodowanej ścieżki. I oczywiście, jeśli instalujesz SomeLib, prawdopodobnie znasz swoją platformę CMAKE_MODULE_PATHitp., Więc doskonała odpowiedź @ user2288008 będzie Cię obejmować.

Ryan Feeley
źródło
1

Nie musisz określać ścieżki modułu jako takiej. CMake jest dostarczany z własnym zestawem wbudowanych skryptów find_package, a ich lokalizacja to domyślna CMAKE_MODULE_PATH.

Bardziej normalnym przypadkiem użycia dla projektów zależnych, które zostały CMakeified, byłoby użycie polecenia external_project CMake, a następnie dołączenie pliku Use [Project] .cmake z podprojektu. Jeśli potrzebujesz tylko skryptu Find [Project] .cmake, skopiuj go z podprojektu i do kodu źródłowego własnego projektu, a wtedy nie będziesz musiał rozszerzać CMAKE_MODULE_PATH, aby znaleźć podprojekt na poziomie systemu.

zjm555
źródło
12
their location is in the default CMAKE_MODULE_PATHdomyślnie CMAKE_MODULE_PATHjest pusty
Może potwierdzić komentarz @ user2288008 w 2018 r. CMAKE_MODULE_PATHJest pusty w systemie Windows.
Jeroen
Jest to zmienna specyficzna dla projektu, przeznaczona dla modułów wysyłanych wraz z projektem. „Domyślnie jest pusty, ma być ustawiony przez projekt”. cmake.org/cmake/help/latest/variable/CMAKE_MODULE_PATH.html
Farway
1

Jak to się zwykle robi? Czy powinienem skopiować cmake/katalog SomeLib do mojego projektu i ustawić względnie CMAKE_MODULE_PATH?

Jeśli nie ufasz, że CMake ma ten moduł, to - tak, zrób to - w pewnym sensie: skopiuj find_SomeLib.cmakei jego zależności do swojegocmake/ katalogu. To właśnie robię jako pomoc. To jednak brzydkie rozwiązanie.

Zauważ, że FindFoo.cmakekażdy z modułów jest rodzajem pomostu między zależnością platformy a niezależnością platformy - szukają w różnych miejscach specyficznych dla platformy, aby uzyskać ścieżki w zmiennych, których nazwy są niezależne od platformy.

einpoklum
źródło