Wrap-Best-Practices und Tipps
Bei der Erstellung einer Meson-Build-Definition für ein Projekt müssen mehrere Dinge berücksichtigt werden. Dies gilt insbesondere dann, wenn das Projekt als Unterprojekt verwendet werden soll. Diese Seite listet einige Dinge auf, die bei der Erstellung Ihrer Definitionen zu beachten sind.
config.h nicht in den externen Suchpfad legen
Viele Projekte verwenden eine Header-Datei config.h, die sie zur internen Konfiguration ihres Projekts verwenden. Diese Dateien werden nie in die System-Header-Dateien installiert, sodass es keine Kollisionen bei der Einbindung gibt. Dies ist bei Unterprojekten nicht der Fall; Ihr Projektbaum kann eine beliebige Anzahl von Konfigurationsdateien enthalten, daher müssen wir sicherstellen, dass sie nicht miteinander kollidieren.
Das grundlegende Problem ist, dass die Benutzer des Unterprojekts die Header-Dateien des Unterprojekts einbinden können müssen, ohne die config.h-Datei zu sehen. Die korrekteste Lösung ist, die config.h-Datei in etwas Eindeutiges umzubenennen, z. B. foobar-config.h. Dies ist normalerweise nicht praktikabel, es sei denn, Sie sind der Maintainer des betreffenden Unterprojekts.
Die pragmatische Lösung ist, die Konfigurations-Header in ein Verzeichnis zu legen, das keine anderen Header-Dateien enthält, und diese dann vor allen anderen zu verbergen. Eine Möglichkeit ist, ein Unterverzeichnis auf oberster Ebene namens internal zu erstellen und dieses zum Erstellen Ihrer eigenen Quellen zu verwenden, wie hier gezeigt:
subdir('internal') # create config.h in this subdir
internal_inc = include_directories('internal')
shared_library('foo', 'foo.c', include_directories : internal_inc)
Viele Projekte behalten ihre config.h im Hauptverzeichnis, in dem sich keine anderen Quelldateien befinden. In diesem Fall müssen Sie sie nicht verschieben, sondern können stattdessen Folgendes tun:
internal_inc = include_directories('.') # At top level meson.build
Bibliotheken sowohl als statische als auch als gemeinsam genutzte Versionen baubar machen
Einige Plattformen (z. B. iOS) erfordern, dass alles in Ihrer Haupt-App statisch verknüpft wird. In anderen Fällen möchten Sie möglicherweise gemeinsam genutzte Bibliotheken. Sie sind während der Entwicklung auch schneller, da Meson die erneute Verknüpfung optimiert. Das Erstellen beider Bibliothekstypen bei allen Builds ist jedoch langsam und verschwenderisch.
Ihr Projekt sollte die Methode library verwenden, die mit der integrierten Option default_library zwischen gemeinsam genutzt und statisch umgeschaltet werden kann.
mylib = library('foo', 'foo.c')
Generierte Header explizit deklarieren
Mesons Ninja-Backend funktioniert anders als Make und andere Systeme. Anstatt Dinge Verzeichnis für Verzeichnis zu verarbeiten, betrachtet es die gesamte Build-Definition auf einmal und führt die einzelnen Kompilierungsjobs in einer Reihenfolge aus, die von außen betrachtet zufällig erscheinen mag.
Der Grund dafür ist, dass dies wesentlich effizienter ist und Ihre Builds schneller abgeschlossen werden. Der Nachteil ist, dass Sie bei Ihren Abhängigkeiten vorsichtig sein müssen. Das häufigste Problem hier sind Header, die zur Kompilierungszeit mit z. B. Code-Generatoren generiert werden. Wenn diese Header beim Kompilieren von Code benötigt werden, der diese Bibliotheken verwendet, kann der Kompilierungsjob vor dem Code-Generierungsschritt ausgeführt werden. Die Lösung besteht darin, die Abhängigkeit explizit wie folgt herzustellen:
myheader = custom_target(...)
mylibrary = shared_library(...)
mydep = declare_dependency(link_with : mylibrary,
include_directories : include_directories(...),
sources : myheader)
Und dann können Sie die Abhängigkeit auf übliche Weise verwenden
executable('dep_using_exe', 'main.c',
dependencies : mydep)
Meson stellt sicher, dass die Header-Datei erstellt wurde, bevor main.c kompiliert wird.
Vermeiden Sie es, kompilierbare Quelldateien in declare_dependency offenzulegen
Der Hauptzweck des Arguments sources in declare_dependency ist die Konstruktion des korrekten Abhängigkeitsgraphen für die Backends, wie im vorherigen Abschnitt gezeigt. Es ist äußerst wichtig zu beachten, dass es *nicht* verwendet werden sollte, um kompilierbare Quellen (.c, .cpp usw.) von Abhängigkeiten direkt offenzulegen, sondern nur für Header/Konfigurationsdateien verwendet werden sollte. Das folgende Beispiel veranschaulicht, was schief gehen kann, wenn Sie versehentlich kompilierbare Quelldateien offengelegen.
Sie haben also von Unity-Builds gelesen und wie Meson diese nativ unterstützt. Sie entscheiden sich, die Quellen der Abhängigkeiten offenzulegen, um Unity-Builds zu haben, die ihre Abhängigkeiten einschließen. Für Ihre Support-Bibliothek machen Sie:
my_support_sources = files(...)
mysupportlib = shared_library(
...
sources : my_support_sources,
...)
mysupportlib_dep = declare_dependency(
...
link_with : mylibrary,
sources : my_support_sources,
...)
Und für Ihr Hauptprojekt machen Sie:
mylibrary = shared_library(
...
dependencies : mysupportlib_dep,
...)
myexe = executable(
...
link_with : mylibrary,
dependencies : mysupportlib_dep,
...)
Dies ist äußerst gefährlich. Beim Erstellen erstellt mylibrary die Support-Quellen von my_support_sources und verknüpft sie mit der resultierenden gemeinsam genutzten Bibliothek. Dann werden für myexe dieselben Support-Quellen erneut kompiliert, mit der resultierenden ausführbaren Datei verknüpft, zusätzlich zu ihrer bereits vorhandenen Präsenz in mylibrary. Dies kann schnell gegen die One Definition Rule (ODR) in C++ verstoßen, da Sie mehr als eine Definition eines Symbols haben, was zu undefiniertem Verhalten führt. Während C keine strenge ODR-Regel hat, gibt es keine Sprache im Standard, die ein solches Verhalten garantiert. Verstöße gegen die ODR können zu seltsamen, eigentümlichen Fehlern wie Segfaults führen. In den überwiegenden Fällen ist es daher falsch, Bibliotheksquellen über das Argument sources in declare_dependency offenzulegen. Wenn Sie die volle Cross-Library-Leistung erzielen möchten, sollten Sie mysupportlib stattdessen als statische Bibliothek erstellen und LTO verwenden.
Es gibt Ausnahmen von dieser Regel. Wenn es natürliche Einschränkungen gibt, wie Ihre Bibliothek verwendet werden soll, können Sie Quellen offengelegen. Zum Beispiel legt das WrapDB-Modul für GoogleTest die Quellen von GTest und GMock direkt offen. Dies ist gültig, da GTest und GMock nur jemals in *terminalen* Linkzielen verwendet werden. Ein terminales Ziel ist das endgültige Ziel in einer Abhängigkeitsverknüpfungskette, zum Beispiel myexe im letzten Beispiel, während mylibrary ein zwischengeschaltetes Linkziel ist. Für die meisten Bibliotheken gilt diese Regel jedoch nicht, da Sie im Allgemeinen nicht kontrollieren können, wie andere Ihre Bibliothek nutzen, und daher keine Quellen offengelegen sollten.
Die Ergebnisse der Suche sind