Emscripten stellt zwei Skripte zur Verfügung, die Ihre Makefiles so konfigurieren, dass emcc als direkter Ersatz für gcc verwendet wird — in den meisten Fällen bleibt der Rest des aktuellen Build-Systems Ihres Projekts unverändert.
Um mit Emscripten zu bauen, müssen Sie gcc in Ihren Makefiles durch emcc ersetzen. Dies geschieht mit emconfigure, das die entsprechenden Umgebungsvariablen wie CXX (C++-Compiler) und CC (der Compiler) setzt.
Betrachten Sie den Fall, in dem Sie normalerweise mit den folgenden Befehlen bauen
./configure
make
Tipp
Wenn Sie mit diesen Build-Befehlen nicht vertraut sind, ist der Artikel The magic behind configure, make, make install eine gute Einführung.
Um mit Emscripten zu bauen, würden Sie stattdessen die folgenden Befehle verwenden
# Run emconfigure with the normal configure command as an argument.
emconfigure ./configure
# Run emmake with the normal make to generate Wasm object files.
emmake make
# Compile the linked code generated by make to JavaScript + WebAssembly.
# 'project.o' should be replaced with the make output for your project, and
# you may need to rename it if it isn't something emcc recognizes
# (for example, it might have a different suffix like 'project.so' or
# 'project.so.1', or no suffix like just 'project' for an executable).
# If the project output is a library, you may need to add your 'main.c' file
# here as well.
# [-Ox] represents build optimisations (discussed in the next section).
emcc [-Ox] project.o -o project.js
emconfigure wird mit dem normalen configure als Argument aufgerufen (in configure-basierten Build-Systemen) und emmake mit make als Argument. Wenn Ihr Build-System CMake verwendet, ersetzen Sie im obigen Beispiel ./configure durch cmake . usw. Wenn Ihr Build-System weder configure noch CMake verwendet, können Sie den ersten Schritt überspringen und einfach make ausführen (obwohl Sie dann möglicherweise das Makefile manuell bearbeiten müssen).
Tipp
Wir empfehlen, sowohl das emconfigure- als auch das emmake-Skript in configure- und CMake-basierten Build-Systemen aufzurufen. Ob Sie tatsächlich beide Tools aufrufen müssen, hängt vom Build-System ab (einige Systeme speichern die Umgebungsvariablen im Konfigurationsschritt, andere nicht).
Make erzeugt Wasm-Objektdateien. Es kann die Objektdateien auch in Bibliotheken und/oder ausführbare Wasm-Dateien linken. Sofern ein solches Build-System nicht so modifiziert wurde, dass es auch JavaScript-Ausgaben erzeugt, müssen Sie einen zusätzlichen emcc-Befehl ausführen (wie oben gezeigt), der das endgültige ausführbare JavaScript und WebAssembly generiert.
Hinweis
Die Dateiausgabe von make kann ein anderes Suffix haben: .a für ein statisches Bibliotheksarchiv, .so für eine Shared Library, .o für Objektdateien (diese Dateierweiterungen sind dieselben, die gcc für die verschiedenen Typen verwenden würde). Unabhängig von der Dateiendung enthalten diese Dateien etwas, das emcc in das endgültige JavaScript + WebAssembly kompilieren kann (normalerweise ist der Inhalt Wasm-Objektdateien, aber wenn Sie mit LTO bauen, enthalten sie LLVM-Bitcode).
Hinweis
Einige Build-Systeme geben Wasm-Objektdateien mit dem oben genannten Verfahren möglicherweise nicht korrekt aus, und Sie sehen möglicherweise Warnungen wie is not a valid input file. Sie können file ausführen, um zu prüfen, was eine Datei enthält (außerdem können Sie manuell prüfen, ob der Inhalt mit \0asm beginnt, um zu sehen, ob es sich um Wasm-Objektdateien handelt, oder mit BC bei LLVM-Bitcode). Es lohnt sich auch, emmake make VERBOSE=1 auszuführen, was die ausgeführten Befehle ausgibt - Sie sollten sehen, dass emcc verwendet wird und nicht der native Systemcompiler. Wenn emcc nicht verwendet wird, müssen Sie möglicherweise die configure- oder cmake-Skripte modifizieren.
Sofern emcc nicht mit bestimmten Flags (wie -c, -S, -r oder -shared) ausgeführt wird, führt es die Linker-Phase aus, die mehr als nur eine Datei erzeugen kann. Die Menge der erzeugten Dateien ändert sich je nach den an emcc übergebenen Flags und dem Namen der angegebenen Ausgabedatei. Hier ist eine Übersicht, welche Dateien unter welchen Bedingungen erstellt werden
emcc ... -o output.html erstellt eine output.html-Datei als Ausgabe, sowie eine begleitende output.js-Launcher-Datei und eine output.wasm WebAssembly-Datei.
emcc ... -o output.js verzichtet auf die Generierung einer HTML-Launcher-Datei (in der Erwartung, dass Sie diese selbst bereitstellen, wenn Sie die Ausführung im Browser planen) und erzeugt zwei Dateien: output.js und output.wasm (die z.B. in der node.js-Shell ausgeführt werden können).
emcc -o output.wasm verzichtet auf die Generierung von JavaScript- oder HTML-Launcher-Dateien und erzeugt eine einzelne Wasm-Datei im Standalone-Modus, als ob die Einstellung -sSTANDALONE_WASM verwendet worden wäre. Die resultierende Datei erwartet die Ausführung mit dem WASI ABI - insbesondere müssen Sie nach der Initialisierung des Moduls manuell entweder den _start-Export oder (im Falle von --no-entry) den _initialize-Export aufrufen, bevor Sie etwas anderes damit tun.
emcc ... -o output.{html,js} -sWASM=0 bewirkt, dass der Compiler auf JavaScript abzielt, weshalb keine .wasm-Datei erzeugt wird.
emcc ... -o output.{html,js} --emit-symbol-map erzeugt eine Datei output.{html,js}.symbols, wenn WebAssembly das Ziel ist (ohne -sWASM=0) oder wenn JavaScript das Ziel ist und -Os, -Oz oder -O2 oder höher angegeben ist, aber das Debug-Level auf -g1 oder niedriger eingestellt ist (d. h. wenn eine Symbol-Minifizierung stattgefunden hat).
emcc ... -o output.{html,js} -gsource-map generiert eine Source-Map-Datei output.wasm.map. Wenn JavaScript mit -sWASM=0 das Ziel ist, lautet der Dateiname output.{html,js}.map.
Die Direktive emcc ... -o output.{html,js} --preload-file xxx erzeugt eine vorgeladene MEMFS-Dateisystemdatei output.data.
emcc ... -o output.{html,js} -sWASM={0,1} -sSINGLE_FILE führt JavaScript- und WebAssembly-Code in der einzigen Ausgabedatei output.{html,js} (in base64) zusammen, um nur eine Datei für das Deployment zu erzeugen. (In Kombination mit --preload-file existiert die vorgeladene .data-Datei weiterhin als separate Datei).
Diese Liste ist nicht vollständig, illustriert aber die am häufigsten verwendeten Kombinationen.
Hinweis
Unabhängig vom Namen der Ausgabedatei führt emcc immer das Linking durch und erzeugt eine endgültige ausführbare Datei, es sei denn, spezifische Flags (z. B. -c) weisen ihn an, etwas anderes zu tun. Dies unterscheidet sich vom früheren Verhalten, bei dem emcc standardmäßig Objektdateien kombinierte (was im Wesentlichen -r voraussetzte), sofern keine spezifische Dateiendung für ausführbare Dateien (z. B. .js oder .html) angegeben wurde.
Emscripten führt Compiler-Optimierungen auf zwei Ebenen durch: Jede Quelldatei wird von LLVM optimiert, während sie in eine Objektdatei kompiliert wird, und anschließend werden JavaScript/WebAssembly-spezifische Optimierungen angewendet, wenn die Objektdateien in das endgültige JavaScript/WebAssembly umgewandelt werden.
Um den Code richtig zu optimieren, ist es in der Regel am besten, dieselben Optimierungs-Flags und andere Compiler-Optionen sowohl beim Kompilieren des Quellcodes in Objektcode als auch beim Umwandeln des Objektcodes in JavaScript (oder HTML) zu verwenden.
Betrachten Sie die folgenden Beispiele
# Sub-optimal - JavaScript/WebAssembly optimizations are omitted
emcc -O2 a.cpp -c -o a.o
emcc -O2 b.cpp -c -o b.o
emcc a.o b.o -o project.js
# Sub-optimal - LLVM optimizations omitted
emcc a.cpp -c -o a.o
emcc b.cpp -c -o b.o
emcc -O2 a.o b.o -o project.js
# Usually the right thing: The same options are provided at compile and link.
emcc -O2 a.cpp -c -o a.o
emcc -O2 b.cpp -c -o b.o
emcc -O2 a.o b.o -o project.js
Manchmal möchten Sie jedoch bei bestimmten Dateien leicht abweichende Optimierungen vornehmen
# Optimize the first file for size, and the rest using `-O2`.
emcc -Oz a.cpp -c -o a.o
emcc -O2 b.cpp -c -o b.o
emcc -O2 a.o b.o -o project.js
Hinweis
Leider definiert jedes Build-System seine eigenen Mechanismen zum Festlegen von Compiler- und Optimierungsmethoden. Sie müssen den richtigen Ansatz herausfinden, um die LLVM-Optimierungs-Flags für Ihr System zu setzen.
Einige Build-Systeme haben ein Flag wie ./configure --enable-optimize.
JavaScript/WebAssembly-Optimierungen werden im letzten Schritt festgelegt (manchmal "Link" genannt, da dieser Schritt typischerweise auch eine Reihe von Dateien zusammenfügt, die alle gemeinsam in eine JavaScript/WebAssembly-Ausgabe kompiliert werden). Zum Beispiel, um mit -O1 zu kompilieren
# Compile the object file to JavaScript with -O1 optimizations.
emcc -O1 project.o -o project.js
Das Erstellen eines Projekts, das Debug-Informationen enthält, erfordert, dass Debug-Flags sowohl für die LLVM- als auch für die JavaScript-Kompilierungsphase angegeben werden.
Damit Clang und LLVM Debug-Informationen in Objektdateien ausgeben, müssen Sie die Quellen mit -g kompilieren (genau wie normalerweise mit clang oder gcc).
Hinweis
Jedes Build-System definiert seine eigenen Mechanismen zum Setzen von Debug-Flags. Damit Clang LLVM-Debug-Informationen ausgibt, müssen Sie den richtigen Ansatz für Ihr System herausfinden.
Einige Build-Systeme haben ein Flag wie ./configure --enable-debug. In CMake-basierten Build-Systemen setzen Sie den CMAKE_BUILD_TYPE auf "Debug".
Damit emcc die in den Objektdateien vorhandenen Debug-Informationen bei der Generierung des endgültigen JavaScripts und WebAssemblys berücksichtigt, muss Ihr finaler emcc-Befehl -g oder eine der -gN Debug-Level-Optionen angeben.
# Compile the Wasm object file to JavaScript+WebAssembly, with debug info
# -g or -gN can be used to set the debug level (N)
emcc -g project.o -o project.js
Für allgemeinere Informationen siehe das Thema Debugging.
Es gibt integrierte Unterstützung für eine Reihe von Standardbibliotheken: libc, libc++ und SDL. Diese werden automatisch gelinkt, wenn Sie Code kompilieren, der sie verwendet (Sie müssen nicht einmal -lSDL hinzufügen, beachten Sie jedoch unten weitere SDL-spezifische Details).
Wenn Ihr Projekt andere Bibliotheken verwendet, zum Beispiel zlib oder glib, müssen Sie diese bauen und linken. Der normale Ansatz besteht darin, die Bibliotheken zu bauen (als Objektdateien oder .a-Archive davon) und diese dann mit Ihrem Hauptprogramm zu verknüpfen, um JavaScript+WebAssembly auszugeben.
Betrachten Sie zum Beispiel den Fall, in dem ein Projekt „project“ eine Bibliothek „libstuff“ verwendet
# Compile libstuff to libstuff.a
emconfigure ./configure
emmake make
# Compile project to project.o
emconfigure ./configure
emmake make
# Link the library and code together.
emcc project.o libstuff.a -o final.html
Emscripten Ports ist eine Sammlung nützlicher Bibliotheken, die auf Emscripten portiert wurden. Sie befinden sich auf GitHub und verfügen über Integrationsunterstützung in emcc. Wenn Sie die Verwendung eines Ports anfordern, lädt emcc diesen vom Remote-Server herunter, richtet ihn ein, baut ihn lokal, verlinkt ihn dann mit Ihrem Projekt, fügt notwendige Includes zu Ihren Build-Befehlen hinzu usw. SDL2 ist beispielsweise in den Ports enthalten, und Sie können dessen Verwendung mit --use-port=sdl2 anfordern. Zum Beispiel,
emcc test/browser/test_sdl2_glshader.c --use-port=sdl2 -sLEGACY_GL_EMULATION -o sdl2.html
Sie sollten einige Benachrichtigungen über die Verwendung von SDL2 sehen, und dass es gebaut wird, falls dies zuvor nicht der Fall war. Sie können dann sdl2.html in Ihrem Browser ansehen.
Um eine Liste aller verfügbaren Ports zu sehen, führen Sie emcc --show-ports aus.
Hinweis
SDL_image wurde ebenfalls zu den Ports hinzugefügt, verwenden Sie es mit --use-port=sdl2_image. Damit sdl2_image nützlich ist, müssen Sie im Allgemeinen die Bildformate angeben, die Sie verwenden möchten, z. B. mit --use-port=sdl2_image:formats=bmp,png,xpm,jpg. Dies stellt auch sicher, dass IMG_Init ordnungsgemäß funktioniert, wenn Sie diese Formate angeben. Alternativ können Sie emcc --use-preload-plugins verwenden und Ihre Bilder mit --preload-file vorladen, damit die Browser-Codecs sie dekodieren (siehe Preloading files). Ein Codepfad im sdl2_image-Port wird über emscripten_get_preloaded_image_data() geladen, aber dann schlagen Ihre Aufrufe von IMG_Init mit diesen Bildformaten fehl (denn obwohl die Bilder durch Preloading funktionieren, meldet IMG_Init keine Unterstützung für diese Formate, da keine Unterstützung einkompiliert ist - mit anderen Worten, IMG_Init meldet keine Unterstützung für Formate, die nur über Preloading funktionieren).
Hinweis
SDL_net wurde ebenfalls zu den Ports hinzugefügt, verwenden Sie es mit --use-port=sdl2_net.
Hinweis
Emscripten unterstützt auch das ältere SDL1, das fest eingebaut ist. Wenn Sie SDL2 nicht wie im obigen Befehl angeben, wird SDL1 gelinkt und die SDL1-Include-Pfade verwendet. SDL1 unterstützt sdl-config, das sich in system/bin befindet. Die Verwendung des nativen sdl-config kann zu Kompilierungs- oder Missing-Symbol-Fehlern führen. Sie müssen das Build-System so modifizieren, dass es nach Dateien in emscripten/system oder emscripten/system/bin sucht, um das Emscripten sdl-config zu verwenden.
Hinweis
Sie können eine Bibliothek aus den Ports auch manuell bauen, wenn Sie das bevorzugen, müssen dann aber auch die Python-Logik anwenden, die die Ports verwenden. Dieser Code (unter tools/ports/) kann Dinge tun wie sicherstellen, dass notwendige JS-Funktionen im Build enthalten sind, Exports hinzufügen und so weiter. Generell ist es besser, die Ports-Version zu verwenden, da diese getestet ist und bekanntermaßen funktioniert.
Hinweis
Seit Emscripten 3.1.54 ist --use-port die bevorzugte Syntax, um einen Port in Ihrem Projekt zu verwenden. Die veraltete Syntax (zum Beispiel -sUSE_SDL2, -sUSE_SDL_IMAGE=2) bleibt weiterhin verfügbar.
Contrib Ports werden von der breiteren Community beigesteuert und auf einer „Best Effort“-Basis unterstützt. Da sie nicht als Teil der Emscripten-CI ausgeführt werden, ist nicht immer garantiert, dass sie gebaut werden können oder funktionieren. Siehe Contrib Ports für weitere Informationen.
Der einfachste Weg, einen neuen Port hinzuzufügen, ist, ihn unter dem Verzeichnis contrib abzulegen. Grundsätzlich sind die Schritte
Stellen Sie sicher, dass der Port Open Source ist und eine geeignete Lizenz hat.
Lesen Sie die Datei
README.mduntertools/ports/contrib, die weitere Informationen enthält.
Emscripten unterstützt auch externe Ports (Ports, die nicht Teil der Distribution sind). Um einen solchen Port zu verwenden, geben Sie einfach seinen Pfad an: --use-port=/pfad/zu/mein_port.py
Hinweis
Beachten Sie, dass die von Emscripten verwendete Port-API nicht 100 % stabil ist und sich zwischen Versionen ändern kann, wenn Sie am Code eines Ports arbeiten.
Einige große Projekte generieren ausführbare Dateien und führen diese aus, um Eingaben für spätere Teile des Build-Prozesses zu generieren (zum Beispiel kann ein Parser gebaut und dann auf eine Grammatik angewendet werden, was wiederum C/C++-Code generiert, der diese Grammatik implementiert). Diese Art von Build-Prozess verursacht Probleme bei der Verwendung von Emscripten, da Sie den generierten Code nicht direkt ausführen können.
Die einfachste Lösung besteht meist darin, das Projekt zweimal zu bauen: einmal nativ und einmal für JavaScript. Wenn der JavaScript-Build-Prozess fehlschlägt, weil eine generierte ausführbare Datei nicht vorhanden ist, können Sie diese ausführbare Datei aus dem nativen Build kopieren und den Build normal fortsetzen. Dieser Ansatz wurde beispielsweise erfolgreich zum Kompilieren von Python verwendet (das seine pgen-Ausführbare während des Builds ausführen muss).
In einigen Fällen ist es sinnvoll, die Build-Skripte so zu modifizieren, dass sie die generierte ausführbare Datei nativ bauen. Dies kann zum Beispiel durch die Angabe von zwei Compilern in den Build-Skripten, emcc und gcc, geschehen, wobei gcc nur für die generierten ausführbaren Dateien verwendet wird. Dies kann jedoch komplizierter sein als die vorherige Lösung, da Sie die Build-Skripte des Projekts ändern müssen und möglicherweise Fälle umgehen müssen, in denen Code sowohl für das Endergebnis als auch für eine generierte ausführbare Datei kompiliert und verwendet wird.
Das Ziel von Emscripten ist es, den schnellsten und kleinstmöglichen Code zu generieren. Aus diesem Grund konzentriert es sich darauf, ein gesamtes Projekt in eine einzige Wasm-Datei zu kompilieren und dynamisches Linken nach Möglichkeit zu vermeiden.
Standardmäßig erzeugt Emscripten, wenn das Flag -shared zum Erstellen einer Shared Library verwendet wird, eine .so-Bibliothek, die eigentlich nur eine reguläre .o-Objektdatei ist (unter der Haube wird ld -r verwendet, um Objekte zu einem einzigen größeren Objekt zu kombinieren). Wenn diese Pseudo-"Shared-Libraries" in Ihre Anwendung gelinkt werden, werden sie effektiv wie statische Bibliotheken behandelt. Beim Bauen dieser Shared Libraries ignoriert Emcc andere Shared Libraries in der Befehlszeile. Dies soll sicherstellen, dass dieselbe dynamische Bibliothek nicht mehrfach in Zwischenstufen des Builds gelinkt wird, was zu Fehlern wegen doppelter Symbole führen würde.
Siehe experimentelle Unterstützung für Informationen darüber, wie echte dynamische Bibliotheken gebaut werden können, die entweder zum Ladezeitpunkt oder zur Laufzeit (via dlopen) miteinander verlinkt werden können.
Projekte, die configure, cmake oder eine andere portable Konfigurationsmethode verwenden, können während der Konfigurationsphase Prüfungen durchführen, um zu verifizieren, dass die Toolchain und die Pfade korrekt eingerichtet sind. Emcc versucht, diese Prüfungen nach Möglichkeit bestehen zu lassen, aber Sie müssen möglicherweise Tests deaktivieren, die aufgrund eines "False Negative" fehlschlagen (zum Beispiel Tests, die in der endgültigen Ausführungsumgebung bestehen würden, aber nicht in der Shell während des configure-Schritts).
Tipp
Stellen Sie sicher, dass bei Deaktivierung einer Prüfung die getestete Funktionalität tatsächlich funktioniert. Dies kann das manuelle Hinzufügen von Befehlen zu den Make-Dateien mittels einer Build-System-spezifischen Methode erfordern.
Hinweis
Im Allgemeinen ist configure nicht ideal für einen Cross-Compiler wie Emscripten geeignet. configure ist darauf ausgelegt, nativ für das lokale System zu bauen, und arbeitet hart daran, das native Build-System und die lokalen System-Header zu finden. Mit einem Cross-Compiler zielen Sie auf ein anderes System ab und ignorieren diese Header usw.
Emscripten unterstützt .a-Archivdateien, bei denen es sich um Sammlungen von Objektdateien handelt. Dies ist ein einfaches Format für Bibliotheken, das eine spezielle Semantik hat — zum Beispiel spielt die Reihenfolge beim Linken bei .a-Dateien eine Rolle, bei einfachen Objektdateien jedoch nicht. Größtenteils sollte diese spezielle Semantik in Emscripten genauso funktionieren wie anderswo.
Das Emscripten-Tutorial hat gezeigt, wie emcc verwendet werden kann, um einzelne Dateien in JavaScript zu kompilieren. Emcc kann auch auf alle anderen Arten verwendet werden, die Sie von gcc erwarten würden.
# Generate a.out.js from C++. Can also take .ll (LLVM assembly) or .bc (LLVM bitcode) as input
emcc src.cpp
# Generate an object file called src.o.
emcc src.cpp -c
# Generate result.js containing JavaScript.
emcc src.cpp -o result.js
# Generate an object file called result.o
emcc src.cpp -c -o result.o
# Generate a.out.js from two C++ sources.
emcc src1.cpp src2.cpp
# Generate object files src1.o and src2.o
emcc src1.cpp src2.cpp -c
# Combine two object files into a.out.js
emcc src1.o src2.o
# Combine two object files into another object file (not normally needed)
emcc src1.o src2.o -r -o combined.o
# Combine two object files into library file
emar rcs libfoo.a src1.o src2.o
Zusätzlich zu den Fähigkeiten, die es mit gcc teilt, unterstützt emcc Optionen zur Codeoptimierung, zur Steuerung der ausgegebenen Debug-Informationen, zur Generierung von HTML und anderen Ausgabeformaten usw. Diese Optionen sind in der emcc-Tool-Referenz dokumentiert (emcc --help auf der Kommandozeile).
Emscripten stellt die folgenden Präprozessor-Makros zur Verfügung, mit denen die Compiler-Version und die Plattform identifiziert werden können
Das Präprozessor-Define
__EMSCRIPTEN__ist immer definiert, wenn Programme mit Emscripten kompiliert werden.Die Präprozessor-Variablen
__EMSCRIPTEN_major__,__EMSCRIPTEN_minor__und__EMSCRIPTEN_tiny__sind inemscripten/version.hdefiniert und geben als Ganzzahlen die aktuell verwendete Emscripten-Compilerversion an.Emscripten verhält sich wie eine Unix-Variante, daher sind die Präprozessor-Defines
unix,__unixund__unix__beim Kompilieren von Code mit Emscripten immer vorhanden.Emscripten verwendet Clang/LLVM als zugrunde liegenden Codegen-Compiler, daher sind die Präprozessor-Defines
__llvm__und__clang__gesetzt, und die Präprozessor-Defines__clang_major__,__clang_minor__und__clang_patchlevel__geben die verwendete Clang-Version an.Clang/LLVM ist GCC-kompatibel, daher sind auch die Präprozessor-Defines
__GNUC__,__GNUC_MINOR__und__GNUC_PATCHLEVEL__definiert, um den Grad der GCC-Kompatibilität zu repräsentieren, den Clang/LLVM bietet.Der Präprozessor-String
__VERSION__gibt die GCC-kompatible Version an, die erweitert wird, um auch Emscripten-Versionsinformationen anzuzeigen.Ebenso ist
__clang_version__vorhanden und gibt sowohl Emscripten- als auch LLVM-Versionsinformationen an.Emscripten ist eine 32-Bit-Plattform, daher ist
size_teine vorzeichenlose 32-Bit-Ganzzahl,__POINTER_WIDTH__=32,__SIZEOF_LONG__=4und__LONG_MAX__entspricht2147483647L.Wenn SSEx SIMD-APIs mit einem der Compiler-Flags
-msse,-msse2,-msse3,-mssse3oder-msse4.1angesteuert werden, sind eines oder mehrere der Präprozessor-Flags__SSE__,__SSE2__,__SSE3__,__SSSE3__,__SSE4_1__vorhanden, um die verfügbare Unterstützung für diese Befehlssätze anzuzeigen.Wenn die pthreads-Multithreading-Unterstützung mit dem Compiler- und Linker-Flag
-pthreadangesteuert wird, ist das Präprozessor-Define__EMSCRIPTEN_PTHREADS__vorhanden.
Manchmal kann es nützlich sein, einen Compiler-Wrapper zu verwenden, um Dinge wie ccache, distcc oder gomacc zu nutzen. Bei ccache sollte die normale Methode, einfach den gesamten Compiler zu wrappen, funktionieren, z. B. ccache emcc. Für verteilte Builds kann es vorteilhaft sein, den Emscripten-Treiber lokal auszuführen und nur die zugrunde liegenden Clang-Befehle zu verteilen. Wenn dies gewünscht ist, kann die Einstellung COMPILER_WRAPPER in der Konfigurationsdatei verwendet werden, um einen Wrapper um die internen Aufrufe von Clang hinzuzufügen. Wie andere Konfigurationseinstellungen kann dies auch über eine Umgebungsvariable gesetzt werden, z. B.
EM_COMPILER_WRAPPER=gomacc emcc -c hello.c
emconfigure und emmake konfigurieren pkg-config für Cross-Compiling und setzen die Umgebungsvariablen PKG_CONFIG_LIBDIR und PKG_CONFIG_PATH. Um benutzerdefinierte pkg-config-Pfade anzugeben, setzen Sie die Umgebungsvariable EM_PKG_CONFIG_PATH.
Die Emscripten-Testsuite (test/runner.py) enthält eine Reihe guter Beispiele — große C/C++-Projekte, die mit ihren normalen Build-Systemen wie oben beschrieben gebaut werden: freetype, openjpeg, zlib, bullet und poppler.
Es lohnt sich auch, einen Blick auf die Build-Skripte im ammo.js-Projekt zu werfen.
Stellen Sie sicher, dass Sie emar verwenden (das llvm-ar aufruft), da das System- ar unsere Objektdateien möglicherweise nicht unterstützt. emmake und emconfigure setzen die AR-Umgebungsvariable korrekt, aber ein Build-System könnte fälschlicherweise ar fest kodiert haben.
Ähnlich kann die Verwendung von System- ranlib anstelle von emranlib (das llvm-ranlib aufruft) zu Problemen führen, da es unsere Objektdateien möglicherweise nicht unterstützt und den Index entfernt, was zu archive has no index; run ranlib to add one von wasm-ld führt. Auch hier sollte die Verwendung von emmake/emconfigure dies vermeiden, indem die Umgebungsvariable RANLIB gesetzt wird, aber ein Build-System könnte sie fest kodiert haben oder erfordern, dass Sie eine Option übergeben.
Der Kompilierungsfehler multiply defined symbol weist darauf hin, dass das Projekt eine bestimmte statische Bibliothek mehrfach verlinkt hat. Das Projekt muss so geändert werden, dass die problematische Bibliothek nur einmal verlinkt wird.
Hinweis
Sie können llvm-nm verwenden, um zu sehen, welche Symbole in jeder Objektdatei definiert sind.
Eine Lösung besteht darin, dynamisches Linken zu verwenden. Dies stellt sicher, dass Bibliotheken nur einmal in der finalen Build-Phase verlinkt werden.
Stellen Sie bei der Generierung von Standalone-Wasm sicher, dass Sie den _start- oder (bei --no-entry) _initialize-Export aufrufen, bevor Sie versuchen, das Modul zu verwenden.