Was ist der richtige Weg, um "pkg-config" von "cmake" zu verwenden?

81

Wenn ich mich im Internet umsehe, habe ich eine Menge Code wie diesen gesehen:

include(FindPkgConfig)
pkg_search_module(SDL2 REQUIRED sdl2)

target_include_directories(app SYSTEM PUBLIC ${SDL2_INCLUDE_DIRS}
target_link_libraries(app ${SDL2_LIBRARIES})

Dies scheint jedoch der falsche Weg zu sein, da nur die Include-Verzeichnisse und -Bibliotheken verwendet werden, aber definierte Definitionen, Bibliothekspfade und andere Flags, die von zurückgegeben werden könnten, ignoriert werden pkg-config.

Was wäre der richtige Weg, um sicherzustellen, dass alle von zurückgegebenen Kompilierungs- und Verknüpfungsflags pkg-configvon den Kompilierten verwendet werden app? Und gibt es einen einzigen Befehl, um dies zu erreichen, also so etwas wie target_use(app SDL2)?

ref:

Grumbel
quelle

Antworten:

28

Wenn Sie cmake und pkg-config auf ganz normale Weise verwenden, funktioniert diese Lösung.

Wenn Sie jedoch eine Bibliothek haben, die in einem Entwicklungsverzeichnis vorhanden ist (z. B. / home / me / hack / lib), können die Linkerpfade mit anderen hier gezeigten Methoden nicht konfiguriert werden. Bibliotheken, die sich nicht unter den typischen Installationsorten befinden, würden zu Linkerfehlern führen, wie z /usr/bin/ld: cannot find -lmy-hacking-library-1.0. Diese Lösung behebt den Linkerfehler für diesen Fall.

Ein weiteres Problem könnte sein, dass die pkg-config-Dateien nicht an der normalen Stelle installiert sind und die pkg-config-Pfade für das Projekt mithilfe der Umgebungsvariablen PKG_CONFIG_PATH hinzugefügt werden müssen, während cmake ausgeführt wird (siehe andere diesbezügliche Fragen zum Stapelüberlauf). Angenommen, Sie haben den richtigen pkg-config-Pfad eingerichtet, behebt diese Lösung auch dieses Problem.

Die Lösung läuft auf diese endgültige Version einer funktionierenden CMakeLists.txt hinaus:

cmake_minimum_required(VERSION 3.14)
project(ya-project C)

# the `pkg_check_modules` function is created with this call
find_package(PkgConfig REQUIRED) 

# these calls create special `PkgConfig::<MODULE>` variables
pkg_check_modules(MY_PKG REQUIRED IMPORTED_TARGET any-package)
pkg_check_modules(YOUR_PKG REQUIRED IMPORTED_TARGET ya-package)

add_executable(program-name file.c ya.c)

target_link_libraries(program-name PUBLIC
        PkgConfig::MY_PKG
        PkgConfig::YOUR_PKG)

Beachten Sie, dass target_link_librariesdies mehr als nur die Linker-Befehle ändert. Es werden auch andere PUBLIC-Eigenschaften bestimmter Ziele weitergegeben, z. B.: Compiler-Flags, Compiler-Definitionen, Include-Pfade usw.

activedecay
quelle
5
IMPORTED_TARGETerfordert CMake 3.6 oder neuer.
Cris Luengo
Wenn Sie dies abgelehnt haben, stellen Sie bitte sicher und kommentieren Sie, warum Sie abgelehnt haben, damit wir die Antwort verbessern können.
Activedecay
62

Zunächst der Anruf:

include(FindPkgConfig)

sollte ersetzt werden durch:

find_package(PkgConfig)

Der find_package()Anruf ist flexibler und ermöglicht Optionen wie REQUIRED, die Dinge automatisch erledigen, mit denen man manuell arbeiten müsste include().

Zweitens sollte ein manueller Anruf pkg-confignach Möglichkeit vermieden werden. CMake enthält eine Reihe von Paketdefinitionen, die unter Linux unter zu finden sind /usr/share/cmake-3.0/Modules/Find*cmake. Diese bieten dem Benutzer mehr Optionen und Auswahlmöglichkeiten als ein roher Anruf an pkg_search_module().

Was den erwähnten hypothetischen target_use()Befehl betrifft, hat CMake diesen bereits in gewisser Weise mit PUBLIC | PRIVATE | INTERFACE eingebaut. Ein Aufruf wie target_include_directories(mytarget PUBLIC ...)bewirkt, dass die Include-Verzeichnisse automatisch in jedem Ziel verwendet werden mytarget, das z target_link_libraries(myapp mytarget). Dieser Mechanismus scheint jedoch nur für Bibliotheken zu gelten, die in der CMakeLists.txtDatei erstellt wurden, und funktioniert nicht für Bibliotheken, die mit erworben wurden pkg_search_module(). Der Anruf add_library(bar SHARED IMPORTED)könnte dafür verwendet werden, aber ich habe das noch nicht untersucht.

Die Hauptfrage funktioniert hier in den meisten Fällen:

find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
...
target_link_libraries(testapp ${SDL2_LIBRARIES})
target_include_directories(testapp PUBLIC ${SDL2_INCLUDE_DIRS})
target_compile_options(testapp PUBLIC ${SDL2_CFLAGS_OTHER})

Das SDL2_CFLAGS_OTHERenthält Definitionen und andere Flags, die für eine erfolgreiche Kompilierung erforderlich sind. Die Flaggen SDL2_LIBRARY_DIRSund SDL2_LDFLAGS_OTHERsind aber immer noch ignoriert, keine Ahnung , wie oft , dass ein Problem werden würde.

Weitere Dokumentation hier http://www.cmake.org/cmake/help/v3.0/module/FindPkgConfig.html

Grumbel
quelle
4
Ich bin damit einverstanden , dass pkg-config sollte vermieden werden IF eine Suche * .cmake existiert, aber das ist noch nicht der Fall für die neueste Version von cmake in 2016.
Cubic
3
Dies funktioniert nicht, wenn sich die Bibliotheken nicht in den Standardverzeichnissen befinden. link_directories () kann eine Problemumgehung sein, ist jedoch global.
Henry Hu
Dieser Ansatz funktioniert nicht für vcpkg . Kann ich SDL2_image ohne fest codierte Pfade finden?
user2023370
@HenryHu könnten Sie zeigen, wie dies gemacht werden würde, wenn dies in der Konfiguration in dieser Antwort der Fall wäre?
Tim Visée
2
Es macht keinen Sinn, ein Build-Tool wie CMake zu benötigen, um Heuristiken zu bündeln, um jede Bibliothek auf der Welt zu durchsuchen. Es ist nicht ihre Rolle. Pkg-config ist so konzipiert, dass es in der Verantwortung des lib-Autors oder des pkg / distro-Betreuers liegt, es den Benutzern zur Verfügung zu stellen. Und wenn dieses Schema befolgt wird, ist die korrekte Verwendung einer Bibliothek immer der Aufruf von pkg-config.
Johan Boulé
10

Es ist selten, dass man nur mit SDL2 verknüpfen muss. Die derzeit beliebte Antwort verwendet, pkg_search_module()welche nach bestimmten Modulen sucht und das erste funktionierende verwendet.

Es ist wahrscheinlicher, dass Sie eine Verknüpfung mit SDL2 und SDL2_Mixer und SDL2_TTF usw. herstellen möchten, pkg_check_modules()um nach allen angegebenen Modulen zu suchen .

# sdl2 linking variables
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2 SDL2_ttf SDL2_mixer SDL2_image)

# your app
file(GLOB SRC "my_app/*.c")
add_executable(my_app ${SRC})
target_link_libraries(my_app ${SDL2_LIBRARIES})
target_include_directories(my_app PUBLIC ${SDL2_INCLUDE_DIRS})
target_compile_options(my_app PUBLIC ${SDL2_CFLAGS_OTHER})

Haftungsausschluss: Ich hätte Grumbels Selbstantwort einfach kommentiert, wenn ich genug Street Creds mit Stackoverflow gehabt hätte.

Fluxrider
quelle
2
Das Globbing von Quelldateien ist eine schlechte Praxis und wird nicht empfohlen.
Liberforce
1
Für mich hat es target_link_libraries(my_app ${SDL2_LINK_LIBRARIES})besser funktioniert.
Stefan
2
@liberforce Globbing-Quelldateien sind eine gute Praxis. Es ist CMakes Schuld, wenn sie fehlerhaft sind.
Johan Boulé
1
@ JohanBoulé: Nein, ist es nicht. Möglicherweise muss ein Entwickler eine Reihe von Dateien lokal hinzufügen und die Daten auf seinem Computer ausführen, ohne alle erforderlichen Dateien festzuschreiben. Dann schieben sie ihre Änderungen und es bricht für andere Menschen. Sicher, dies kann durch eine kontinuierliche Integration erfasst werden, aber dies ist nur das offensichtlichste Problem. Das Meson-Build-System hat sich dafür entschieden, kein Dateiglobing zu implementieren , und CMake-Entwickler raten ausdrücklich vom Globbing ab . Explizit ist besser als implizit.
Liberforce
1
@liberforce Ich habe dieses Argument schon oft gesehen, mehr als das eigentliche Problem, über das es theoretisiert. Meson ist dagegen, build2 ist für. Keiner wird es haben, wie Tab gegen Leerzeichen.
Johan Boulé
6

Die meisten verfügbaren Antworten können die Header für die pkg-configBibliothek nicht konfigurieren . Nachdem ich über die Dokumentation für FindPkgConfig meditiert hatte, kam ich auf eine Lösung, die auch Folgendes bietet:

include(FindPkgConfig)
if(NOT PKG_CONFIG_FOUND)
  message(FATAL_ERROR "pkg-config not found!" )
endif()
 
pkg_check_modules(<some-lib> REQUIRED IMPORTED_TARGET <some-lib>)
 
target_link_libraries(<my-target> PkgConfig::<some-lib>)

( Ersetzen Sie Ihr Ziel anstelle der <my-target>Bibliothek und entsprechend der Bibliothek <some-lib>entsprechend. )

Die IMPORTED_TARGETOption scheint der Schlüssel zu sein und macht alles dann unter dem PkgConfig::Namespace verfügbar . Das war alles , was erforderlich war , und auch , dass alle sollte erforderlich sein.

Eero Aaltonen
quelle
TIPP: print Cmake var nach dem Ausführen pkg_check_modulesder verfügbaren sehen Vars stackoverflow.com/a/9328525/1211174
Eiche
0
  1. Es gibt keinen Befehl wie target_use. Ich kenne jedoch mehrere Projekte, die einen solchen Befehl für den internen Gebrauch geschrieben haben. Jedes Projekt möchte jedoch zusätzliche Flags oder Definitionen übergeben, daher ist es nicht sinnvoll, es allgemein als CMake zu verwenden. Ein weiterer Grund, warum Sie es nicht haben sollten, sind Bibliotheken mit C ++ - Vorlagen wie Eigen. Es gibt keine Bibliothek, aber Sie haben nur eine Reihe von Include-Dateien.

  2. Der beschriebene Weg ist oft richtig. Es kann für einige Bibliotheken unterschiedlich sein, dann müssen Sie _LDFLAGSoder hinzufügen _CFLAGS. Ein Grund mehr, nicht zu haben target_use. Wenn es bei Ihnen nicht funktioniert, stellen Sie eine neue Frage zu SDL2 oder einer anderen Bibliothek, die Sie verwenden möchten.

usr1234567
quelle
-2

Wenn Sie auch Definitionen aus der Bibliothek hinzufügen möchten, ist die add_definitionsAnweisung dafür da. Dokumentation findet sich hier , zusammen mit mehr Möglichkeiten , Compiler - Flags hinzuzufügen.

Das folgende Codefragment verwendet diese Anweisung, um GTKGL zum Projekt hinzuzufügen:

pkg_check_modules(GTKGL REQUIRED gtkglext-1.0)
include_directories(${GTKGL_INCLUDE_DIRS})
link_directories(${GTKGL_LIBRARY_DIRS})
add_definitions(${GTKGL_CFLAGS_OTHER})
set(LIBS ${LIBS} ${GTKGL_LIBRARIES})

target_link_libraries([insert name of program] ${LIBS})
Jeanluc
quelle
3
Verwenden Sie nicht include_directoriesetc, es wird den globalen Geltungsbereich infizieren! Verwenden Sie target_include_directoriesetc
Dawid Drozd