Grüß euch Ich dachte bis grad eben eigentlich dass ich CMake so halbwegs kapiert hab, nun bin ich aber dummerweise grad zum ersten Mal in ein Problem mit "doppelten" Abhängigkeiten gelaufen und ich hab keine Ahnung wie man das richtig löst. Mein Projekt besitzt in etwa folgende Struktur: - Hauptprogramm - Bibliothek - Modul - Bibliothek Es soll sich also das Hauptprogramm und ein externes Modul die selbe Bibliothek teilen. CMake beschwert sich jetzt allerdings, dass der "add_library" Aufruf für die Bibliothek doppelt vorhanden ist und bricht mit einer Fehlermeldung ab. Ich war bis jetzt immer der Auffassung dass CMake grad für solche Probleme entwickelt wurde...? Hab dann auch ein Projekt auf github gefunden wo extra ein "GUARD" um die Bibliothek herumgewickelt wird: https://github.com/proxict/cmake-diamond bzw. CMakeLists hier -> https://github.com/proxict/cmake-diamond/blob/master/A/CMakeLists.txt Das kann doch bitte nicht die Lösung dafür sein oder? tia
wut nein, definitiv nicht. Erkläre mal was du erreichen willst. Was ist ein "Modul"? Es gibt in CMake nur Targets, die idR entweder Executables oder Libraries sind. Normalerweise baut das CMakeLists.txt in einem Unterordner aus den Source-Files dort eine oder mehrere Libraries. Die übergeordneten Order linken dann gegen diese Libraries. Du solltest nie in die Situation kommen, dass du irgendwo zweimal dasselbe Source File auflistest, geschweige denn zweimal dasselbe Target erzeugst.
:
Bearbeitet durch User
Ok, nennen wir "Modul" eine "anwendungsspezifische Bibliothek" und "Bibliothek" eine "anwendungsagnostische Bibliothek". Also in der einen is etwa ein std::ring_buffer, in der anderen ein json::parser. Der Punkt ist, dass der json::parser auch gerne den std::ring_buffer nutzen würde. Ich fände es aber unlogisch deshalb die Bibliothek mit dem std::ring_buffer im Hauptprogramm nicht einzubinden, auch wenn das streng genommen natürlich nicht nötig wäre. Meine target_link_libraries sehen auch dementsprechend aus. Also um beim Beispiel zu bleiben etwa
1 | # Hauptprogramm CMakeLists.txt
|
2 | target_link_libraries(Hauptprogramm PRIVATE JSON STD) |
3 | |
4 | # JSON CMakeLists.txt
|
5 | target_link_libraries(JSON PRIVATE STD) |
/edit Die entsprechende Fehlermeldung seitens CMake wäre dann: add_library cannot create target "STD" because another target with the same name already exists
:
Bearbeitet durch User
Ich glaube dein Problem ist, dass du dir eine komische Mischform aus externen / internen Sachen überlegt hast. Entweder du hast all diese Komponenten in einem Repository. Dann muss auch nur an jeweils einer Stelle add_subdirectory aufgerufen werden, halt in der richtigen Reihenfolge. Oder du willst wirklich einzelne Module daraus bauen. Dann benutze CMake's find_package-System zusammen mit install(TARGETS ... EXPORT ...) um die entsprechenden .config-Files zu generieren. In keinem Fall willst du CMake-Dateien, die zum Bauen eines externen Projekts benutzt werden, in dein Projekt einbinden, z.B. mit add_subdirectory. Das machst du aber.
Ah ok ich versteh nun so in etwa wie das gedacht ist. Bisher nutzen meine "FindXXX.cmake" Datein direkt add_subdirectory was wohl so nicht gedacht ist. Das klassische install() brauch ich so wie es überall dokumentiert wird allerdings auch nicht. Ich bräuchte das ganze für ein cross-compiliertes Embedded-Target. Was wenn ich meine Bibliothek nicht "installieren" will (Stichwort GNUInstallDirs) sondern nur temporär irgendwie im Build-Ordner belassen damit der Rest vom Code halt dagegen linken kann? Was ist da "best practice"? Gibts da irgendwo Beispiele oder Tutorials? /edit Ich hab mir jetzt nochmals folgende Talks angesehn - https://youtu.be/eC9-iRN2b04 - https://youtu.be/bsXLMQ6WgIk und in beiden findet innerhalb der "FindXXX.cmake" ein add_library() Aufruf statt, allerdings zu einem IMPORTED target. Soweit so gut, aber wie das für Targets gedacht ist die ich einfach jedes mal neu bauen möchte und nirgends hin installiert brauche weiß ich immer noch nicht...?
:
Bearbeitet durch User
FindXXX.cmake ist legacy bzw. für Projekte die selbst kein CMake benutzen. Die bessere Variante, wenn du die Wahl hast, sind die XXX-config.cmake-Dateien. Die kannst du mit genanntem install(... EXPORT) fast komplett automatisch generieren. Du kannst die Bibliothek auch aus dem Build-Ordner linken, aber dann indem du einfach gegen das Target linkst, das du eh schon generiert hast. Also, du machst add_subdirectory(A), das macht add_library(A_lib), dann machst du add_subdirectory(B) und das macht target_link_libraries(B A_lib). Was nicht geht ist das was du versuchst hast, also da wo du gegen die Library linken willst nochmal das CMake-File einzubinden was die Library erzeugt. Das macht einfach keinen Sinn. Die wird ja dann auch nochmal übersetzt und alles.
Du brauchst add_library nur einmal für die Bibliothek und kannst sie dann zu Hauptprogramm und Modul linken. add_library deklariert die Bibliothek, das "add" ist nur zur Verwirrung da.
Vincent H. schrieb: > jedes mal neu bauen möchte Warum? Verschiedene Flags? Dann sollten die verschieden gebauten Bibliotheken auch verschiedene Namen haben.
Vincent H. schrieb: > Soweit so gut, aber > wie das für Targets gedacht ist die ich einfach jedes mal neu bauen > möchte und nirgends hin installiert brauche weiß ich immer noch > nicht...? Das macht wenig Sinn als Library. Dann mach einfach nur eine Liste, set(mysources 1.cpp 2.cpp 3.cpp ...) und füge die jedem Target als Source-Liste hinzu. Gibt auch noch diese object libraries, aber ist schon die Frage was du eigentlich erreichen willst.
Sven B. schrieb: > FindXXX.cmake ist legacy bzw. für Projekte die selbst kein CMake > benutzen. Die bessere Variante, wenn du die Wahl hast, sind die > XXX-config.cmake-Dateien. Die kannst du mit genanntem install(... > EXPORT) fast komplett automatisch generieren. > > Du kannst die Bibliothek auch aus dem Build-Ordner linken, aber dann > indem du einfach gegen das Target linkst, das du eh schon generiert > hast. Also, du machst add_subdirectory(A), das macht add_library(A_lib), > dann machst du add_subdirectory(B) und das macht target_link_libraries(B > A_lib). Genau das möchte ich eigentlich nicht. - es eine A_lib gibt - jene A_lib von B_lib genutzt wird - und C_exe sowohl B_lib als auch A_lib nutzt Sebastian schrieb: > Du brauchst add_library nur einmal für die Bibliothek und kannst sie > dann zu Hauptprogramm und Modul linken. > > add_library deklariert die Bibliothek, das "add" ist nur zur Verwirrung > da. Im Prinzip die selbe Antwort? Ich möchte eben explizit in meinem CMakeLists stehen haben dass C B und A nutzt. Man könnte auch einfach mal annehmen dass ich nicht weiß dass A bereits von B genutzt wird... Sven B. schrieb: > Vincent H. schrieb: >> Soweit so gut, aber >> wie das für Targets gedacht ist die ich einfach jedes mal neu bauen >> möchte und nirgends hin installiert brauche weiß ich immer noch >> nicht...? > > Das macht wenig Sinn als Library. Dann mach einfach nur eine Liste, > set(mysources 1.cpp 2.cpp 3.cpp ...) und füge die jedem Target als > Source-Liste hinzu. > > Gibt auch noch diese object libraries, aber ist schon die Frage was du > eigentlich erreichen willst. Eine Library ist ein eigenständiges Projekt. Ich möchte schon dass diese als Target verfügbar ist.
Dann mach es wie ich beschrieben habe und generiere libA-config mit install(...EXPORT...). Ich würde dir aber wirklich raten auf ein bisschen Style zu verzichten und einfach alles als ein CMake-Projekt zu organisieren; die Lösung mit den Modulen ist ungleich mehr Aufwand und für deinen Fall over-engineered. Ich verstehe auch nicht das Problem, was du damit hast. Was ist an dieser Lösung schlecht? Es macht auch keiner so, außer es gibt eine komplett andere Partei mit einem komplett anderen Projekt, die denselben Code benutzen will.
:
Bearbeitet durch User
Vincent H. schrieb: >
1 | > # Hauptprogramm CMakeLists.txt |
2 | > target_link_libraries(Hauptprogramm PRIVATE JSON STD) |
3 | >
|
4 | > # JSON CMakeLists.txt |
5 | > target_link_libraries(JSON PRIVATE STD) |
6 | >
|
Genau so, sollte es aber eigentlich funktionieren. Main-CMakeLists.txt:
1 | ...
|
2 | add_executable(Hauptprogramm) |
3 | target_link_libraries(Hauptprogramm PRIVATE JSON STD) |
4 | |
5 | add_subdirectory(json) |
./json/CMakeLists.txt
1 | add_library(JSON STATIC ...) |
2 | target_link_libraries(JSON PRIVATE STD) |
Ich tippe mal, der Fehler liegt in dem Teil, den Du uns noch nicht gezeigt hast.
Torsten R. schrieb: > Ich tippe mal, der Fehler liegt in dem Teil, den Du uns noch nicht > gezeigt hast. Er hat doch auf ein Repo verlinkt, und warum das Unsinn ist, wurde auch schon drei Runden lang diskutiert.
Torsten R. schrieb: > Genau so, sollte es aber eigentlich funktionieren. Nein, genau so funktioniert es nicht. Aber keine Sorge falls du das bisher falsch gemacht hast... Um CMake richtig anzuwenden muss man nur 4 Bücher lesen, 10 Talks schaun, 391h googlen und 7 Jungfrauen bei Blutmond opfern. Srsly, die Menge an Falschinformation und wiedersprüchlichen Meinungen im Netz diesbezüglich is ABSOLUT FUCKING ABSURD. Selbst im offiziellen Repo von GTest rät Google dazu das Projekt via add_subdirectory zu inkludieren. Das ist ein absolutes Antipattern und darf NIE praktiziert werden. Den Moment wo zwei auf diese Art inkludierte Projekte die selbe Abhängigkeit besitzen fliegt einem alles um die Ohren. So wie ich das sehe gibt es nur einen einzigen Grund überhaupt jemals add_subdirectory zu nutzen und zwar wenn man einen Sub-Teil eines größeren internen(!!!) Projekts inkludiert. Über diesen Teil muss man die absolute Kontrolle besitzen und jede Abhängigkeit muss glasklar bekannt sein. Am besten wäre vermutlich dass der Sub-Teil selbst überhaupt keine Abhängigkeiten besitzt... Alles andere darf wie es bereits angemerkt wurde ausschließĺich über install(...) inkludiert werden. Ich versuche gerade von git submodulen auf ein Pattern umzusteigen dass mit Hilfe des FetchContent Modules git-repos abholt und via XXXConfig.make / find_package einbindet. Damit sollte es möglich sein 1.) unnötige Kopiern von Submodulen zu vermeiden 2.) bei doppelten Abhängigkeiten nicht mit Fehlern bombardiert zu werden
Vincent H. schrieb: > Srsly, die Menge an Falschinformation und wiedersprüchlichen Meinungen > im Netz diesbezüglich is ABSOLUT FUCKING ABSURD. Ja, das ist leider eines der großen Probleme von CMake, es hat sich in den letzten 10 Jahren sehr viel verändert. Dazu haben viele Leute keine Lust, sich hinreichend mit den Problemstellungen auseinanderzusetzen und pfuschen einfach irgendwas hin. Man muss sich dessen bewusst sein und die Seriösität der Quelle mit bewerten. Ist aber eigentlich immer so. ;) Schöne CMake-Dateien haben z.B. die KDE Frameworks 5-Projekte sowie einige der Applications. Im Zweifel z.B. da mal nach Anregungen suchen. > Selbst im offiziellen > Repo von GTest rät Google dazu das Projekt via add_subdirectory zu > inkludieren. Das ist ein absolutes Antipattern und darf NIE praktiziert > werden. Naja, aber das Projekt wird doch dann auch von deinem Build-System gebaut, oder? Deshalb macht es an der Stelle schon Sinn. Ob es schön ist, ist sicher diskutierbar. > So wie ich das sehe gibt es nur einen einzigen Grund überhaupt jemals > add_subdirectory zu nutzen und zwar wenn man einen Sub-Teil eines > größeren internen(!!!) Projekts inkludiert. Hm? Nein, der Anwendungszweck für add_subdirectory ist, Teile des eigenen Projekts, die in Unterordnern sind, in separaten CMakeLists.txt-Dateien zu verwalten. In diesem Sinne ist GTest auch Teil des eigenen Projekts, weil du ja alle Source-Files in deinem Repo hast. > Alles andere darf wie es bereits angemerkt wurde ausschließĺich über > install(...) inkludiert werden. Ich vermute du meinst über find_package, mit dem install() kann man höchstens die für find_package nötigen Dateien erzeugen. > Ich versuche gerade von git submodulen > auf ein Pattern umzusteigen dass mit Hilfe des FetchContent Modules > git-repos abholt und via XXXConfig.make / find_package einbindet. Stelle dir die Frage, ob der Mehrwert getrennter Repos und Build-Systeme den Aufwand wert ist, oder ob du nicht lieber alles in ein Repo legst. Der Mehraufwand ist auf Dauer ziemlich erheblich und der Nutzen abgesehen von "sieht cooler aus" nicht unbedingt gegeben. Auch dann ist die Frage, ob die Repos mit CMake klonen so die Lösung ist. Vielleicht doch lieber ein "bootstrap.sh"-Skript und das Build-System kümmert sich nur um's Bauen? Die typische Linux-Distro oder der typische CI-Admin werden jedenfalls mit letzterem besser klar kommen.
Sven B. schrieb: >> So wie ich das sehe gibt es nur einen einzigen Grund überhaupt jemals >> add_subdirectory zu nutzen und zwar wenn man einen Sub-Teil eines >> größeren internen(!!!) Projekts inkludiert. > > Hm? Nein, der Anwendungszweck für add_subdirectory ist, Teile des > eigenen Projekts, die in Unterordnern sind, in separaten > CMakeLists.txt-Dateien zu verwalten. In diesem Sinne ist GTest auch Teil > des eigenen Projekts, weil du ja alle Source-Files in deinem Repo hast. Ja das mein ich ja. Auf der offiziellen Seite könnte man trotzdem darauf hinweisen dass dies maximal für Test-Frameworks Sinn macht. >> Alles andere darf wie es bereits angemerkt wurde ausschließĺich über >> install(...) inkludiert werden. > > Ich vermute du meinst über find_package, mit dem install() kann man > höchstens die für find_package nötigen Dateien erzeugen. Exakt. Halt die Kombination install() + export() + find_package(). >> Ich versuche gerade von git submodulen >> auf ein Pattern umzusteigen dass mit Hilfe des FetchContent Modules >> git-repos abholt und via XXXConfig.make / find_package einbindet. > > Stelle dir die Frage, ob der Mehrwert getrennter Repos und Build-Systeme > den Aufwand wert ist, oder ob du nicht lieber alles in ein Repo legst. > Der Mehraufwand ist auf Dauer ziemlich erheblich und der Nutzen > abgesehen von "sieht cooler aus" nicht unbedingt gegeben. Nachdem wir manche Sachen auch extern sharen müssen würd ich diese Frage für mich mit ja beantworten.
:
Bearbeitet durch User
Vincent H. schrieb: > Ja das mein ich ja. Auf der offiziellen Seite könnte man trotzdem darauf > hinweisen dass dies maximal für Test-Frameworks Sinn macht. Verstehe ich nicht. Nochmal: Der Sinn von add_subdirectory ist, Teile eines Projekts -- das können Plugins, Libraries, oder einfach nur logische Gruppen von Quelldateien sein -- in getrennten CMakeLists.txt-Dateien in einer Ordnerstruktur zu verwalten, anstatt eine Top-Level CMakeLists.txt-Datei mit 12.000 Zeilen zu haben. Daraus folgt nicht, dass jeder Unterordner ein eigenständig kompilierbares Projekt darstellt. Siehe z.B. https://invent.kde.org/kde/kdevelop/-/blob/master/plugins/CMakeLists.txt oder https://invent.kde.org/kde/kdevelop/-/blob/master/kdevplatform/CMakeLists.txt wo add_subdirectory extensiv und m.E. sinnvoll verwendet wird. > Nachdem wir manche Sachen auch extern sharen müssen würd ich diese Frage > für mich mit ja beantworten. Ok, das weißt nur du. Das allein finde ich aber nicht hinreichend -- du kannst auch aus einem großen Repository mit install() verschiedene Komponenten exportieren.
:
Bearbeitet durch User
Sven B. schrieb: > Vincent H. schrieb: >> Ja das mein ich ja. Auf der offiziellen Seite könnte man trotzdem darauf >> hinweisen dass dies maximal für Test-Frameworks Sinn macht. > > Verstehe ich nicht. Nochmal: Der Sinn von add_subdirectory ist, *Teile* > eines Projekts -- das können Plugins, Libraries, oder einfach nur > logische Gruppen von Quelldateien sein -- in getrennten > CMakeLists.txt-Dateien in einer Ordnerstruktur zu verwalten, anstatt > eine Top-Level CMakeLists.txt-Datei mit 12.000 Zeilen zu haben. Daraus > folgt nicht, dass jeder Unterordner ein eigenständig kompilierbares > Projekt darstellt. Genau deshalb find ich es nicht sinnvoll dass z.b. GTest dazu rät via add_subdirectory eingebunden zu werden. Das Testframework ist in meinen Augen nicht mehr Teil eines Projekts.
Vincent H. schrieb: > Genau deshalb find ich es nicht sinnvoll dass z.b. GTest dazu rät via > add_subdirectory eingebunden zu werden. Das Testframework ist in meinen > Augen nicht mehr Teil eines Projekts. Der Grund an der Stelle ist die ungewöhnliche Distributionsstrategie, dass alle Sources in das Projekt kopiert und nochmal kompiliert werden. In der Regel würde man eher eine Shared Library zur Verfügung stellen und Header exportieren (und dann eben auch find_package etc verwenden). Ich bin nur sehr tangential mit GTest vertraut und weiß nicht, warum es da so gemacht ist.
Vincent H. schrieb: > Ja das mein ich ja. Auf der offiziellen Seite könnte man trotzdem darauf > hinweisen dass dies maximal für Test-Frameworks Sinn macht. Das ist nicht so, bzw. muss nicht so sein. CMakeLists.txt können sowohl eigenständig stehen, als auch mit add_subdirectory() in ein bestehendes Projekt eingebunden werden, wo sie dann dem einbindenden Projekt targets zur verfügung stellen. Ich denke auch nicht, dass man 10 Bücher gelesen haben muss, um CMake zu verstehen. Es gibt aber erstaunlich wenig, gute Quellen. Die Referenz ist natürlich vollständig, erklärt aber immer nur die Details, wenn man eh schon weiß was man braucht. Meiner Meinung nach gibt es im Moment nur ein einziges, brauchbares Buch: https://crascit.com/professional-cmake/
Torsten R. schrieb: > Vincent H. schrieb: > >> Ja das mein ich ja. Auf der offiziellen Seite könnte man trotzdem darauf >> hinweisen dass dies maximal für Test-Frameworks Sinn macht. > > Das ist nicht so, bzw. muss nicht so sein. CMakeLists.txt können sowohl > eigenständig stehen, als auch mit add_subdirectory() in ein bestehendes > Projekt eingebunden werden, wo sie dann dem einbindenden Projekt targets > zur verfügung stellen. Clone mal das oben von mir verlinkte Repo: https://github.com/proxict/cmake-diamond Entferne den GUARD aus der CMakeLists.txt im Ordner /A. Sprich so ->
1 | cmake_minimum_required(VERSION 3.0) |
2 | |
3 | project(libA VERSION 1.0.0 LANGUAGES C CXX) |
4 | |
5 | add_library(alib STATIC |
6 | src/a.cpp |
7 | )
|
8 | |
9 | target_include_directories(alib |
10 | PUBLIC include |
11 | )
|
Und dann probier das ganze zu Builden.
:
Bearbeitet durch User
Vincent H. schrieb: > Und dann probier das ganze zu Builden. Wenn Du die Targets mehrfach definierst, dann bekommst Du natürlich Probleme. Wenn Du aber einfach im Top-Level ein CMakeLists.txt anlegst (geh' bitte mal auf eine neuere Version in letzter Zeit hat sich bei Cmake sehr viel getan):
1 | cmake_minimum_required(VERSION 3.15) |
2 | |
3 | project(diamond VERSION 1.0.0 LANGUAGES C CXX) |
4 | |
5 | add_subdirectory(A) |
6 | add_subdirectory(B_dependingOnA) |
7 | add_subdirectory(C_dependingOnA) |
8 | add_subdirectory(D_dependingOnBC) |
und die ganzen add_subdirectory() entfernst, die den Symbolischen Links folgen, dann hat CMake da kein Problem. Wenn A kein Teil von B ist, dann sollte B auch nicht so tun, als wäre A ein Teil von B (sprich kein add_subdirectory). An der Stelle sollte B dann einfach davon ausgehen, dass der Top-Level dafür sorgen wird, dass A definiert wird. Guck Dir mal https://github.com/TorstenRobitzki/bluetoe/blob/master/CMakeLists.txt an. Das Projekt kannst Du so z.B. einfach über Git submodules in Dein Projekt einbinden und das Projekt mit add_subdirectory() hinzufügen. Die Beispiele des Projekts nutzen das z.B. und binden die Bibliothek über add_subdirectory() ein: https://github.com/TorstenRobitzki/bluetoe/blob/master/examples/CMakeLists.txt
Torsten R. schrieb: > Wenn A kein Teil von B ist, dann sollte B auch nicht so tun, als wäre A > ein Teil von B (sprich kein add_subdirectory). An der Stelle sollte B > dann einfach davon ausgehen, dass der Top-Level dafür sorgen wird, dass > A definiert wird. Das wurde doch mittlerweile ein paar mal wiederholt. Ja, das geht gut solange man selbst Herr all seiner Abhängigkeiten ist. Bindet man externe Projekte ein, dann ist das eine sehr fragile Angelegenheit. Das verlinkte Projekt ist übrigens nicht von mir. Ich bin momentan auf 3.16.5. /edit Eine Frage hätte ich noch. CMake findet meine XXXConfig.cmake Datei immer erst beim 2. Anlauf. Den Pfad der Config füge ich folgendermaßen hinzu:
1 | set(MYLIB_DIR "/path/to/folder") |
2 | find_package(MYLIB) |
Beim 1.mal Laufen beschwer sich CMake immer dass die Config Datei nicht gefunden werden kann? Beim 2.mal funktionierts dann? Kann das irgendwie am FetchContent Modul liegen?
:
Bearbeitet durch User
Hab ein reproduzierbares Minimalbeispiel in ein Repo gepackt: https://gitlab.com/higaski/CMakeError Und hier die Stackoverflow-Frage dazu: https://stackoverflow.com/questions/60719333/package-configuration-only-found-when-running-cmake-twice
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.