Was nützt find_package (), wenn Sie CMAKE_MODULE_PATH trotzdem angeben müssen?

167

Ich versuche, ein plattformübergreifendes Build-System mit CMake zum Laufen zu bringen. Jetzt hat die Software einige Abhängigkeiten. Ich habe sie selbst kompiliert und auf meinem System installiert.

Einige Beispieldateien, die installiert wurden:

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

Jetzt hat CMake eine, find_package()die eine Find*.cmakeDatei öffnet und nach der Bibliothek im System sucht und einige Variablen wie SomeLib_FOUNDusw. definiert .

Meine CMakeLists.txt enthält ungefähr Folgendes:

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

Der erste Befehl definiert, wo CMake nach dem sucht, Find*.cmakeund ich habe das Verzeichnis hinzugefügt, SomeLibin dem FindSomeLib.cmakedas gefunden werden kann, damit es find_package()wie erwartet funktioniert.

Dies ist jedoch etwas seltsam, da einer der Gründe dafür darin find_package()besteht, sich von nicht codierungsübergreifenden, hartcodierten Pfaden zu entfernen.

Wie wird das normalerweise gemacht? Soll ich das cmake/Verzeichnis von SomeLibin mein Projekt kopieren und das CMAKE_MODULE_PATHrelativ einstellen ?

MarcDefiant
quelle
Dieses Muster erscheint mir sehr seltsam. Bibliotheken, die CMake verwenden, dürfen ihr Suchmodul nicht auf diese Weise verfügbar machen. Wie sind Sie auf diese Weise auf die Suche nach "SomeLib" gekommen? Und welche Bibliothek ist das?
SirDarius
2
Ähnliches geschieht in cmake.org/Wiki/… . Und es ist OGRE.
MarcDefiant
2
In dem Abschnitt, auf den Sie verlinken, wird Folgendes erwähnt: "Da CMake es (derzeit) nicht versendet, müssen Sie es innerhalb Ihres Projekts versenden." Dies habe ich in flvmeta getan, um LibYAML zu finden (siehe github.com/noirotm/flvmeta/tree/master/cmake/modules ). Der Modulpfad zeigt auf dieses Verzeichnis in meinem Projekt.
SirDarius
3
Normalerweise kopiere ich FindXXX-Module in mein Projekt und setze CMAKE_MODULE_PATH (wenn diese Module natürlich nicht in CMake vorhanden sind). Ich habe dieses Muster auch oft in anderen Projekten gesehen
szx

Antworten:

213

Der Befehl find_packagehat zwei Modi: ModuleModus und ConfigModus. Sie versuchen, den ModuleModus zu verwenden , wenn Sie den ConfigModus tatsächlich benötigen .

Modulmodus

Find<package>.cmakeDatei in Ihrem Projekt. Etwas wie das:

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

CMakeLists.txt Inhalt:

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

Beachten Sie, dass dies CMAKE_MODULE_PATHeine hohe Priorität hat und nützlich sein kann, wenn Sie eine Standarddatei neu schreiben müssen Find<package>.cmake.

Konfigurationsmodus (installieren)

<package>Config.cmakeDatei befindet sich außerhalb und wird auf install Befehl eines anderen Projekts erstellt ( Foozum Beispiel).

foo Bibliothek:

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

Vereinfachte Version der Konfigurationsdatei:

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

Standardmäßig im CMAKE_INSTALL_PREFIXVerzeichnis installiertes Projekt :

> 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

Konfigurationsmodus (verwenden)

Verwenden Sie find_package(... CONFIG)diese Option , um das FooConfig.cmakeimportierte Ziel einzuschließen 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

Beachten Sie, dass das importierte Ziel in hohem Maße konfigurierbar ist. Siehe meine Antwort .

Aktualisieren

Gemeinschaft
quelle
1
Ihre Antwort ist großartig. Das Beispiel bei github ist jedoch komplexer als IMO. In dem allgemeinen Fall, in dem ein Unterverzeichnis (Modul) ein einzelnes Artefakt exportiert, beispielsweise eine Bibliothek zusammen mit den Headern, müssen Sie keine benutzerdefinierte * Config.cmake generieren. Dadurch kann die Konfiguration erheblich reduziert werden. Ich denke, ich werde selbst ein ähnliches Beispiel machen.
Dimitris
2
@ Dimitris Ja, es kann ein wenig vereinfacht werden. Ich habe das Github-Beispiel aktualisiert, sodass es jetzt nicht mehr verwendet wird configure_package_config_file. Übrigens, wenn Sie weitere Vorschläge haben, können Sie mir eine Pull-Anfrage senden.
1
@rusio Hier ist mein Beispiel . Es unterstützt monolithische Builds (alle Module aus dem Stammordner) oder autonome Builds (jedes Modul separat, muss installiert werden).
Dimitris
1
@ Dimitris Okay, jetzt verstehe ich. Normalerweise dient die Datei, die Sie " wegoptimieren ", zum Laden zusätzlicher Inhalte wie find_dependency . Ich denke, es ist eine gute Vorlage, um zu beginnen, also werde ich sie behalten, auch wenn sie tatsächlich nicht verwendet wird. Der Rest des Codes sieht einfacher aus, da Ihnen einige Funktionen wie Version, Export für DLL, Layout mit fehlen bin/lib(versuchen Sie, die ausführbare Datei zu installieren und unter Windows auszuführen). Und Namespaces sehen sehr hübsch aus, also werde ich sie auch behalten :) Außerdem habe ich monolithicBuild hinzugefügt .
1
Jedes Ihrer Beispiele war für mich sehr hilfreich. Danke euch beiden!
Zmb
2

Wenn Sie sich selbst cmakegenerieren SomeLibmöchten (z. B. als Teil eines Superbuilds), sollten Sie die Benutzerpaketregistrierung verwenden . Dies erfordert keine fest codierten Pfade und ist plattformübergreifend. Unter Windows (einschließlich mingw64) funktioniert es über die Registrierung. Wenn Sie untersuchen, wie die Liste der Installationspräfixe im CONFIGModus des Befehls find_packages () erstellt wird , werden Sie feststellen , dass die Benutzerpaketregistrierung eines der Elemente ist.

Kurze Anleitung

Verknüpfen Sie die Ziele SomeLib, die Sie außerhalb dieses externen Projekts benötigen, indem Sie sie einem Exportsatz in den CMakeLists.txtDateien hinzufügen, in denen sie erstellt werden:

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

Erstellen Sie eine XXXConfig.cmakeDatei für die SomeLibin seinem ${CMAKE_CURRENT_BUILD_DIR}und speichert diese Position im Benutzerpaket Registry , indem Sie zwei Anrufe zu exportieren () auf den im CMakeLists.txtZusammenhang mit 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)Geben Sie Ihren Befehl in der CMakeLists.txtDatei des Projekts aus, die davon abhängt, SomeLibohne dass die "plattformübergreifenden fest codierten Pfade" an der Datei basteln CMAKE_MODULE_PATH.

Wenn es der richtige Ansatz sein könnte

Dieser Ansatz eignet sich wahrscheinlich am besten für Situationen, in denen Sie Ihre Software niemals nach dem Build-Verzeichnis verwenden (z. B. wenn Sie Cross-Compilieren und niemals etwas auf Ihrem Computer installieren oder wenn Sie die Software nur zum Ausführen von Tests erstellen das Build-Verzeichnis), da es einen Link zu einer .cmake-Datei in Ihrer "Build" -Ausgabe erstellt, die vorübergehend sein kann.

Wenn Sie jedoch nie SomeLibin Ihrem Workflow installieren , EXPORT(PACKAGE <name>)können Sie durch Aufrufen den fest codierten Pfad vermeiden. Und natürlich kennen Sie bei der Installation SomeLibwahrscheinlich Ihre Plattform CMAKE_MODULE_PATHusw., sodass Sie mit der hervorragenden Antwort von @ user2288008 behandelt werden.

Ryan Feeley
quelle
1

Sie nicht brauchen das Modul Pfad per se zu spezifizieren. CMake wird mit einem eigenen Satz integrierter find_package-Skripts ausgeliefert. Der Speicherort befindet sich im Standard-CMAKE_MODULE_PATH.

Der normalere Anwendungsfall für abhängige Projekte, die CMakeified wurden, besteht darin, den Befehl external_project von CMake zu verwenden und dann die Cake-Datei Use [Project] aus dem Unterprojekt einzuschließen. Wenn Sie nur das Skript "Find [Project] .cmake" benötigen, kopieren Sie es aus dem Teilprojekt in den Quellcode Ihres eigenen Projekts, und Sie müssen CMAKE_MODULE_PATH nicht erweitern, um das Teilprojekt auf Systemebene zu finden.

zjm555
quelle
12
their location is in the default CMAKE_MODULE_PATHStandardmäßig CMAKE_MODULE_PATHist leer
Kann den Kommentar von @ user2288008 im Jahr 2018 bestätigen. Ist unter CMAKE_MODULE_PATHWindows leer.
Jeroen
Es ist eine projektspezifische Variable für Module, die mit Ihrem Projekt geliefert werden. "Standardmäßig ist es leer und soll vom Projekt festgelegt werden." cmake.org/cmake/help/latest/variable/CMAKE_MODULE_PATH.html
Farway
1

Wie wird das normalerweise gemacht? Sollte ich das cmake/Verzeichnis von SomeLib in mein Projekt kopieren und CMAKE_MODULE_PATH relativ einstellen?

Wenn Sie CMake nicht vertrauen, dass es über dieses Modul verfügt , gehen Sie wie folgt vor: Kopieren Sie das find_SomeLib.cmakeund seine Abhängigkeiten in Ihr cmake/Verzeichnis. Das mache ich als Fallback. Es ist jedoch eine hässliche Lösung.

Beachten Sie, dass die FindFoo.cmakeModule jeweils eine Art Brücke zwischen Plattformabhängigkeit und Plattformunabhängigkeit darstellen. Sie suchen an verschiedenen plattformspezifischen Stellen nach Pfaden in Variablen, deren Namen plattformunabhängig sind.

einpoklum
quelle