Emscripten Tutorial

Die Verwendung von Emscripten ist auf grundlegender Ebene recht einfach. Dieses Tutorial führt Sie durch die notwendigen Schritte, um Ihre ersten Emscripten-Beispiele über die Befehlszeile zu kompilieren. Es zeigt außerdem, wie man mit Dateien arbeitet und die wichtigsten Optimierungs-Flags des Compilers setzt.

Das Wichtigste zuerst

Stellen Sie sicher, dass Sie Emscripten heruntergeladen und installiert haben (die genaue Vorgehensweise hängt von Ihrem Betriebssystem ab: Linux, Windows oder Mac).

Der Zugriff auf Emscripten erfolgt über das Emscripten Compiler Frontend (emcc). Dieses Skript ruft alle anderen Tools auf, die zum Erstellen Ihres Codes benötigt werden, und kann als direkter Ersatz für einen Standard-Compiler wie gcc oder clang dienen. Es wird auf der Befehlszeile mit ./emcc oder ./em++ aufgerufen.

Hinweis

Unter Windows wird das Tool mit einer etwas anderen Syntax aufgerufen: emcc oder em++. Der Rest dieses Tutorials verwendet den Linux-Ansatz (./emcc).

Für den nächsten Abschnitt müssen Sie eine Eingabeaufforderung öffnen

  • Öffnen Sie unter Linux oder macOS ein Terminal.

  • Öffnen Sie unter Windows den Emscripten Command Prompt, eine Eingabeaufforderung, die mit den korrekten Systempfaden und Einstellungen vorkonfiguriert ist, um auf die aktiven Emscripten-Tools zu verweisen. Um auf diese Eingabeaufforderung zuzugreifen, geben Sie Emscripten im Windows 8 Startbildschirm ein und wählen Sie dann die Option Emscripten Command Prompt.

Navigieren Sie mit der Eingabeaufforderung zum emscripten-Verzeichnis unter dem SDK. Dies ist ein Ordner unterhalb des emsdk-Stammverzeichnisses, typischerweise <emsdk root directory>/upstream/emscripten/. Die folgenden Beispiele hängen davon ab, dass Dateien relativ zu diesem Ort gefunden werden.

Hinweis

In älteren Emscripten-Versionen war die Verzeichnisstruktur anders: Die Versionsnummer erschien, aber das Backend (fastcomp/upstream) nicht, sodass Sie so etwas wie <emsdk root directory>/emscripten/1.20.0/ verwenden würden.

Emscripten verifizieren

Wenn Sie Emscripten noch nie ausgeführt haben, führen Sie es jetzt aus mit

./emcc -v

Falls die Ausgabe Warnungen über fehlende Tools enthält, lesen Sie Verifizieren der Emscripten-Entwicklungsumgebung für Hilfe beim Debugging. Andernfalls fahren Sie mit den nächsten Abschnitten fort, in denen wir Code erstellen werden.

Emscripten ausführen

Sie können nun Ihre erste C/C++ Datei zu JavaScript kompilieren.

Werfen wir zunächst einen Blick auf die zu kompilierende Datei: hello_world.c. Dies ist der einfachste Testcode im SDK, und wie Sie sehen können, gibt er lediglich „hello, world!“ auf der Konsole aus und beendet sich dann.

/*
 * Copyright 2011 The Emscripten Authors.  All rights reserved.
 * Emscripten is available under two separate licenses, the MIT license and the
 * University of Illinois/NCSA Open Source License.  Both these licenses can be
 * found in the LICENSE file.
 */

#include <stdio.h>

int main() {
  printf("hello, world!\n");
  return 0;
}

Um die JavaScript-Version dieses Codes zu erstellen, geben Sie einfach die C/C++ Datei nach emcc an (verwenden Sie em++, um die Kompilierung als C++ zu erzwingen)

./emcc test/hello_world.c

Sie sollten sehen, dass zwei Dateien durch diesen Befehl generiert wurden: a.out.js und a.out.wasm. Die zweite ist eine WebAssembly-Datei, die den kompilierten Code enthält, und die erste ist eine JavaScript-Datei, die die Laufzeitunterstützung zum Laden und Ausführen enthält. Sie können diese mit node.js ausführen

node a.out.js

Dies gibt wie erwartet „hello, world!“ auf der Konsole aus.

Hinweis

Ältere node.js-Versionen unterstützen WebAssembly noch nicht. In diesem Fall sehen Sie eine Fehlermeldung, die vorschlägt, mit -sWASM=0 zu bauen, um WebAssembly zu deaktivieren; Emscripten wird den kompilierten Code dann als JavaScript ausgeben. Im Allgemeinen wird WebAssembly empfohlen, da es eine breite Browserunterstützung bietet und sowohl bei der Ausführung als auch beim Download effizienter ist (weshalb Emscripten es standardmäßig ausgibt). Manchmal müssen Sie Ihren Code jedoch in einer Umgebung ausführen, in der es noch nicht vorhanden ist, und sollten es daher deaktivieren.

Tipp

Wenn beim Aufruf von emcc ein Fehler auftritt, führen Sie es mit der Option -v aus, um viele nützliche Debug-Informationen auszugeben.

Hinweis

In diesem Abschnitt und später führen wir einige Dateien aus dem Ordner test/ aus. Dieser Ordner enthält Dateien für die Emscripten-Testsuite. Einige können eigenständig ausgeführt werden, andere müssen über die Testumgebung selbst ausgeführt werden, siehe Emscripten Test Suite für weitere Informationen.

HTML generieren

Emscripten kann auch HTML zum Testen von eingebettetem JavaScript generieren. Um HTML zu generieren, verwenden Sie den Befehl -o (Ausgabe) und geben Sie eine HTML-Datei als Zieldatei an

./emcc test/hello_world.c -o hello.html

Sie können nun hello.html in einem Webbrowser öffnen.

Hinweis

Leider unterstützen mehrere Browser (einschließlich Chrome, Safari und Internet Explorer) keine file:// XHR-Anfragen und können zusätzliche vom HTML benötigte Dateien (wie eine .wasm-Datei oder paketierte Dateidaten, wie weiter unten erwähnt) nicht laden. Für diese Browser müssen Sie die Dateien über einen lokalen Webserver bereitstellen und dann https://:8000/hello.html öffnen.

Sobald Sie das HTML in Ihrem Browser geladen haben, sehen Sie einen Textbereich zur Anzeige der Ausgabe der printf()-Aufrufe im nativen Code.

Die HTML-Ausgabe beschränkt sich nicht nur auf die Anzeige von Text. Sie können auch die SDL-API verwenden, um einen farbigen Würfel in einem <canvas>-Element anzuzeigen (in Browsern, die dies unterstützen). Bauen Sie als Beispiel den hello_world_sdl.c Testcode und aktualisieren Sie dann den Browser

./emcc test/hello_world_sdl.c -o hello.html

Der Quellcode für das zweite Beispiel ist unten angegeben

// Copyright 2011 The Emscripten Authors.  All rights reserved.
// Emscripten is available under two separate licenses, the MIT license and the
// University of Illinois/NCSA Open Source License.  Both these licenses can be
// found in the LICENSE file.

#include <stdio.h>
#include <SDL/SDL.h>

#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif

int main(int argc, char** argv) {
  printf("hello, world!\n");

  SDL_Init(SDL_INIT_VIDEO);
  SDL_Surface *screen = SDL_SetVideoMode(256, 256, 32, SDL_SWSURFACE);

#ifdef TEST_SDL_LOCK_OPTS
  EM_ASM("SDL.defaults.copyOnLock = false; SDL.defaults.discardOnLock = true; SDL.defaults.opaqueFrontBuffer = false;");
#endif

  if (SDL_MUSTLOCK(screen)) SDL_LockSurface(screen);
  for (int i = 0; i < 256; i++) {
    for (int j = 0; j < 256; j++) {
#ifdef TEST_SDL_LOCK_OPTS
      // Alpha behaves like in the browser, so write proper opaque pixels.
      int alpha = 255;
#else
      // To emulate native behavior with blitting to screen, alpha component is ignored. Test that it is so by outputting
      // data (and testing that it does get discarded)
      int alpha = (i+j) % 255;
#endif
      *((Uint32*)screen->pixels + i * 256 + j) = SDL_MapRGBA(screen->format, i, j, 255-i, alpha);
    }
  }
  if (SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);
  SDL_Flip(screen);

  printf("you should see a smoothly-colored square - no sharp lines but the square borders!\n");
  printf("and here is some text that should be HTML-friendly: amp: |&| double-quote: |\"| quote: |'| less-than, greater-than, html-like tags: |<cheez></cheez>|\nanother line.\n");

  SDL_Quit();

  return 0;
}

Dateien verwenden

Hinweis

Ihr C/C++ Code kann über die normale libc stdio API (fopen, fclose, etc.) auf Dateien zugreifen.

JavaScript wird normalerweise in der Sandbox-Umgebung eines Webbrowsers ausgeführt, ohne direkten Zugriff auf das lokale Dateisystem. Emscripten simuliert ein Dateisystem, auf das Sie von Ihrem kompilierten C/C++ Code über die normale libc stdio API zugreifen können.

Dateien, auf die Sie zugreifen möchten, sollten in das virtuelle Dateisystem vorgeladen oder eingebettet werden. Das Vorladen (oder Einbetten) generiert ein virtuelles Dateisystem, das der Dateisystemstruktur zum Zeitpunkt der Kompilierung entspricht, relativ zum aktuellen Verzeichnis.

Das hello_world_file.cpp Beispiel zeigt, wie eine Datei geladen wird (sowohl der Testcode als auch die zu ladende Datei sind unten dargestellt)

// Copyright 2012 The Emscripten Authors.  All rights reserved.
// Emscripten is available under two separate licenses, the MIT license and the
// University of Illinois/NCSA Open Source License.  Both these licenses can be
// found in the LICENSE file.

#include <stdio.h>

int main() {
  FILE *file = fopen("test/hello_world_file.txt", "rb");
  if (!file) {
    printf("cannot open file\n");
    return 1;
  }
  while (!feof(file)) {
    char c = fgetc(file);
    if (c != EOF) {
      putchar(c);
    }
  }
  fclose (file);
  return 0;
}
==
This data has been read from a file.
The file is readable as if it were at the same location in the filesystem, including directories, as in the local filesystem where you compiled the source.
==

Hinweis

Das Beispiel erwartet, eine Datei unter test/hello_world_file.txt laden zu können

FILE *file = fopen("test/hello_world_file.txt", "rb");

Wir kompilieren das Beispiel aus dem Verzeichnis „über“ test, um sicherzustellen, dass das virtuelle Dateisystem mit der korrekten Struktur relativ zum Kompilierzeit-Verzeichnis erstellt wird.

Der folgende Befehl wird verwendet, um eine Datendatei anzugeben, die vor dem Ausführen des kompilierten Codes in das virtuelle Dateisystem von Emscripten vorgeladen werden soll. Dieser Ansatz ist nützlich, da Browser Daten aus dem Netzwerk nur asynchron laden können (außer in Web Workern), während viel nativer Code synchronen Dateisystemzugriff verwendet. Das Vorladen stellt sicher, dass der asynchrone Download der Datendateien abgeschlossen ist (und die Datei verfügbar ist), bevor der kompilierte Code die Möglichkeit hat, auf das Emscripten-Dateisystem zuzugreifen.

./emcc test/hello_world_file.cpp -o hello.html --preload-file test/hello_world_file.txt

Führen Sie den obigen Befehl aus und öffnen Sie dann hello.html in einem Webbrowser, um die Daten aus hello_world_file.txt anzuzeigen.

Für weitere Informationen über die Arbeit mit dem Dateisystem siehe Dateisystem-Übersicht, Dateisystem-API und Verwendung des synchronen virtuellen XHR-gestützten Dateisystems.

Code optimieren

Emscripten generiert, wie gcc und clang, standardmäßig nicht optimierten Code. Sie können mit dem Befehlszeilenargument -O1 leicht optimierten Code generieren

./emcc -O1 test/hello_world.cpp

Der in a.out.js erstellte „Hello World“-Code muss nicht wirklich optimiert werden, daher werden Sie keinen Geschwindigkeitsunterschied im Vergleich zur nicht optimierten Version bemerken.

Sie können jedoch den generierten Code vergleichen, um die Unterschiede zu sehen. -O1 wendet mehrere kleinere Optimierungen an und entfernt einige Laufzeit-Assertionen. Beispielsweise wird printf im generierten Code durch puts ersetzt worden sein.

Die durch -O2 bereitgestellten Optimierungen (siehe hier) sind viel aggressiver. Wenn Sie den folgenden Befehl ausführen und den generierten Code (a.out.js) untersuchen, werden Sie sehen, dass er ganz anders aussieht

./emcc -O2 test/hello_world.cpp

Für weitere Informationen über Compiler-Optimierungsoptionen siehe Code optimieren und die emcc Tool-Referenz.

Emscripten Test Suite und Benchmarks

Emscripten verfügt über eine umfassende Testsuite, die praktisch alle Emscripten-Funktionalitäten abdeckt. Diese Tests sind eine hervorragende Ressource für Entwickler, da sie praktische Beispiele für die meisten Funktionen bieten und dafür bekannt sind, auf dem main-Branch erfolgreich zu bauen.

Siehe Emscripten Test Suite für weitere Informationen.

Allgemeine Tipps und nächste Schritte

Dieses Tutorial hat Sie durch Ihre ersten Schritte beim Aufruf von Emscripten über die Befehlszeile geführt. Natürlich können Sie mit dem Tool noch viel mehr tun. Nachfolgend finden Sie weitere allgemeine Tipps zur Verwendung von Emscripten

  • Diese Seite enthält viele weitere Informationen über das Kompilieren und Erstellen von Projekten, das Integrieren Ihres nativen Codes in die Webumgebung, das Paketieren Ihres Codes und das Veröffentlichen.

  • Die Emscripten-Testsuite ist ein großartiger Ort, um nach Beispielen für die Verwendung von Emscripten zu suchen. Wenn Sie beispielsweise besser verstehen möchten, wie die emcc-Option --pre-js funktioniert, suchen Sie nach --pre-js in der Testsuite: Die Testsuite ist umfangreich und es gibt wahrscheinlich mindestens einige Beispiele.

  • Um zu lernen, wie man Emscripten auf fortgeschrittene Weise nutzt, lesen Sie src/settings.js und emcc, welche die Compiler-Optionen beschreiben, sowie emscripten.h für Details zu JavaScript-spezifischen C-APIs, die Ihre C/C++ Programme verwenden können, wenn sie mit Emscripten kompiliert wurden.

  • Lesen Sie die FAQ.

  • Im Zweifelsfall kontaktieren Sie uns!