Cross-Kompilierung
Meson unterstützt die Cross-Kompilierung vollständig durch die Verwendung einer Cross-Build-Definitionsdatei. Ein minimales Beispiel für eine solche Datei x86_64-w64-mingw32.txt für einen GCC/MinGW-Cross-Compiler, der auf 64-Bit-Windows abzielt, könnte sein:
[binaries]
c = 'x86_64-w64-mingw32-gcc'
cpp = 'x86_64-w64-mingw32-g++'
ar = 'x86_64-w64-mingw32-ar'
windres = 'x86_64-w64-mingw32-windres'
strip = 'x86_64-w64-mingw32-strip'
exe_wrapper = 'wine64'
[host_machine]
system = 'windows'
cpu_family = 'x86_64'
cpu = 'x86_64'
endian = 'little'
Welche dann während der setup-Phase verwendet wird.
meson setup --cross-file x86_64-w64-mingw32.txt build-mingw
meson compile -C build-mingw
Da Cross-Kompilierung komplizierter ist als native Builds, gehen wir zunächst einige Nomenklaturen durch. Die drei wichtigsten Definitionen werden traditionell als Build, Host und Target bezeichnet. Dies ist verwirrend, da diese Begriffe für sehr unterschiedliche Dinge verwendet werden. Um das Problem zu vereinfachen, werden wir diese als Build-Maschine, Host-Maschine und Target-Maschine bezeichnen. Ihre Definitionen sind wie folgt:
- Build-Maschine ist der Computer, der die eigentliche Kompilierung durchführt.
- Host-Maschine ist die Maschine, auf der die kompilierte Binärdatei ausgeführt wird.
- Target-Maschine ist die Maschine, auf der die Ausgabe der kompilierten Binärdatei ausgeführt wird, nur sinnvoll, wenn das Programm maschinenspezifische Ausgaben erzeugt.
Die tl/dr-Zusammenfassung ist wie folgt: Wenn Sie reguläre Cross-Kompilierung durchführen, kümmern Sie sich nur um die build_machine und die host_machine. Ignorieren Sie einfach die target_machine vollständig, und Sie werden in 99% der Fälle richtig liegen. Nur Compiler und ähnliche Werkzeuge kümmern sich um die Target-Maschine. Tatsächlich muss für sogenannte "Multi-Target"-Tools die Target-Maschine zur Build-Zeit nicht so fest sein wie die anderen, sondern kann zur Laufzeit ausgewählt werden, sodass target_machine immer noch keine Rolle spielt. Wenn Ihre Bedürfnisse komplexer sind oder Sie an den tatsächlichen Details interessiert sind, lesen Sie weiter.
Dies ist möglicherweise leichter durch Beispiele zu verstehen. Beginnen wir mit dem regulären Fall, der keine Cross-Kompilierung ist. In diesen Fällen sind alle drei Maschinen gleich. Bis hierhin einfach.
Betrachten wir als Nächstes die gängigste Cross-Kompilierungs-Konfiguration. Nehmen wir an, Sie befinden sich auf einer 64-Bit-OSX-Maschine und kompilieren eine Binärdatei, die auf einem 32-Bit-ARM-Linux-Board ausgeführt werden soll. In diesem Fall ist Ihre Build-Maschine 64-Bit-OSX, Ihre Host-Maschine ist 32-Bit-ARM-Linux und Ihre Target-Maschine ist irrelevant (aber standardmäßig gleich der Host-Maschine). Das sollte auch recht verständlich sein.
Der übliche Fehler in diesem Fall ist, das OSX-System als Host und das ARM-Linux-Board als Target zu bezeichnen. Das liegt daran, dass dies ihre tatsächlichen Namen waren, als der Cross-Compiler selbst kompiliert wurde! Nehmen wir an, der Cross-Compiler wurde ebenfalls auf OSX erstellt. Als dies geschah, waren die Build- und Host-Maschinen dasselbe OSX und unterschieden sich von der ARM-Linux-Target-Maschine.
Kurz gesagt, der typische Fehler geht davon aus, dass die Begriffe Build, Host und Target sich auf feste Positionen beziehen, während sie tatsächlich relativ dazu sind, wo der aktuelle Compiler läuft. Betrachten Sie Host als Kind des aktuellen Compilers und Target als optionales Enkelkind. Compiler ändern ihre Terminologie nicht, wenn sie einen anderen Compiler erstellen, das würde zumindest ihre Benutzeroberfläche erheblich komplexer machen.
Der komplizierteste Fall ist, wenn Sie einen Cross-Compiler cross-kompilieren. Als Beispiel können Sie auf einer Linux-Maschine einen Cross-Compiler generieren, der unter Windows läuft, aber Binärdateien unter MIPS-Linux erzeugt. In diesem Fall ist die Build-Maschine x86 Linux, die Host-Maschine x86 Windows und die Target-Maschine MIPS Linux. Diese Konfiguration ist als Canadian Cross bekannt. Nebenbei bemerkt: Seien Sie vorsichtig, wenn Sie Artikel über Cross-Kompilierung auf Wikipedia oder im Internet im Allgemeinen lesen. Es ist sehr üblich, dass dort Build-, Host- und Target-Begriffe durcheinandergebracht werden, selbst in aufeinanderfolgenden Sätzen, was Sie verwirren kann, bis Sie es herausgefunden haben.
Beachten Sie erneut, dass die 3 Systeme (Build, Host und Target), die beim Erstellen des Cross-Compilers verwendet werden, nicht mit denen übereinstimmen, die beim Erstellen von etwas mit diesem neu erstellten Cross-Compiler verwendet werden. Um unser Canadian Cross-Szenario von oben zu nehmen (für volle Allgemeinheit): Da seine Host-Maschine x86 Windows ist, ist die Build-Maschine von allem, was wir damit bauen, x86 Windows. Und da seine Target-Maschine MIPS Linux ist, ist die Host-Maschine von allem, was wir damit bauen, MIPS Linux. Nur die Target-Maschine von allem, was wir damit bauen, kann von uns frei gewählt werden, z. B. wenn wir einen weiteren Cross-Compiler erstellen möchten, der unter MIPS Linux läuft und Aarch64 iOS als Ziel hat. Wie dieses Beispiel Ihnen hoffentlich verdeutlicht, sind die Maschinennamen relativ und um eine Position nach links verschoben.
Wenn Sie nicht alle Details verstanden haben, machen Sie sich keine Sorgen. Für die meisten Menschen dauert es eine Weile, bis sie diese Konzepte verstanden haben. Keine Panik, es kann eine Weile dauern, bis es klickt, aber Sie werden es schließlich beherrschen.
Definieren der Umgebung
Meson verlangt, dass Sie eine Cross-Build-Definitionsdatei schreiben. Sie definiert verschiedene Eigenschaften der Cross-Build-Umgebung. Die Cross-Datei besteht aus verschiedenen Abschnitten.
Es gibt eine Reihe von Optionen, die von Cross- und nativen Dateien gemeinsam genutzt werden, hier. Es wird davon ausgegangen, dass Sie diesen Abschnitt bereits gelesen haben, da diese Dokumentation nur Optionen hervorhebt, die für Cross-Dateien spezifisch sind.
Binärdateien
[binaries]
exe_wrapper = 'wine' # A command used to run generated executables.
Die Option exe_wrapper definiert einen Wrapper-Befehl, der verwendet werden kann, um ausführbare Dateien für diesen Host auszuführen. In diesem Fall können wir Wine verwenden, das Windows-Anwendungen unter Linux ausführt. Andere Optionen sind die Ausführung der Anwendung mit qemu oder einem Hardware-Simulator. Wenn Sie eine solche Wrapper-Funktion haben, sind diese Zeilen alles, was Sie schreiben müssen. Meson verwendet automatisch den angegebenen Wrapper, wenn es Host-Binärdateien ausführen muss. Dies geschieht z. B. beim Ausführen der Testsuite des Projekts.
Eigenschaften
Zusätzlich zu den Eigenschaften, die in allen Maschinendateien zulässig sind, kann die Cross-Datei spezifische Informationen über den Cross-Compiler oder die Host-Maschine enthalten. Sie sieht so aus:
[properties]
sizeof_int = 4
sizeof_wchar_t = 4
sizeof_void* = 4
alignment_char = 1
alignment_void* = 4
alignment_double = 4
has_function_printf = true
sys_root = '/some/path'
pkg_config_libdir = '/some/path/lib/pkgconfig'
In den meisten Fällen benötigen Sie die Größen- und Ausrichtungseinstellungen nicht; Meson erkennt all dies durch Kompilieren und Ausführen einiger Beispielprogramme. Wenn Ihr Build ein Stück Daten benötigt, das hier nicht aufgeführt ist, wird Meson stoppen und eine Fehlermeldung ausgeben, wie Sie das Problem beheben können. Wenn Sie zusätzliche Compiler-Argumente für die Cross-Kompilierung benötigen, können Sie diese mit [langname]_args = [args] festlegen. Denken Sie daran, die Argumente als Array und nicht als einzelne Zeichenkette anzugeben (d. h. nicht als '-DCROSS=1 -DSOMETHING=3').
Seit 0.52.0 Die Eigenschaft sys_root kann auf das Stammverzeichnis des Host-Systems verweisen (das System, auf dem die kompilierten Binärdateien ausgeführt werden). Dies wird intern von Meson verwendet, um die Umgebungsvariable PKG_CONFIG_SYSROOT_DIR für pkg-config festzulegen. Wenn dies nicht gesetzt ist, wird angenommen, dass das Host-System eine gemeinsame Wurzel mit dem Build-System hat.
Seit 0.54.0 Die Eigenschaft pkg_config_libdir kann auf eine Liste von Pfaden verweisen, die intern von Meson verwendet werden, um die Umgebungsvariable PKG_CONFIG_LIBDIR für pkg-config festzulegen. Dies verhindert, dass pkg-config Cross-Abhängigkeiten in Systemverzeichnissen sucht.
Eine wichtige Sache zu beachten ist: Wenn Sie im vorherigen Abschnitt keine exe_wrapper definiert haben, wird Meson nach bestem Wissen versuchen, ob es die generierten Binärdateien auf der Build-Maschine ausführen kann. Es bestimmt dies, indem es das system und die cpu_family von Build vs. Host vergleicht. Es wird jedoch Fälle geben, in denen sie übereinstimmen, die Build-Maschine aber tatsächlich nicht mit der Host-Maschine kompatibel ist. Typischerweise geschieht dies, wenn die von der Build- und Host-Maschine verwendete libc inkompatibel ist oder der Code Kernel-Features nutzt, die auf der Build-Maschine nicht verfügbar sind. Ein konkretes Beispiel ist eine macOS-Build-Maschine, die Binärdateien für einen iOS-Simulator (x86-64-Host) erzeugt. Beide sind darwin und haben die gleiche Architektur, aber ihre Binärdateien sind nicht wirklich kompatibel. In solchen Fällen können Sie die Eigenschaft needs_exe_wrapper verwenden, um die automatische Erkennung zu überschreiben.
[properties]
needs_exe_wrapper = true
Maschineneinträge
Der nächste Teil ist die Definition von Host- und Target-Maschinen. Jede Cross-Build-Definition muss eine oder beide davon enthalten. Wenn sie keine hätte, wäre der Build kein Cross-Build, sondern ein nativer Build. Sie müssen die Build-Maschine nicht definieren, da alle notwendigen Informationen automatisch extrahiert werden. Die Definitionen für Host- und Target-Maschinen sehen gleich aus. Hier ist ein Beispiel für die Host-Maschine.
[host_machine]
system = 'windows'
subsystem = 'windows'
kernel = 'nt'
cpu_family = 'x86'
cpu = 'i686'
endian = 'little'
Diese Werte definieren die Maschinen ausreichend für Cross-Kompilierungszwecke. Die entsprechende Target-Definition würde gleich aussehen, aber target_machine im Header haben. Diese Werte sind in Ihren Meson-Skripten verfügbar. Es gibt drei vordefinierte Variablen namens, überraschenderweise, build_machine, host_machine und target_machine. Die Ermittlung des Betriebssystems Ihrer Host-Maschine ist einfach eine Frage des Aufrufs von host_machine.system(). Ab Version 1.2.0 können Sie detailliertere Informationen mit den Methoden .subsystem() und .kernel() erhalten. Die Rückgabewerte dieser Funktionen sind auf der Referenztabelle-Seite dokumentiert.
Es gibt zwei verschiedene Werte für die CPU. Der erste ist cpu_family. Dies ist ein allgemeiner CPU-Typ. Dies sollte einen Wert aus der CPU-Familientabelle haben. Beachten Sie, dass Meson el am Ende des cpu_family-Werts für Little-Endian-Systeme nicht hinzufügt. Big-Endian- und Little-Endian-MIPS sind beide nur mips, wobei das Feld endian entsprechend gesetzt ist.
Der zweite Wert ist cpu, der ein spezifischeres Untertyp für die CPU ist. Typische Werte für eine x86-CPU-Familie könnten i386 oder i586 sein und für die arm-Familie armv5 oder armv7hl. Beachten Sie, dass CPU-Typ-Strings sehr systemabhängig sind. Sie könnten einen anderen Wert erhalten, wenn Sie seinen Wert auf derselben Maschine, aber mit unterschiedlichen Betriebssystemen überprüfen.
Wenn Sie Ihre Host-Maschine nicht definieren, wird angenommen, dass sie die Build-Maschine ist. Ebenso wird angenommen, dass die Target-Maschine die Host-Maschine ist, wenn Sie sie nicht angeben.
Starten eines Cross-Builds
Sobald Sie die Cross-Datei haben, ist das Starten eines Builds einfach:
$ meson setup builddir --cross-file cross_file.txt
Nachdem die Konfiguration abgeschlossen ist, wird die Kompilierung durch Aufrufen von meson compile wie üblich gestartet.
Introspektion und Systemprüfungen
Das Hauptobjekt meson bietet zwei Funktionen zur Bestimmung des Cross-Kompilierungsstatus.
meson.is_cross_build() # returns true when cross compiling
meson.can_run_host_binaries() # returns true if the host binaries can be run, either with a wrapper or natively
Sie können Systemprüfungen sowohl auf dem Systemcompiler als auch auf dem Cross-Compiler ausführen. Sie müssen nur angeben, welcher verwendet werden soll.
build_compiler = meson.get_compiler('c', native : true)
host_compiler = meson.get_compiler('c', native : false)
build_int_size = build_compiler.sizeof('int')
host_int_size = host_compiler.sizeof('int')
Mischen von Host- und Build-Zielen
Manchmal müssen Sie ein Tool erstellen, das zum Generieren von Quelldateien verwendet wird. Diese werden dann für das eigentliche Ziel kompiliert. Dafür möchten Sie einige Ziele mit dem nativen Compiler des Systems erstellen. Dies erfordert nur ein zusätzliches Schlüsselwortargument.
native_exe = executable('mygen', 'mygen.c', native : true)
Sie können dann native_exe nehmen und es als Teil einer Generatorregel oder für alles andere verwenden, was Sie möchten.
Verwenden einer benutzerdefinierten Standardbibliothek
Manchmal müssen Sie bei der Cross-Kompilierung Ihre eigene Standardbibliothek erstellen, anstatt die vom Compiler bereitgestellte zu verwenden. Meson bietet integrierte Unterstützung für den transparenten Wechsel von Standardbibliotheken. Der Aufruf, den Sie in Ihrer Cross-Datei verwenden können, lautet:
[properties]
c_stdlib = ['mylibc', 'mylibc_dep'] # Subproject name, variable name
Dies gibt an, dass die C-Standardbibliothek im Meson-Unterprojekt mylibc in der internen Abhängigkeitsvariable mylibc_dep bereitgestellt wird. Sie wird für jedes Cross-built C-Ziel im gesamten Quellbaum (einschließlich Unterprojekten) verwendet und die Standardbibliothek wird deaktiviert. Die Build-Definitionen dieser Ziele müssen nicht geändert werden.
Beachten Sie, dass dies für jede Sprache unterstützt wird, nicht nur für c, unter Verwendung der Eigenschaft <lang>_stdlib.
Seit 0.56.0 ist der Parameter für den Variablennamen nicht mehr erforderlich, solange das Unterprojekt meson.override_dependency('c_stdlib', mylibc_dep) aufruft. Das obige Beispiel wird zu:
[properties]
c_stdlib = 'mylibc'
Ändern von Cross-Datei-Einstellungen
Cross-Datei-Einstellungen werden nur gelesen, wenn das Build-Verzeichnis zum ersten Mal eingerichtet wird. Alle Änderungen danach werden ignoriert. Dies ist dasselbe wie bei regulären Kompilierungen, bei denen Sie den Compiler nicht ändern können, sobald ein Build-Baum eingerichtet wurde. Wenn Sie Ihre Cross-Datei bearbeiten müssen, müssen Sie Ihren Build-Baum löschen und von Grund auf neu erstellen.
Benutzerdefinierte Daten
Sie können beliebige Daten in properties speichern und von Ihren Meson-Dateien darauf zugreifen. Zum Beispiel, wenn Ihre Cross-Datei dies hat:
[properties]
somekey = 'somevalue'
dann können Sie darauf über das meson-Objekt wie folgt zugreifen:
myvar = meson.get_external_property('somekey')
# myvar now has the value 'somevalue'
Cross-Datei-Speicherorte
Ab Version 0.44.0 unterstützt Meson das Laden von Cross-Dateien von Systemspeicherorten (außer unter Windows). Dies sind $XDG_DATA_DIRS/meson/cross, oder wenn XDG_DATA_DIRS undefiniert ist, werden /usr/local/share/meson/cross und /usr/share/meson/cross in dieser Reihenfolge für systemweite Cross-Dateien versucht. Benutzerlokale Dateien können in $XDG_DATA_HOME/meson/cross oder ~/.local/share/meson/cross abgelegt werden, wenn letzteres undefiniert ist.
Die Reihenfolge der versuchten Speicherorte ist wie folgt:
- Eine Datei relativ zum lokalen Verzeichnis
- Der benutzerlokale Speicherort
- Die systemweiten Speicherorte in Reihenfolge
Distributionen werden ermutigt, Cross-Dateien entweder mit ihren Cross-Compiler-Toolchain-Paketen oder als eigenständiges Paket zu liefern und sie an einem der oben genannten Systempfade zu platzieren.
Diese Dateien können automatisch geladen werden, ohne einen Pfad zur Cross-Datei hinzuzufügen. Wenn beispielsweise ~/.local/share/meson/cross eine Datei namens x86-linux enthält, würde der folgende Befehl einen Cross-Build unter Verwendung dieser Cross-Dateien starten:
meson setup builddir/ --cross-file x86-linux
Die Ergebnisse der Suche sind