Skip to content
kripken edited this page Dec 6, 2011 · 37 revisions

Building Projects

Building large projects with Emscripten is generally very simple, the process looks like

   emconfiguren.py ./configure
   make
   emscripten.py project.bc > project.js

That is, you run configure and make pretty much normally, the only changes being running configure through tools/emconfiguren.py (which tells configure and later make to use clang in a way that generates proper LLVM bitcode), and running emscripten.py afterwards to convert the LLVM bitcode to JS. For more details, see the emmaken tool (tools/emmaken.py), the latest docs are inside that file.

While the build process is very simple, there is one major difference with a 'normal' build process: Emscripten does not generate JavaScript at every compilation. Instead, it generates LLVM bitcode. We then use the LLVM linker to link files together, and only at the very last step do we convert LLVM bitcode to JavaScript. All of this is done "under the hood", so you don't need to bother with it (but in some cases it might have an effect - for example, compilation options that make sense for the translation to JS need to be given at the last step, not when building the separate files). The reasons for this approach are simplicity for the compiler, and efficiency of the generated code (we can optimize better when we see everything at once). However, this might be problematic for very large projects, to help with that Emscripten has support for dynamic linking, see Dynamic Linking.

Examples

You can see how the large tests in tests/runner.py are built - the C/C++ projects are built using configure, make and so forth (using emmaken.py). Specifically, the large tests include: freetype, openjpeg, zlib and poppler.

Also worth looking at the build scripts in the following projects:

The documentation below gives more low-level details of the process, most of which you do not need if you use emmaken.py (but it might help to understand things).

Detailed Overview

  • The basic idea is replacing the normal compiler with Clang or llvm-gcc, and telling it to emit LLVM bitcode. Also replace the linker with llvm-link.

    • In CMake this can be done with:

          SET(CMAKE_C_COMPILER "/..PATH../llvm-gcc")
          SET(CMAKE_CXX_COMPILER "/..PATH../llvm-g++")
          SET(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -emit-llvm)`
          SET(CMAKE_LINKER "/..PATH../llvm-link")
          SET(CMAKE_CXX_LINKER "/..PATH../llvm-link")
          SET(CMAKE_C_LINK_EXECUTABLE "/..PATH../llvm-link")
          SET(CMAKE_CXX_LINK_EXECUTABLE "/..PATH../llvm-link")
          SET(CMAKE_AR "/..PATH../llvm-link")
          SET(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> -S -o <TARGET> <LINK_FLAGS> <OBJECTS>")
          SET(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> -S -o <TARGET> <LINK_FLAGS> <OBJECTS>")
          SET(CMAKE_RANLIB "echo") # Hackish way to disable it
      
    • In a Makefile, this can be done with:

          C = /..PATH../llvm-gcc
          CXX = /..PATH../llvm-g++
          LINKER = /..PATH../llvm-link
          CXX_LINKER = /..PATH../llvm-link
      
          # Undefining architecture stuff means code will not generate ASM
          CXXFLAGS=' -emit-llvm -U__i386__ -U__x86_64__'
      
  • Build the project using its normal build system, makefiles, etc. Note that it might fail if it tries to generate executables; you should probably just create libraries. Use make VERBOSE=1 to see the commands being run, so you can make sure you are using the right compiler, etc.

  • Run llvm-link to combine libraries.

  • Compile the generated .bc file using Emscripten.

Other Tips

  • If you manually link your .bc files yourself, do NOT use llvm-ld to link your files. It will also optimize them, in potentially dangerous ways (for us). Instead, use llvm-link.

Clone this wiki locally