Обучение

Использовать Emscripten на базов уровне достаточно просто. Это обучение расскажет о необходимых шагах для компиляции первого проекта из комнадной строки. Кроме того, объяснит как работать с файлами и использовать оптимизационные флаги компиляции.

Первым делом

Убедитесь что вы скачали и установили Emscripten (способ установки зависит от операционной системы: Linux, Windows, or Mac).

Emscripten доступен с использованием Emscripten Compiler Frontend (emcc). Этот скрипт запускает все другие утилиты необходимые для сборки вашего кода, и функционирует как прямая замена стандартного компилятора gcc или clang. Его можно вызвать используя команды ./emcc или ./em++.

Note

В Windows используется другой синтаксис emcc или em++. Остаток этого обучния использует Linux команды(./emcc).

В следующей главе вам потребуется открыть консоль:

  • В Linux или macOS, используйте Terminal.
  • В Windows запустите Emscripten Command Prompt, этот терминал уже настроен на использование активного инструментария Emscripten. Для получение доступа к терминалу, наберите Emscripten в стартовом экране Windows 8, и выберите Emscripten Command Prompt.

Откройте директорию emscripten внутри SDK. Эта директория находится в корневой директории emsdk (emsdk root directory), обычно <emsdk root directory>/fastcomp/emscripten/ (для “fastcomp” реализации; для новой реализации upstream - <emsdk root directory>/upstream/emscripten/). Примеры ниже, будут использовать файлы относительно этой директории.

Note

В старых версиях emscripten стурктура каталогов другая: появляется номер версии, и исчезает реализация (fastcomp/upstream), т.е. каталог будет <emsdk root directory>/emscripten/1.20.0/.

Проверка установленной версии

Если вы не делали это ранее, запустите:

./emcc -v

Вывод может содержать предупреждения об отсутствии некоторых инструментов, см. Verifying the Emscripten Development Environment за помощью. Иначе, можете переходить к следующей секции, для сборки проекта.

Запуск Emscripten

Теперь вы можете скомпилировать первый C/C++ файл в JavaScript.

Сперва, изучим файл который будет скомпилирован: hello_world.c. Это простейший тест в SDK, как вы можете видеть он всего лишь печатает “hello, world!” в консоль.

/*
 * 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;
}

Что бы собрать JavaScript версию этого кода, просто укажите C/C++ файл после команды emcc (используйте em++ что бы принудить компилировать C++):

./emcc tests/hello_world.c

Должна произойти генерация двух файлов: a.out.js и a.out.wasm. Второй это WebAssembly файл содержащий скомпилированный код. Первый файл предназначен для загрузки и выполнения WebAssembly кода. Вы можете запустить его с помощью node.js:

node a.out.js

В консоле должно отобразится “hello, world!”, как и ожидалось.

Note

Более старые версии node.js не имеют поддержки WebAssembly. В этом случае вы увидите подсказку рекомендующую выполнить сборку с флагом -s WASM= что бы выключить поддержку WebAssembly. В этом случае emscripten будет генерировать JavaScript код. Обычно, WebAssemlby лучший выбор, он имеет общирную поддержку браузеров, гораздо произовдительнее и генерирует файлы меньшего размера (поэтому emscripten использует его по умолчанию), но иногда может потребоваться запустить ваш код в окуржении не поддержвающем WebAssembly.

Tip

Если возникает ошибка в момент вызова emcc, запустите его с флагом -v для вывода более подробной информации.

Note

В этом разделе и далее, мы используем некоторые файлы из директории test/. Этот каталог содержит файлы из тестового набора Emscripten. Некоторые могут быть использованы самостоятельно, другие с использованием утилиты тестирования, см. Emscripten Test Suite для подробной информации.

Генерация HTML

Emscripten так же может генерировать HTML для тестирования. Для генерации HTML, используйте флаг -o (output) и укажите имя целевого файла:

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

Теперь вы можете открыть hello.html в браузере.

Note

К сожалению некоторые барузеры (включая Chrome, Safari, и Internet Explorer) не поддерживает file:// в XHR запросах, и не могут загрузить дополнительные файлы необходимые для HTML (например, .wasm или упакованные данные, как объясняется ниже). Для таких барузеров Вам потребуется запустить веб сервер для отдачи этих файлов. Простейший способ использовать SimpleHTTPServer (в текущей директории выполните python -m SimpleHTTPServer 8080 и откройте http://localhost:8080/hello.html).

Когда HTML будет загружен в браузер, вы увидите текстовое поле отображающее вызов printf() из нативного кода.

HTML вывод не ограничен выводом только текста. Вы можете использовать SDL API для отображения разноцветного куба внутри <canvas> элемента (в браузерах которые поддерживают его). Соберите hello_world_sdl.cpp тест и обновите браузер:

./emcc tests/hello_world_sdl.cpp -o hello.html

Исходный код второго примера приведен ниже:

// 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

extern "C" 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;
}

Using files

Note

Your C/C++ code can access files using the normal libc stdio API (fopen, fclose, etc.)

JavaScript is usually run in the sandboxed environment of a web browser, without direct access to the local file system. Emscripten simulates a file system that you can access from your compiled C/C++ code using the normal libc stdio API.

Files that you want to access should be preloaded or embedded into the virtual file system. Preloading (or embedding) generates a virtual file system that corresponds to the file system structure at compile time, relative to the current directory.

The hello_world_file.cpp example shows how to load a file (both the test code and the file to be loaded shown below):

// 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("tests/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.
==

Note

The example expects to be able to load a file located at tests/hello_world_file.txt:

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

We compile the example from the directory “above” tests to ensure that virtual filesystem is created with the correct structure relative to the compile-time directory.

The following command is used to specify a data file to preload into Emscripten’s virtual file system — before running any compiled code. This approach is useful because Browsers can only load data from the network asynchronously (except in Web Workers) while a lot of native code uses synchronous file system access. Preloading ensures that the asynchronous download of data files is complete (and the file is available) before compiled code has the opportunity to access the Emscripten file system.

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

Run the above command, then open hello.html in a web browser to see the data from hello_world_file.txt being displayed.

For more information about working with the file system see the File System Overview, File System API and Synchronous Virtual XHR Backed File System Usage.

Optimizing code

Emscripten, like gcc and clang, generates unoptimized code by default. You can generate slightly-optimized code with the -O1 command line argument:

./emcc -O1 tests/hello_world.cpp

The “hello world” code created in a.out.js doesn’t really need to be optimized, so you won’t see a difference in speed when compared to the unoptimized version.

However, you can compare the generated code to see the differences. -O1 applies several minor optimizations and removes some runtime assertions. For example, printf will have been replaced by puts in the generated code.

The optimizations provided by -O2 (see here) are much more aggressive. If you run the following command and inspect the generated code (a.out.js) you will see that it looks very different:

./emcc -O2 tests/hello_world.cpp

For more information about compiler optimization options see Optimizing Code and the emcc tool reference.

Emscripten Test Suite and Benchmarks

Emscripten has a comprehensive test suite, which covers virtually all Emscripten functionality. These tests are an excellent resource for developers as they provide practical examples of most features, and are known to build successfully on the master branch.

See Emscripten Test Suite for more information.

General tips and next steps

This tutorial walked you through your first steps in calling Emscripten from the command line. There is, of course, far more you can do with the tool. Below are other general tips for using Emscripten: