Skip to content
kripken edited this page Nov 16, 2011 · 47 revisions

Getting Started

TL;DR

  • Get LLVM and Clang, version 3.0
  • Get the trunk versions of SpiderMonkey and V8. Older versions will not work!
  • Run the emscripten test runner, python tests/runner.py. On first run it will create ~/.emscripten. Edit that so the paths are to the right places
  • Run the test runner and see that the tests pass. Or run just an individual test with python tests/runner.py clang_0_0.test_hello_world

Preparations

Operating System

Emscripten is developed and tested on Linux, but should work on every platform where you can run a JS engine, LLVM, and Python.

LLVM & Clang

You will need to get and build LLVM and Clang. You should really read the official Clang installation guide, but here is a short sketch:

  • Get LLVM 3.0. Other versions may not work!
  • Get Clang 3.0 and place it in your LLVM directory under tools/clang (note: not clang-3.0 or anything like that - simply clang)
  • Make a cbuild/ directory inside the LLVM (not Clang) directory
  • In cbuild/, run cmake .. (or ../configure) and then make. This will build LLVM and Clang.

LLVM-GCC

As of LLVM 3.0, llvm-gcc is deprecated, so Emscripten doesn't officially support it either. It might still work, or it might not.

JavaScript Engine

Next you need a JavaScript engine like SpiderMonkey (building guide) or V8 (building guide). Get one of those and build it. Or better yet get both. Note that you should get a recent version - from hg for SpiderMonkey, and svn for V8. For V8, build it with scons d8 to build the d8 shell.

Running the Automated Tests

  • In the top directory, run python tests/runner.py. That will run all the tests. They should all pass. This will take a long time - we have a lot of tests!
  • Note that the first time you run the tests (or emscripten.py), it will create a config file, ~/.emscripten. Its initial values are taken from settings.py. You should edit the paths in ~/.emscripten to those relevant for you.
  • You can do python tests/runner.py clang_0_0.test_X where X is one of the tests in runner.py (for example, intvars or fannkuch). This will run that single test. You can then view the generated code by going to the temp directory (TEMP_DIR) mentioned in ~/.emscripten, and looking at src.cpp.o.js. The other files in that directory can also be useful to figure out what went wrong.
    • clang_0_0 in the example above defines how to compile it. First is the compiler name, clang or llvm_gcc. Then whether to run LLVM optimizations (1 for yes, 0 for no), and whether to optimize & reloop (1 for yes, 0 for no).
  • If you get errors on all the tests, there is probably something wrong in the config file that is mentioned above. To debug that, run a single test, say python tests/runner.py clang_0_0.test_hello_world, and look at the output files in the temp directory as mentioned above.

Compiling Code

The are two phases to compiling C/C++ (or something else) to JS:

  • C/C++ => LLVM bitcode
  • LLVM bitcode => JS

We have scripts to help with both:

  • C/C++ => LLVM bitcode: emmaken.py wraps around clang (the LLVM compiler), and gets it to generate LLVM bitcode from your project.
  • LLVM bitcode => JS: emscripten.py transforms LLVM bitcode into JS (writing to stdout).

In more detail, the stages are:

  • C/C++ => LLVM bitcode: This stage is just about compiling the source code to LLVM bitcode. No optimization is done at this stage.
  • LLVM bitcode => JS: Optionally, some LLVM optimizations can be done. Then Emscripten compiles the LLVM bitcode to JavaScript, optionally optimizing it. Typically you will then further optimize it afterwards using the Emscripten eliminator or Closure Compiler.

Now let's go back to the beginning and see what the actual commands would look like. For the first stage, emmaken.py is in tools/. You can either use it just like you would use clang or gcc, for example

python tools/emmaken.py myfile.cpp -o myfile.bc

will compile myfile.cpp to LLVM bitcode in myfile.bc. You can also tell your project to use emmaken.py instead of gcc/ld/ranlib/ar, and then build your project normally using configure/make (see docs in emmaken.py). Note that emmaken.py will tell clang to use the emscripten bundled headers, not your system headers, so that compilation is portable.

After you have LLVM bitcode, you can use emscripten.py to compile it to JS, for example with

python emscripten.py myfile.bc > myfile.js

Note that by default this is unoptimized code! To optimize it, see Optimizing-Code.

You can then run your code using a JS engine shell, for example one of

js -m -n myfile.js arg1 arg2
d8 myfile.js -- arg1 arg2

Notes:

  • For building large projects, see Building Projects.

  • If you have issues with compiling or with the generated code, see the Debugging page.

  • As just mentioned, Emscripten is meant to be used with the included headers. The included headers are a complete environment for building, which allows you to compile code for purposes of Emscripten from any OS (this is similar to say the Android NDK: it ships with its own header files, so building for Android can be done from any OS). So it shouldn't matter what local header files you have, and so forth - you can build on Linux, OS X or Windows, and the results should be the same. Note that the headers are fairly similar to a *NIX-type environment, because that was easiest to do.

  • You can also run clang directly, instead of with emmaken. In this case, you should run it with -emit-llvm. You should also use the -g flag when compiling to preserving symbols, since Emscripten uses these to calculate struct field offsets. After that, to generate human-readable assembly (.ll) from a .bc file, you should translate that to .ll using llvm-dis with the -show-annotations option (emscripten.py will do this for you if necessary). Note that the automatic test runner, tests/runner.py, does all of this during the tests, so reading what it does can be a useful way to learn.

  • Note that Emscripten expects the output to be formatted and annotated the way llvm-dis -show-annotations does. So if you get .ll output from something else, you should run it through llvm-as (to generate LLVM bitcode) and then llvm-dis -show-annotations (to get properly formatted and annotated .ll).

Running Emscriptened Code on the Web

By default the generated code is meant to run in a console engine, which is useful for the automated tests (for example, arguments is then equal to the command line arguments). To run that code in a browser, a few trivial changes should be done. For an example, see demos/cubescript.html and demos/cubescript.js. The basic steps are:

  • If you want to pass command like arguments, define scriptArgs to an array of strings.
  • Likely you will want to disable the call to run() (the entry point) by setting INVOKE_RUN to 0 in settings.js before compilation and call it yourself at the right time.
  • Connect your web JavaScript to the Emscriptened JavaScript. Currently this must be done manually. Again, see the cubescript demo mentioned before, but in general:
    • Calling Emscriptened code requires you to use the name-mangled function names (thirdparty/demangler.py can help unmangle them).
    • Parameters need to be converted. Integers and floats should be fine, but strings need to go through allocate(intArrayFromString('foo')) (which makes a string into an int array, then copies that to the heap and returns a pointer to it). Arrays should go through allocate([1, 2, 3]).
  • Optionally, provide some print() function, if you expect the code to call that function to show output. For lower level I/O controls, see the Filesystem Guide.