-
Notifications
You must be signed in to change notification settings - Fork 3
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 between 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.
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:
- https://github.com/kripken/ammo.js/blob/master/make.py
- https://github.com/mbebenita/Broadway/blob/master/Avc/make.py
- https://github.com/kripken/j2k.js/blob/master/make.py
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).
-
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=1to see the commands being run, so you can make sure you are using the right compiler, etc. -
Run
llvm-linkto combine libraries. -
Compile the generated
.bcfile using Emscripten.
- If you manually link your .bc files yourself, do NOT use
llvm-ldto link your files. It will also optimize them, in potentially dangerous ways (for us). Instead, usellvm-link.