CMake Link zur externen Bibliothek

125

Wie kann CMake dazu gebracht werden, eine ausführbare Datei mit einer externen gemeinsam genutzten Bibliothek zu verknüpfen, die nicht im selben CMake-Projekt erstellt wurde?

Nur zu tun target_link_libraries(GLBall ${CMAKE_BINARY_DIR}/res/mylib.so)gibt den Fehler

make[2]: *** No rule to make target `res/mylib.so', needed by `GLBall'.  Stop.
make[1]: *** [CMakeFiles/GLBall.dir/all] Error 2
make: *** [all] Error 2
(GLBall is the executable)

nachdem ich die Bibliothek in das Binärverzeichnis kopiert habe bin/res.

Ich habe es versucht find_library(RESULT mylib.so PATHS ${CMAKE_BINARY_DIR}/res)

Was mit scheitert RESULT-NOTFOUND.

Prime
quelle

Antworten:

100

Legen Sie zuerst den Suchpfad für Bibliotheken fest:

LINK_DIRECTORIES(${CMAKE_BINARY_DIR}/res)

Und dann einfach tun

TARGET_LINK_LIBRARIES(GLBall mylib)
Menge
quelle
42
Von der Verwendung link_directorieswird auch in der eigenen Dokumentation abgeraten. Ich denke, es wäre hier besser, den fehlgeschlagenen find_libraryAnruf in der ursprünglichen Frage zu beheben oder die Lösung von @ Andre zu verwenden.
Fraser
4
Ich finde das "importierte" Bibliotheksziel robuster, da es auf den Speicherort der jeweiligen Bibliothek abzielt und stattdessen einfach einen globalen Suchpfad angibt. Siehe Andres Antwort.
Mark Lakata
Sie sollten find_librarydiesen Pfad immer verwenden und verwenden, anstatt ihn fest zu codieren, vgl. meine Antwort .
usr1234567
120

Die Antwort von arrowdodger ist richtig und wird bei vielen Gelegenheiten bevorzugt. Ich möchte einfach eine Alternative zu seiner Antwort hinzufügen:

Sie können anstelle eines Linkverzeichnisses ein "importiertes" Bibliotheksziel hinzufügen. Etwas wie:

# Your-external "mylib", add GLOBAL if the imported library is located in directories above the current.
add_library( mylib SHARED IMPORTED )
# You can define two import-locations: one for debug and one for release.
set_target_properties( mylib PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/res/mylib.so )

Und dann verlinken, als ob diese Bibliothek von Ihrem Projekt erstellt wurde:

TARGET_LINK_LIBRARIES(GLBall mylib)

Ein solcher Ansatz würde Ihnen etwas mehr Flexibilität geben: Sehen Sie sich den Befehl add_library () und die vielen Zieleigenschaften an, die sich auf importierte Bibliotheken beziehen .

Ich weiß nicht, ob dies Ihr Problem mit "aktualisierten Versionen von Bibliotheken" lösen wird.

André
quelle
2
Das wäre wahrscheinlich add_library( mylib SHARED IMPORTED )oder Sie bekommen einen add_library called with IMPORTED argument but no library typeFehler
Marvin
4
@ Andrew: Ich denke, nachdem IMPORTED_LOCATIONdie öffnende Klammer falsch ist
Ela782
5
Sie müssen hinzufügen GLOBALnachdem , IMPORTEDob Sie die importierte Bibliothek in Verzeichnisse über dem aktuellen zugreifen möchten:add_library(breakpad STATIC IMPORTED GLOBAL)
Roman Kruglov
@ Andre IMPORTED_LOCATION scheint den Pfad zur Datei anstelle des Verzeichnisses zu erfordern, das die Datei enthält
SOUser
1
@SOUser: Ja, IMPORTED_LOCATION sollte auf die Datei und nicht auf das Verzeichnis verweisen. Ich habe das behoben, denke, der Autor wird sich nicht beschweren.
Tsyvarev
63

Ich gehe davon aus, dass Sie eine Verknüpfung zu einer Bibliothek namens foo herstellen möchten. Der Dateiname ist normalerweise ein Link foo.dlloder libfoo.so.

1. Bibliothek
suchen Sie müssen die Bibliothek finden. Dies ist eine gute Idee, auch wenn Sie den Pfad zu Ihrer Bibliothek kennen. CMake wird einen Fehler ausgeben, wenn die Bibliothek verschwunden ist oder einen neuen Namen erhalten hat. Dies hilft, Fehler frühzeitig zu erkennen und dem Benutzer (möglicherweise selbst) klar zu machen, was ein Problem verursacht.
Um eine Bibliothek zu finden foo und speichern Sie den Pfad in FOO_LIBGebrauch

    find_library(FOO_LIB foo)

CMake wird selbst herausfinden, wie der tatsächliche Dateiname lautet. Es prüft die üblichen Orten wie /usr/lib, /usr/lib64und die Wege in PATH.

Sie kennen bereits den Standort Ihrer Bibliothek. Fügen Sie es dem hinzu, CMAKE_PREFIX_PATHwenn Sie CMake aufrufen. CMake sucht dann auch in den übergebenen Pfaden nach Ihrer Bibliothek.

Manchmal müssen Sie Hinweise oder Pfadsuffixe hinzufügen. Weitere Informationen finden Sie in der Dokumentation: https://cmake.org/cmake/help/latest/command/find_library.html

2. Verknüpfen Sie die Bibliothek Ab 1. Sie haben den vollständigen Bibliotheksnamen in FOO_LIB. Sie verwenden dies, um die Bibliothek GLBallwie in mit Ihrem Ziel zu verknüpfen

  target_link_libraries(GLBall PRIVATE "${FOO_LIB}")

Sie sollten hinzufügen PRIVATE, PUBLICoder INTERFACEnach dem Ziel, vgl die Dokumentation: https://cmake.org/cmake/help/latest/command/target_link_libraries.html

Wenn Sie keinen dieser Sichtbarkeitsspezifizierer hinzufügen, verhält sich dieser entweder wie PRIVATEoder PUBLIC, abhängig von der CMake-Version und den festgelegten Richtlinien.

3. Includes hinzufügen (Dieser Schritt ist möglicherweise nicht obligatorisch.)
Wenn Sie auch Header-Dateien einschließen möchten, verwenden Sie find_pathähnlich wie find_libraryund suchen Sie nach einer Header-Datei. Fügen Sie dann das Include-Verzeichnis mit target_include_directoriesähnlichem hinzu target_link_libraries.

Dokumentation: https://cmake.org/cmake/help/latest/command/find_path.html und https://cmake.org/cmake/help/latest/command/target_include_directories.html

Wenn für die externe Software verfügbar, können Sie diese ersetzen find_libraryund find_pathdurch find_package.

usr1234567
quelle
3
IMHO ist dies die beste Antwort. Ich hatte jedoch Probleme, weil ich nach "project" nicht "find_library" und nach "add_executable" "target_link_libraries" aufrief.
Smoothware
1
find_packageist so viel einfacher als diese Schritte zu
befolgen
2
Ich glaube, ich verstehe Schritt 2 nicht. Für eine gemeinsam genutzte Bibliothek ist $ {FOO_LIB} wie /full/path/to/libfoo.dylib. Wie ist das nützlich? target_link_libraries erstellt kein "-L / full / path / to -lfoo", daher gibt find_library nichts Nützliches zurück, außer zu überprüfen, ob sich die Bibliothek an dem Ort befindet, an dem ich sie bereits kenne. Was vermisse ich?
Guymac
target_link_libraries(mylib "${FOO_LIB}")? Das Ziel ist mylibanstelle seines eigentlichen Ziels GLBall? macht für mich nicht viel Sinn
Bersan
5

Eine weitere Alternative, falls Sie mit dem Appstore arbeiten, benötigen "Berechtigungen" und müssen daher mit einem Apple-Framework verknüpft werden.

Damit Berechtigungen funktionieren (z. B. GameCenter), müssen Sie einen "Link Binary with Libraries" -Bildstep haben und dann mit "GameKit.framework" verknüpfen. CMake "injiziert" die Bibliotheken auf einer "niedrigen Ebene" in die Befehlszeile, daher weiß Xcode nicht wirklich davon, und als solches wird GameKit im Bildschirm "Funktionen" nicht aktiviert.

Eine Möglichkeit, CMake zu verwenden und einen "Link with Binaries" -Bildstep zu erstellen, besteht darin, das xcodeproj mit CMake zu generieren und dann mit 'sed' zu suchen und zu ersetzen und das GameKit so hinzuzufügen, wie es XCode gefällt ...

Das Skript sieht folgendermaßen aus (für Xcode 6.3.1).

s#\/\* Begin PBXBuildFile section \*\/#\/\* Begin PBXBuildFile section \*\/\
    26B12AA11C10544700A9A2BA \/\* GameKit.framework in Frameworks \*\/ = {isa = PBXBuildFile; fileRef = 26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/; };#g

s#\/\* Begin PBXFileReference section \*\/#\/\* Begin PBXFileReference section \*\/\
    26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameKit.framework; path = System\/Library\/Frameworks\/GameKit.framework; sourceTree = SDKROOT; };#g

s#\/\* End PBXFileReference section \*\/#\/\* End PBXFileReference section \*\/\
\
\/\* Begin PBXFrameworksBuildPhase section \*\/\
    26B12A9F1C10543B00A9A2BA \/\* Frameworks \*\/ = {\
        isa = PBXFrameworksBuildPhase;\
        buildActionMask = 2147483647;\
        files = (\
            26B12AA11C10544700A9A2BA \/\* GameKit.framework in Frameworks xxx\*\/,\
        );\
        runOnlyForDeploymentPostprocessing = 0;\
    };\
\/\* End PBXFrameworksBuildPhase section \*\/\
#g

s#\/\* CMake PostBuild Rules \*\/,#\/\* CMake PostBuild Rules \*\/,\
            26B12A9F1C10543B00A9A2BA \/\* Frameworks xxx\*\/,#g
s#\/\* Products \*\/,#\/\* Products \*\/,\
            26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/,#g

speichere dies in "gamecenter.sed" und "wende" es dann so an (es ändert dein xcodeproj!)

sed -i.pbxprojbak -f gamecenter.sed myproject.xcodeproj/project.pbxproj

Möglicherweise müssen Sie die Skriptbefehle ändern, um sie Ihren Anforderungen anzupassen.

Warnung: Es ist wahrscheinlich, dass es mit einer anderen Xcode-Version bricht, da sich das Projektformat ändern kann, die (fest codierte) eindeutige Nummer möglicherweise nicht wirklich eindeutig ist - und im Allgemeinen sind die Lösungen anderer Personen besser -, es sei denn, Sie müssen den Appstore + unterstützen Berechtigungen (und automatisierte Builds) tun dies nicht.

Dies ist ein CMake-Fehler, siehe http://cmake.org/Bug/view.php?id=14185 und http://gitlab.kitware.com/cmake/cmake/issues/14185

Kalmiya
quelle
Insbesondere ist es nicht das Problem, cmake mit einer externen Bibliothek zu verknüpfen (oben gibt es mehrere Lösungen). Es ist eine Herausforderung , dies automatisiert zum Laufen zu bringen, damit es mit dem Apple Appstore und den Berechtigungen funktioniert . In diesem speziellen Fall funktionieren die oben genannten Lösungen nicht, da XCode die auf diese Weise verknüpften Bibliotheken nicht "sieht" und Berechtigungen einfach nicht funktionieren. Afaik cmake kann die Bibliotheken nicht so hinzufügen, wie es xcode benötigt, und zwar auf Appstore-kompatible Weise. Sie können mich auch hier aufklären.
Kalmiya
1
Oh, das ist traurig. Der Vollständigkeit halber der Link zum neuen Issue-Tracker, der derzeit keine Commnets enthält: gitlab.kitware.com/cmake/cmake/issues/14185
usr1234567
Das Problem wurde vor 5 Monaten behoben, sodass es mit einer neueren Version von CMake nicht mehr vorhanden sein sollte. Siehe gitlab.kitware.com/cmake/cmake/issues/14185
usr1234567