Diese FAQ enthält Antworten auf viele Fragen, die im IRC und auf der Mailingliste gestellt wurden.
Siehe das Emscripten-Tutorial und emcc.
Es ist bekannt, dass alle Tests in der Emscripten-Testsuite auf unserer Testinfrastruktur erstellt und bestanden werden. Wenn Sie lokal Fehler sehen, liegt wahrscheinlich ein Problem mit Ihrer Umgebung vor. (In seltenen Fällen kann es zu vorübergehenden Störungen kommen, aber niemals bei einer getaggten Release-Version.)
Rufen Sie zuerst emcc --check auf, was grundlegende Integritätsprüfungen durchführt und nützliche Umgebungsinformationen ausgibt. Wenn das nicht hilft, folgen Sie den Anweisungen unter Verifizieren der Emscripten-Entwicklungsumgebung.
Vielleicht möchten Sie auch das Emscripten-Tutorial noch einmal durchgehen, da es bei Änderungen an Emscripten aktualisiert wird.
Stellen Sie außerdem sicher, dass Sie die erforderlichen Voraussetzungen für die Ausführung von Emscripten erfüllen, wie im Abschnitt SDK angegeben, einschließlich ausreichend neuer Versionen der Abhängigkeiten.
Einige allgemeine Schritte, die helfen könnten, das Problem zu finden:
Prüfen Sie, ob das Problem ohne Optimierungen auftritt (-O0 oder keine Angabe einer Optimierungsstufe). Ohne Optimierungen aktiviert Emscripten viele Assertions zur Kompilier- und Laufzeit, die ein Problem abfangen und eine Fehlermeldung mit einem Lösungsvorschlag anzeigen können.
Durchsuchen Sie die Dokumentation auf dieser Website.
Prüfen Sie, ob es einen Test für die fehlerhafte Funktionalität in der Emscripten-Testsuite gibt (führen Sie
grep -rin test/ aus). Diese sollten (mit wenigen Ausnahmen) alle bestehen und bieten somit konkrete „bekanntermaßen funktionierende“ Beispiele für die Verwendung verschiedener Optionen und Code-Strukturen.
In den meisten Fällen können Sie das aktuelle Build-System Ihres Projekts mit Emscripten weiterverwenden. Siehe Projekte erstellen.
Emscripten geht Kompromisse ein, um den generierten Code schneller und kleiner zu machen, auf Kosten längerer Link-Zeiten. Zum Beispiel erstellen wir Teile der Standardbibliothek mit -flto (Link Time Optimization), was zusätzliche Optimierungen ermöglicht, aber länger dauern kann. Zudem lassen wir (in optimierten Builds) den Binaryen-Optimierer über die gesamte Ausgabe laufen, auch ohne LTO.
Hinweis
Sie können feststellen, welche Kompilierschritte am längsten dauern, indem Sie mit EMCC_DEBUG=1 in der Umgebung kompilieren und dann die Debug-Logs überprüfen (standardmäßig in /tmp/emscripten_temp). Beachten Sie, dass das Kompilieren im Debug-Modus länger dauert als normal, da viele Zwischenschritte auf die Festplatte geschrieben werden; es ist also nützlich für das Debugging, aber nicht für das eigentliche Kompilieren.
Die wichtigsten Tipps zur Verbesserung der Build-Zeit sind:
Verwenden Sie -O0 für schnelle Iterations-Builds. Sie können zwar immer noch mit höheren Optimierungsstufen kompilieren, aber die Angabe von -O0 während des Linkens macht diesen Schritt deutlich schneller.
Kompilieren Sie auf einer Maschine mit mehr Kernen.
Verwenden Sie für das Kompilieren Ihrer Quelldateien ein paralleles Build-System (in make können Sie zum Beispiel make -j8 verwenden, um 8 Kerne zu nutzen).
Für den Link-Schritt kann Emscripten einige Optimierungen parallel ausführen (insbesondere Binaryen-Optimierungen für Wasm und unsere JavaScript-Optimierungen). Die Erhöhung der Anzahl der Kerne führt zu einer fast linearen Verbesserung. Emscripten verwendet automatisch mehr Kerne, falls verfügbar, aber Sie können dies mit EMCC_CORES=N in der Umgebung steuern (nützlich, wenn Sie viele Kerne, aber relativ wenig Speicher haben).
Stellen Sie sicher, dass Sie den Code optimieren, indem Sie mit -O2 bauen (noch aggressivere Optimierungen sind verfügbar, allerdings auf Kosten signifikant längerer Kompilierzeiten).
Stellen Sie sicher, dass Sie mit -O3 oder -Os bauen, damit der Code vollständig optimiert und minifiziert wird. Sie sollten den Closure-Compiler verwenden, Gzip-Kompression auf Ihrem Webserver nutzen usw., siehe den Abschnitt über Codegröße unter Code optimieren.
Stellen Sie sicher, dass Sie die mit Emscripten gebündelten System-Header verwenden. Die Verwendung von emcc erfolgt dies standardmäßig, aber es können Probleme auftreten, wenn Sie Ihre lokalen System-Header mit emcc verwenden.
Stellen Sie sicher, dass Sie einen optimierten Build ausführen (kleinere Builds starten schneller).
Die Netzwerklatenz ist ebenfalls ein möglicher Faktor für die Startzeit. Erwägen Sie, den Code zum Laden von Dateien in ein separates Script-Element vom generierten Code zu verschieben, damit der Browser den Netzwerk-Download parallel zum Starten der Codebasis beginnen kann (führen Sie den File Packager aus und platzieren Sie den Datei-Lade-Code in einem Script-Element und die generierte Codebasis in einem späteren Element).
Dieser Fehler kann auftreten, wenn die Seite über eine file:// URL geladen wird, was in einigen Browsern funktioniert, in anderen jedoch nicht. Verwenden Sie stattdessen am besten einen lokalen Webserver. Python hat zum Beispiel einen eingebauten: python -m http.server in Python 3 oder python -m SimpleHTTPServer in Python 2. Danach können Sie https://:8000/ besuchen. Sie können auch emrun DATEINAME.html verwenden (das startet einen Python-Webserver für Sie).
Für schnelle lokale Tests ist eine weitere Option (statt eines lokalen Webservers), alles in eine einzige Datei zu bündeln, indem Sie -sSINGLE_FILE verwenden (da dann keine XHR-Anfragen an file:// URLs gestellt werden).
Andernfalls schauen Sie zum Debuggen nach Fehlern, die auf der Seite selbst, in den Browser-Devtools (Webkonsole und Netzwerk-Tab) oder im Logging Ihres Webservers gemeldet werden.
machine type must be wasm32 oder unknown file type beim Linken?¶Dies bedeutet, dass eine oder mehrere der Linker-Eingabedateien nicht von Emscripten erstellt wurden (oder spezifischer: nicht für die korrekte Zielarchitektur erstellt wurden).
Meistens handelt es sich bei der fraglichen Datei um eine ELF- oder Mach-O-Datei, die für den Host-Rechner erstellt wurde. Sie können das Kommandozeilenprogramm file ausführen, um zu sehen, was sie tatsächlich enthalten.
Häufige Probleme sind:
Der Versuch, gegen Bibliotheken zu linken, die für das Host-System erstellt wurden. Wenn Sie zum Beispiel etwas wie -L/usr/lib in Ihrem Link-Befehl haben, wird das fast immer diese Fehler verursachen, da die Bibliotheken in diesen Systemverzeichnissen fast sicher nicht mit/für Emscripten erstellt wurden. Die Lösung besteht darin, Emscripten zu verwenden, um alle Bibliotheken zu bauen, von denen Sie abhängen, und niemals Host-Bibliotheken zu verwenden.
Einige Bibliotheken oder Objektdateien in Ihrem Projekt wurden mit dem Host-Compiler statt mit dem Emscripten-Compiler erstellt. Wenn Sie Autoconf oder CMake verwenden, stellen Sie sicher, dass Sie den emconfigure/emmake-Wrapper verwenden, siehe Projekte erstellen.
LLVM IR vom alten Backend, falls Sie das Projekt mit einer Version vor 1.39.0 erstellt haben (die standardmäßig das alte Backend nutzte) und jetzt einen inkrementellen Rebuild durchführen. Um das zu beheben, führen Sie einen vollständigen Rebuild aller Dateien Ihres Projekts von Grund auf durch, einschließlich Bibliotheken (dieser Fehler tritt oft auf, wenn Sie vorab kompilierte Bibliotheken von Drittanbietern haben; diese müssen ebenfalls mit dem neuen Backend neu kompiliert werden).
{"text":"asm"}) fehl?¶Emscripten kann keinen Inline-Assembly-Code kompilieren (es sei denn, dieser Assembly-Code wurde speziell für WebAssembly geschrieben).
Sie müssen herausfinden, wo Inline-Assembly verwendet wird, und diese deaktivieren oder durch plattformunabhängigen Code ersetzen.
Das Ereignismodell des Browsers verwendet kooperatives Multitasking — jedes Ereignis hat einen „Zug“ zur Ausführung und muss dann die Kontrolle an die Ereignisschleife des Browsers zurückgeben, damit andere Ereignisse verarbeitet werden können. Eine häufige Ursache für hängende HTML-Seiten ist JavaScript, das nicht abgeschlossen wird und die Kontrolle nicht an den Browser zurückgibt.
Grafische C++-Apps haben typischerweise eine unendliche Hauptschleife, in der Ereignisbehandlung, Verarbeitung und Rendering erfolgen, gefolgt von einer Verzögerung, um die Bildrate korrekt zu halten (SDL_DELAY in SDL-Apps). Da die Hauptschleife nicht endet (unendlich ist), kann sie die Kontrolle nicht an den Browser zurückgeben, und die App hängt sich auf.
Apps, die eine unendliche Hauptschleife verwenden, sollten so umgeschrieben werden, dass die Aktionen für eine einzelne Iteration der Schleife in eine einzelne „endliche“ Funktion verschoben werden. Im nativen Build kann diese Funktion wie zuvor in einer unendlichen Schleife ausgeführt werden. Im Emscripten-Build wird sie als Hauptschleifenfunktion festgelegt und vom Browser mit einer bestimmten Frequenz aufgerufen.
Weitere Informationen zu diesem Thema finden Sie unter Emscripten-Laufzeitumgebung.
Um eine C-Funktion wiederholt auszuführen, verwenden Sie emscripten_set_main_loop() (dies wird unter Emscripten-Laufzeitumgebung besprochen). Die verwandten Funktionen in emscripten.h sind ebenfalls nützlich und ermöglichen es Ihnen, Ereignisse hinzuzufügen, die die Hauptschleife blockieren usw.
Um auf Browser-Ereignisse zu reagieren, verwenden Sie die SDL-API auf normale Weise. Beispiele finden Sie in den SDL-Tests (suchen Sie nach SDL in test/runner.py).
Siehe auch: Warum hängt sich meine HTML-App auf?
Siehe die automatischen SDL-Tests für funktionierende Beispiele: test/runner.py browser.
Systembibliotheken, die in Emscripten enthalten sind, werden beim Kompilieren automatisch gelinkt (nur die notwendigen Teile). Dies umfasst libc, libc++ (C++-Standardbibliothek) und SDL.
Bibliotheken, die nicht in Emscripten enthalten sind (wie Boost), müssen kompiliert und mit dem Programm gelinkt werden, als wären sie ein Modul im Projekt.
Es gibt eine Reihe von Bibliotheken, die für die bequeme Nutzung auf Emscripten portiert wurden, die Emscripten Ports. Siehe Projekte erstellen.
Eine weitere Möglichkeit besteht darin, benötigte C-APIs als JavaScript-Bibliotheken zu implementieren (siehe --js-library in emcc und Implementierung einer C-API in JavaScript). Emscripten selbst tut dies für libc (ohne malloc) und SDL (aber nicht für libc++ oder malloc).
Hinweis
Im Gegensatz zu anderen Compilern benötigen Sie -lSDL nicht, um SDL einzubinden (die Angabe schadet jedoch nicht).
Im speziellen Fall von Boost: Wenn Sie nur die Boost-Header benötigen, müssen Sie nichts kompilieren.
Emscripten bietet teilweise Unterstützung für SDL1- und SDL2-Audio sowie OpenAL.
Um SDL1-Audio zu verwenden, binden Sie es mit #include <SDL/SDL_mixer.h> ein. Sie können es so zusammen mit SDL1, SDL2 oder einer anderen Bibliothek für die Plattformintegration verwenden.
Um SDL2-Audio zu verwenden, binden Sie es mit #include <SDL2/SDL_mixer.h> ein und verwenden Sie -sUSE_SDL_MIXER=2. Die Formatunterstützung ist derzeit auf OGG, WAV, MID und MOD beschränkt.
Emscripten verwendet ein virtuelles Dateisystem, das mit Daten vorab geladen oder mit URLs für Lazy Loading verknüpft werden kann. Weitere Einzelheiten finden Sie in der Dateisystem-Übersicht.
Von Emscripten generierter Code, der im Browser läuft, kann nicht auf Dateien im lokalen Dateisystem zugreifen. Stattdessen können Sie Preloading und Embedding verwenden, um das Fehlen von synchronem Datei-IO zu umgehen. Weitere Informationen finden Sie in der Dateisystem-Übersicht.
Es ist möglich, den Zugriff auf das lokale Dateisystem für Code zu erlauben, der in node.js läuft; verwenden Sie dazu die Dateisystemoption NODEFS.
(Diese Antwort benötigen Sie eventuell, wenn Sie einen Fehler sehen wie native function `x` called before runtime initialization, eine Prüfung, die in ASSERTIONS-Builds aktiviert ist.)
Das Aufrufen einer kompilierten Funktion vor dem vollständigen Laden der Seite kann zu Fehlern führen, wenn die Funktion auf Dateien angewiesen ist, die eventuell noch nicht vorhanden sind (zum Beispiel werden vorab geladene Dateien asynchron geladen. Wenn Sie also einfach JS-Code, der kompilierte Funktionen aufruft, in ein --post-js setzen, wird dieser Code synchron am Ende der kombinierten JS-Datei aufgerufen, potenziell bevor das asynchrone Ereignis eingetreten ist, was problematisch ist).
Der einfachste Weg herauszufinden, wann der Ladevorgang abgeschlossen ist, besteht darin, eine main()-Funktion hinzuzufügen und darin eine JavaScript-Funktion aufzurufen, um Ihren Code zu benachrichtigen, dass das Laden abgeschlossen ist.
Hinweis
Die main()-Funktion wird nach Abschluss des Startvorgangs als Signal aufgerufen, dass es sicher ist, jede kompilierte Methode aufzurufen.
Wenn zum Beispiel allReady() eine JavaScript-Funktion ist, die aufgerufen werden soll, wenn alles bereit ist, können Sie dies tun:
#include <emscripten.h>
int main() {
EM_ASM( allReady() );
}
Eine andere Option ist die Definition einer onRuntimeInitialized-Funktion:
Module['onRuntimeInitialized'] = function() { ... };
Diese Methode wird aufgerufen, wenn die Laufzeitumgebung bereit ist und Sie kompilierte Funktionen aufrufen können. In der Praxis ist dies genau der gleiche Zeitpunkt, an dem main() aufgerufen würde; onRuntimeInitialized ermöglicht also nichts grundlegend Neues, kann aber flexibel zur Laufzeit von JavaScript aus gesetzt werden.
Hier ist ein Beispiel für die Verwendung:
<script type="text/javascript">
var Module = {
onRuntimeInitialized: function() {
Module._foobar(); // foobar was exported
}
};
</script>
<script type="text/javascript" src="my_project.js"></script>
Das Entscheidende ist, dass Module existiert und die Eigenschaft onRuntimeInitialized hat, bevor das Skript mit der Emscripten-Ausgabe (my_project.js in diesem Beispiel) geladen wird.
Eine weitere Option ist die Verwendung der MODULARIZE-Option mittels -sMODULARIZE. Dies umschließt das gesamte generierte JavaScript in einer Factory-Funktion, die Sie aufrufen können, um eine Instanz Ihres Moduls zu erstellen. Die Factory-Funktion gibt ein Promise zurück, das mit der Modulinstanz aufgelöst wird. Das Promise wird aufgelöst, sobald es sicher ist, den kompilierten Code aufzurufen, d. h. nachdem der kompilierte Code heruntergeladen und instanziiert wurde. Wenn Sie zum Beispiel mit -sMODULARIZE -s 'EXPORT_NAME="createMyModule"' bauen, können Sie dies tun:
createMyModule(/* optional default settings */).then(function(Module) {
// this is reached when everything is ready, and you can call methods on Module
});
Beachten Sie, dass wir im MODULARIZE-Modus nicht nach einem globalen Module-Objekt für Standardwerte suchen. Standardwerte müssen als Parameter an die Factory-Funktion übergeben werden (Details in settings.js).
atexit()-Aufrufe nicht ausgeführt?¶(Diese Antwort benötigen Sie eventuell, wenn Sie einen Fehler sehen wie atexit() called, but EXIT_RUNTIME is not set oder stdio streams had content in them that was not flushed. you should set EXIT_RUNTIME to 1.)
Standardmäßig setzt Emscripten EXIT_RUNTIME=0, was bedeutet, dass wir keinen Code zum Herunterfahren der Laufzeitumgebung einfügen. Das bedeutet, dass beim Beenden von main() die Stdio-Streams nicht geleert, die Destruktoren globaler C++-Objekte nicht aufgerufen und keine atexit-Callbacks ausgeführt werden. Dies ermöglicht es uns, standardmäßig kleineren Code zu erzeugen, und ist normalerweise das, was man im Web möchte: Auch wenn main() beendet wurde, könnten später asynchrone Vorgänge eintreten, die ausgeführt werden sollen.
In einigen Fällen möchten Sie jedoch eine eher „kommandozeilenartige“ Erfahrung, bei der die Laufzeit beim Beenden von main() heruntergefahren wird. Sie können mit -sEXIT_RUNTIME bauen, dann rufen wir atexits und so weiter auf. Wenn Sie mit ASSERTIONS bauen, sollten Sie eine Warnung erhalten, wenn dies erforderlich ist. Wenn Ihr Programm zum Beispiel etwas ohne Zeilenumbruch ausgibt:
#include <stdio.h>
int main() {
printf("hello"); // note no newline
}
Wenn wir die Laufzeit nicht herunterfahren und die Stdio-Streams nicht leeren, wird „hello“ nicht gedruckt. In einem ASSERTIONS-Build erhalten Sie einen Hinweis: stdio streams had content in them that was not flushed. you should set EXIT_RUNTIME to 1.
Emscripten führt eine Eliminierung von totem Code (Dead Code Elimination) für Funktionen durch, die nicht vom kompilierten Code aufgerufen werden. Dies minimiert zwar die Codegröße, kann aber Funktionen entfernen, die Sie selbst aufrufen möchten (außerhalb des kompilierten Codes).
Um sicherzustellen, dass eine C-Funktion für den Aufruf aus normalem JavaScript verfügbar bleibt, muss sie über die emcc-Kommandozeile zu den EXPORTED_FUNCTIONS hinzugefügt werden. Um zum Beispiel zu verhindern, dass die Funktionen my_func() und main() entfernt/umbenannt werden, führen Sie emcc so aus:
emcc -sEXPORTED_FUNCTIONS=_main,_my_func ...
Hinweis
_main sollte in der Exportliste stehen (wie in diesem Beispiel), wenn Sie eine main()-Funktion haben. Andernfalls wird sie als toter Code entfernt; es gibt keine spezielle Logik, um main() standardmäßig am Leben zu erhalten.
Hinweis
EXPORTED_FUNCTIONS beeinflusst die Kompilierung zu JavaScript. Wenn Sie zuerst in eine Objektdatei kompilieren und dann das Objekt zu JavaScript, benötigen Sie diese Option beim zweiten Befehl.
Wenn Ihre Funktion in anderen Funktionen verwendet wird, kann LLVM sie „inlinen“ (direkt einbetten), sodass sie nicht als eigenständige Funktion im JavaScript erscheint. Verhindern Sie Inlining, indem Sie die Funktion mit EMSCRIPTEN_KEEPALIVE definieren:
void EMSCRIPTEN_KEEPALIVE yourCfunc() {..}
EMSCRIPTEN_KEEPALIVE exportiert die Funktion ebenfalls, so als stünde sie in EXPORTED_FUNCTIONS.
Hinweis
Alle Funktionen, die nicht durch EXPORTED_FUNCTIONS oder EMSCRIPTEN_KEEPALIVE am Leben erhalten werden, können potenziell entfernt werden. Stellen Sie sicher, dass Sie die benötigten Dinge mit einer oder beiden dieser Methoden erhalten.
Exportierte Funktionen müssen C-Funktionen sein (um C++ Name Mangling zu vermeiden).
Das Dekorieren Ihres Codes mit EMSCRIPTEN_KEEPALIVE kann nützlich sein, wenn Sie nicht explizit Buch über zu exportierende Funktionen führen möchten und sich diese Exporte nicht ändern. Es ist nicht unbedingt geeignet, um Funktionen aus anderen Bibliotheken zu exportieren — zum Beispiel ist es keine gute Idee, den Quellcode der C-Standardbibliothek zu dekorieren und neu zu kompilieren. Wenn Sie dieselbe Quelle auf verschiedene Arten bauen und die Exporte variieren, ist die Verwaltung der Exporte über die Kommandozeile einfacher.
Das Ausführen von emcc mit -sLINKABLE deaktiviert ebenfalls Link-Time-Optimierungen und die Eliminierung von totem Code. Dies wird nicht empfohlen, da es den Code größer und weniger optimiert macht.
Eine weitere mögliche Ursache für fehlenden Code ist das unsachgemäße Linken von .a-Dateien. Die .a-Dateien linken nur die internen Objektdateien, die von vorherigen Dateien in der Kommandozeile benötigt werden; die Reihenfolge der Dateien spielt also eine Rolle, was überraschend sein kann. Wenn Sie .a-Dateien linken, stellen Sie sicher, dass sie am Ende der Dateiliste stehen und untereinander in der richtigen Reihenfolge sind. Alternativ können Sie in Ihrem Projekt einfach .so-Dateien verwenden.
Tipp
Es kann nützlich sein, mit gesetztem EMCC_DEBUG=1 für die Umgebung zu kompilieren (EMCC_DEBUG=1 emcc ... unter Linux, set EMCC_DEBUG=1 unter Windows). Dies teilt die Kompilierschritte auf und speichert sie in /tmp/emscripten_temp. Sie können dann sehen, in welchem Stadium der Code verschwindet (Sie müssen llvm-dis auf die Bitcode-Stadien anwenden, um sie zu lesen, oder llvm-nm verwenden usw.).
Der Closure-Compiler wird den Code der Dateiserver-API minifizieren. Code, der das Dateisystem verwendet, muss zusammen mit der Dateisystem-API optimiert werden, unter Verwendung der --pre-js Option von emcc.
-O2 --closure 1 verwende?¶Der Closure-Compiler minifiziert Variablennamen, was zu sehr kurzen Namen wie i, j, xa usw. führt. Wenn anderer Code Variablen mit denselben Namen im globalen Scope deklariert, kann dies zu schwerwiegenden Problemen führen.
Dies ist wahrscheinlich die Ursache, wenn Code erfolgreich läuft, der mit -O2 (gesetzt) und ohne --closure kompiliert wurde.
Eine Lösung besteht darin, keine kleinen Variablennamen im globalen Scope mehr zu verwenden (oft ist dies ein Fehler — man vergisst var bei der Zuweisung zu einer Variable).
Eine andere Alternative ist, den generierten Code (oder Ihren anderen Code) in einen Closure zu hüllen, wie gezeigt:
var CompiledModule = (function() {
.. GENERATED CODE ..
return Module;
})();
TypeError: Module.someThing is not a function?¶Das Module-Objekt wird exportierte Methoden enthalten. Damit dort etwas erscheint, sollten Sie es zu EXPORTED_FUNCTIONS für kompilierten Code oder zu EXPORTED_RUNTIME_METHODS für eine Laufzeitmethode (wie getValue) hinzufügen. Zum Beispiel:
emcc -sEXPORTED_FUNCTIONS=_main,_my_func ...
würde eine C-Methode my_func exportieren (zusätzlich zu main in diesem Beispiel). Und
emcc -sEXPORTED_RUNTIME_METHODS=ccall ...
wird ccall exportieren. In beiden Fällen können Sie dann auf die exportierte Funktion über das Module-Objekt zugreifen.
Hinweis
Sie können Laufzeitmethoden direkt verwenden, ohne sie zu exportieren, wenn der Compiler deren Verwendung sehen kann. Zum Beispiel können Sie getValue in EM_ASM-Code oder einem --pre-js verwenden, indem Sie es direkt aufrufen. Der Optimierer wird diese JS-Laufzeitmethode nicht entfernen, da er sieht, dass sie verwendet wird. Sie müssen Module.getValue nur verwenden, wenn Sie diese Methode von außerhalb des JS-Codes aufrufen wollen, den der Compiler sehen kann; in diesem Fall müssen Sie sie exportieren.
Hinweis
Früher hat Emscripten viele Laufzeitmethoden standardmäßig exportiert. Dies vergrößerte den Code, weshalb wir diesen Standard geändert haben. Wenn Sie von etwas abhängen, das früher exportiert wurde, sollten Sie in einem unoptimierten Build oder einem Build mit aktivierten ASSERTIONS eine Warnung sehen, die Sie zur Lösung führt. Wir hoffen, dass dies Unannehmlichkeiten minimiert. Siehe ChangeLog.md für Details.
Runtime nicht mehr? Warum erhalte ich einen Fehler beim Versuch, auf Runtime.someThing zuzugreifen?¶1.37.27 beinhaltet ein Refactoring zur Entfernung des Runtime-Objekts. Dies macht den generierten Code effizienter und kompakter, erfordert aber geringfügige Änderungen, falls Sie Runtime.*-APIs verwendet haben. Sie müssen lediglich das Präfix Runtime. entfernen, da diese Funktionen nun einfache Funktionen im obersten Scope sind (eine Fehlermeldung in -O0 oder Builds mit aktivierten Assertions wird dies vorschlagen). Mit anderen Worten, ersetzen Sie:
x = Runtime.stackAlloc(10);
durch
x = stackAlloc(10);
Hinweis
Das Obige funktioniert für Code in einem --pre-js oder einer JS-Bibliothek, also Code, der zusammen mit der Emscripten-Ausgabe kompiliert wird. Wenn Sie versuchen, auf Runtime.*-Methoden von außerhalb des kompilierten Codes zuzugreifen, müssen Sie diese Funktion exportieren (unter Verwendung von EXPORTED_RUNTIME_METHODS) und sie über das Module-Objekt verwenden, siehe diesen FAQ-Eintrag.
NameError oder a problem occurred in evaluating content after a "-s", wenn ich eine -s-Option verwende?¶Dies kann auftreten, wenn Sie komplexe Strings im -s-Argument haben und Schwierigkeiten mit dem korrekten Quoting / Escaping der Shell bekommen.
Die Verwendung der einfacheren Listenform (ohne Anführungszeichen, Leerzeichen oder eckige Klammern) kann manchmal helfen:
emcc a.c -sEXPORTED_RUNTIME_METHODS=foo,bar
Es ist auch möglich, eine Response-Datei zu verwenden, also:
emcc a.c -sEXPORTED_RUNTIME_METHODS=@extra.txt
wobei extra.txt eine einfache Textdatei ist, die foo und bar in separaten Zeilen enthält.
-s-Optionen in einem CMake-Projekt an?¶Einfache Dinge wie dieses sollten in einer CMakeLists.txt-Datei funktionieren:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -sUSE_SDL=2")
Einige -s-Optionen erfordern jedoch möglicherweise Quoting, oder das Leerzeichen zwischen -s und dem nächsten Argument könnte CMake verwirren, wenn Dinge wie target_link_options verwendet werden. Um diese Probleme zu vermeiden, können Sie die -sX=Y-Notation verwenden, also ohne Leerzeichen und ohne eckige Klammern oder Anführungszeichen:
# same as before but no space after -s
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -sUSE_SDL=2")
# example of target_link_options with a list of names
target_link_options(example PRIVATE "-sEXPORTED_FUNCTIONS=_main")
Beachten Sie auch, dass _main nicht in Anführungszeichen gesetzt werden muss, obwohl es ein String-Name ist (emcc weiß, dass das Argument für EXPORTED_FUNCTIONS eine Liste von Strings ist, und akzeptiert daher a oder a,b usw.).
SyntaxError: invalid syntax bei file=.. oder bei einem String, der mit f'..' beginnt?¶Emscripten erfordert eine ausreichend aktuelle Version von Python. Eine ältere Python-Version wie 2.* unterstützt das Print-Statement standardmäßig nicht, weshalb es bei Syntax wie print('..', file=..) zu Fehlern kommt. Und ein älteres 3.* Python unterstützt möglicherweise keine f-Strings, die wie f'..' aussehen.
Stellen Sie sicher, dass Sie eine ausreichend neue Version von Python installiert haben, wie in den SDK-Anweisungen angegeben, und dass diese von emcc verwendet wird (zum Beispiel indem Sie emcc.py mit diesem Python ausführen).
In einer CI-Umgebung müssen Sie möglicherweise die zu verwendende Python-Version angeben, wenn der Standard nicht aktuell genug ist. Zum Beispiel können Sie auf Netlify PYTHON_VERSION verwenden.
RangeError: Maximum call stack size exceeded oder ähnlich?¶Möglicherweise müssen Sie die Stack-Größe für node.js erhöhen.
Unter Linux und macOS können Sie einfach NODE_JS = ['/path/to/node', '--stack_size=8192'] in der Emscripten-Compiler-Konfigurationsdatei (.emscripten) festlegen. Unter Windows (für Node-Versionen älter als v19) benötigen Sie zusätzlich --max-stack-size=8192 und müssen zudem editbin /stack:33554432 node.exe ausführen.
Wenn Sie mit dem Flag -sWASM_BIGINT bauen, werden int64_t und uint64_t als bigint-Werte in JS dargestellt. Ohne das -sWASM_BIGINT-Flag werden die Werte als number in JS dargestellt, was int64 nicht abbilden kann. In diesem Fall werden in exportierten Funktionen (die Sie von JS aus aufrufen können) die Typen „legalisiert“, indem ein i64-Argument in zwei i32s (niedrige und hohe Bits) umgewandelt wird. Ein i64-Rückgabewert wird zu einem i32, und Sie können auf die hohen Bits zugreifen, indem Sie eine Hilfsfunktion namens getTempRet0 aufrufen.
Emscripten-Ausgabe ist standardmäßig nur Code. Wenn dieser in einem Script-Tag platziert wird, bedeutet das, dass sich der Code im globalen Scope befindet. Daher können mehrere solcher Module auf derselben Seite nicht funktionieren.
Indem man jedes Modul in einen Funktions-Scope setzt, wird dieses Problem jedoch vermieden. Emscripten hat dafür sogar ein Compile-Flag, MODULARIZE, das in Verbindung mit EXPORT_NAME nützlich ist (Details in settings.js).
Es gibt jedoch immer noch Probleme, wenn dasselbe Module-Objekt (das das Canvas, den Textausgabebereich usw. definiert) von verschiedenen Modulen gemeinsam genutzt wird. Standardmäßig sucht die Emscripten-Ausgabe sogar im globalen Scope nach Module. Wenn Sie jedoch MODULARIZE verwenden, erhalten Sie eine Funktion, die Sie mit dem Module als Parameter aufrufen müssen, wodurch dieses Problem vermieden wird. Beachten Sie jedoch, dass jedes Modul wahrscheinlich sein eigenes Canvas, seinen eigenen Textausgabebereich usw. haben möchte; das einfache Übergeben desselben Module-Objekts (z. B. aus der Standard-HTML-Shell) funktioniert möglicherweise nicht.
Durch die Verwendung von MODULARIZE und das Erstellen eines eigenen Module-Objekts für jedes Modul können mehrere Module problemlos funktionieren.
Eine weitere Option ist die Verwendung eines iframes. In diesem Fall funktioniert die Standard-HTML-Shell einfach, da jedes seinen eigenen Canvas usw. hat. Dies ist jedoch für kleine Programme, die wie oben beschrieben modular laufen können, übertrieben.
Ja, Sie können die Option ENVIRONMENT in settings.js verwenden. Zum Beispiel erzeugt das Bauen mit emcc -sENVIRONMENT=web Code, der nur im Web läuft und keinen Support-Code für Node.js und andere Umgebungen enthält.
Dies kann nützlich sein, um die Codegröße zu reduzieren, und umgeht zudem Probleme wie den Node.js-Support-Code, der require() verwendet, was Webpack verarbeiten würde und zu unnötigem Code führen könnte.
Ich weiß nicht warum; es ist ein absolut cromulentes Wort!