Ich habe mehrere Projekte (die alle mit CMake aus derselben Quellbaumstruktur erstellt werden), die alle ihre eigene Mischung aus Dutzenden von unterstützenden Bibliotheken verwenden.
So kam ich auf die Frage, wie man das in CMake richtig einrichtet. Bisher habe ich nur gefunden CMake, wie Abhängigkeiten zwischen Zielen korrekt erstellt werdenaber ich kämpfe immer noch zwischen der Einrichtung von allem mit globalen Abhängigkeiten (die Projektebene weiß alles) oder mit lokalen Abhängigkeiten (jedes untergeordnete Ziel behandelt nur seine eigenen Abhängigkeiten).
Hier ist ein reduziertes Beispiel meiner Verzeichnisstruktur und was ich mir derzeit mit CMake und lokalen Abhängigkeiten ausgedacht habe (das Beispiel zeigt nur ein ausführbares Projekt, App1
aber es gibt eigentlich noch mehr, App2
, App3
etc.):
Lib
+-- LibA
+-- Inc
+-- a.h
+-- Src
+-- a.cc
+-- CMakeLists.txt
+-- LibB
+-- Inc
+-- b.h
+-- Src
+-- b.cc
+-- CMakeLists.txt
+-- LibC
+-- Inc
+-- c.h
+-- Src
+-- c.cc
+-- CMakeLists.txt
App1
+-- Src
+-- main.cc
+-- CMakeLists.txt
Lib/LibA/CMakeLists.txt
include_directories(Inc ../LibC/Inc)
add_subdirectory(../LibC LibC)
add_library(LibA Src/a.cc Inc/a.h)
target_link_libraries(LibA LibC)
Lib/LibB/CMakeLists.txt
include_directories(Inc)
add_library(LibB Src/b.cc Inc/b.h)
Lib/LibC/CMakeLists.txt
include_directories(Inc ../LibB/Inc)
add_subdirectory(../LibB LibB)
add_library(LibC Src/c.cc Inc/c.h)
target_link_libraries(LibC LibB)
App1/CMakeLists.txt (der Einfachheit halber generiere ich hier die Quell-/Header-Dateien)
cmake_minimum_required(VERSION 2.8)
project(App1 CXX)
file(WRITE "Src/main.cc" "#include \"a.h\"\n#include \"b.h\"\nint main()\n{\na();\nb();\nreturn 0;\n}")
file(WRITE "../Lib/LibA/Inc/a.h" "void a();")
file(WRITE "../Lib/LibA/Src/a.cc" "#include \"c.h\"\nvoid a()\n{\nc();\n}")
file(WRITE "../Lib/LibB/Inc/b.h" "void b();")
file(WRITE "../Lib/LibB/Src/b.cc" "void b() {}")
file(WRITE "../Lib/LibC/Inc/c.h" "void c();")
file(WRITE "../Lib/LibC/Src/c.cc" "#include \"b.h\"\nvoid c()\n{\nb();\n}")
include_directories(
../Lib/LibA/Inc
../Lib/LibB/Inc
)
add_subdirectory(../Lib/LibA LibA)
add_subdirectory(../Lib/LibB LibB)
add_executable(App1 Src/main.cc)
target_link_libraries(App1 LibA LibB)
Die Bibliotheksabhängigkeiten im obigen Beispiel sehen folgendermaßen aus:
App1 -> LibA -> LibC -> LibB
App1 -> LibB
Im Moment bevorzuge ich die Variante der lokalen Abhängigkeiten, weil sie einfacher zu bedienen ist. Ich gebe nur die Abhängigkeiten auf Quellebene mit an include_directories()
auf Linkebene mit target_link_libraries()
und auf CMake-Ebene mit add_subdirectory()
.
Damit müssen Sie die Abhängigkeiten zwischen den unterstützenden Bibliotheken nicht kennen und – mit dem CMake-Level “includes” – landen Sie nur bei den Zielen, die Sie wirklich verwenden. Natürlich könnten Sie einfach alle Include-Verzeichnisse und Ziele global bekannt machen und den Compiler/Linker den Rest erledigen lassen. Aber das scheint mir eine Art Blähungen zu sein.
Ich habe auch versucht, eine zu haben Lib/CMakeLists.txt
um alle Abhängigkeiten in der zu handhaben Lib
Verzeichnisbaum, aber am Ende hatte ich eine Menge if ("${PROJECT_NAME}" STREQUAL ...)
Prüfungen und das Problem, dass ich keine Zwischenbibliotheken erstellen kann, die Ziele gruppieren, ohne mindestens eine Quelldatei anzugeben.
Das obige Beispiel ist also “so weit, so gut”, aber es wirft den folgenden Fehler, weil Sie a nicht hinzufügen sollten/können CMakeLists.txt
zweimal:
CMake Error at Lib/LibB/CMakeLists.txt:2 (add_library):
add_library cannot create target "LibB" because another target with the
same name already exists. The existing target is a static library created
in source directory "Lib/LibB".
See documentation for policy CMP0002 for more details.
Im Moment sehe ich zwei Lösungen dafür, aber ich denke, ich habe das viel zu kompliziert gemacht.
1. Überschreiben add_subdirectory()
um Duplikate zu vermeiden
function(add_subdirectory _dir)
get_filename_component(_fullpath ${_dir} REALPATH)
if (EXISTS ${_fullpath} AND EXISTS ${_fullpath}/CMakeLists.txt)
get_property(_included_dirs GLOBAL PROPERTY GlobalAddSubdirectoryOnceIncluded)
list(FIND _included_dirs "${_fullpath}" _used_index)
if (${_used_index} EQUAL -1)
set_property(GLOBAL APPEND PROPERTY GlobalAddSubdirectoryOnceIncluded "${_fullpath}")
_add_subdirectory(${_dir} ${ARGN})
endif()
else()
message(WARNING "add_subdirectory: Can't find ${_fullpath}/CMakeLists.txt")
endif()
endfunction(add_subdirectory _dir)
2. Hinzufügen eines “include guard” zu allen Unterebenen CMakeLists.txt
s, wie:
if (NOT TARGET LibA)
...
endif()
Ich habe die von tamas.kenez und ms vorgeschlagenen Konzepte mit einigen vielversprechenden Ergebnissen getestet. Die Zusammenfassungen finden Sie in meinen folgenden Antworten:
- bevorzugte cmake-Projektstruktur
- CMake-Freigabebibliothek mit mehreren ausführbaren Dateien
- Automatisches Zugreifen auf die cmake-Bibliothek durch andere cmake-Pakete
Ich sehe den Grund dafür nicht
add_subdirectory(../Lib/LibA LibA)
innerhalb App1/CMakeLists.txt. Warum brauchen Sie das? Normalerweise haben Sie eine Wurzel CMakeLists.txt die die Unterverzeichnisse hinzufügt.– Frau
20. Juli 2015 um 9:33 Uhr
@ms In meinem realen Projekt gibt es mehr Anwendungen als nur meine
App1
(Ich habe der Frage einige Details dazu hinzugefügt). Bisher habe ich keine Root-Ebene hinzugefügtCMakeLists.txt
um eine Abhängigkeit zwischen den verschiedenen arbeitenden Projektteams zu vermeidenApp1
,App2
, etc (und ich konnte mir keine vorstellen, die auf der einen Seite generisch genug für alle Projekte und auf der anderen Seite spezifisch genug ist, dass sie am Ende nur ein ausführbares Ziel generiert). Im Moment gehen sie einfach in ihr Unterverzeichnis, erstellen ein binäres Unterverzeichnis, generieren ihre Make-Umgebung mitcmake ..
und Ruf anmake
.– Florian
20. Juli 2015 um 10:25 Uhr
Wie wäre es mit hinzufügen
LibA
,LibB
usw. durchExternalProject_Add
beim WeiterarbeitenApp1
?– Frau
20. Juli 2015 um 10:29 Uhr
@ms In meinem
App1
Beispiel, wenn ich beide hinzufügeLibA
undLibB
über externe Projekte, würde ich nicht mit doppelten Symbolen für alles, was drin ist, endenLibB
?– Florian
20. Juli 2015 um 10:34 Uhr
schau dir hier die Artikelserie an: coderwall.com/p/qk2eog/…
– Frau
20. Juli 2015 um 10:36 Uhr