Использовать Emscripten на базов уровне достаточно просто. Это обучение расскажет о необходимых шагах для компиляции первого проекта из комнадной строки. Кроме того, объяснит как работать с файлами и использовать оптимизационные флаги компиляции.
Убедитесь что вы скачали и установили Emscripten (способ установки зависит от операционной системы: Linux, Windows, or Mac).
Emscripten доступен с использованием Emscripten Compiler Frontend (emcc). Этот скрипт запускает все другие утилиты необходимые для сборки вашего кода, и функционирует как прямая замена стандартного компилятора gcc или clang. Его можно вызвать используя команды ./emcc
или ./em++
.
Note
В Windows используется другой синтаксис emcc
или em++
. Остаток этого обучния использует Linux команды(./emcc
).
В следующей главе вам потребуется открыть консоль:
Откройте директорию 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 за помощью. Иначе, можете переходить к следующей секции, для сборки проекта.
Теперь вы можете скомпилировать первый 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 для подробной информации.
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;
}
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.
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 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.
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:
--pre-js
option works, search for --pre-js
in the test suite: the test suite is extensive and there are likely to be at least some examples.