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.
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
/edit
Die entsprechende Fehlermeldung seitens CMake wäre dann:
add_library cannot create target "STD" because another target with the
same name already exists
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...?
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.
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.
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.
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 ->
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(VERSION3.15)
2
3
project(diamondVERSION1.0.0LANGUAGESCCXX)
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?