Building Projects

Emscripten provides helper scripts (emconfigure, emcmake, and emmake) that configure your build system to use emcc as a drop-in replacement for gcc — in most cases the rest of your project’s current build system remains unchanged.

Integrating with a build system

To build using Emscripten you need to replace gcc with emcc in your build system. This is done using emconfigure (for configure/autotools projects) or emcmake (for CMake projects), which set the appropriate environment variables or CMake toolchain file so that emcc is used.

Configure / Autoconf-based projects

Consider the case where you normally build a configure-based project with the following commands:

./configure
make

Tip

If you’re not familiar with these build commands, the article The magic behind configure, make, make install is a good primer.

To build with Emscripten, you would instead use the following commands:

# Run emconfigure with the normal configure command as an argument.
emconfigure ./configure

# Run make to generate Wasm object files.
make

# Compile the linked code generated by make to JavaScript + WebAssembly.
# 'project.o' should be replaced with the make output for your project, and
# you may need to rename it if it isn't something emcc recognizes
# (for example, it might have a different suffix like 'project.so' or
# 'project.so.1', or no suffix like just 'project' for an executable).
# If the project output is a library, you may need to add your 'main.c' file
# here as well.
# [-Ox] represents build optimisations (discussed in the next section).
emcc [-Ox] project.o -o project.js

CMake-based projects

Consider the case where you normally build a CMake-based project with the following commands:

cmake -B build
cmake --build build

To build with Emscripten, use emcmake to configure CMake to use the Emscripten toolchain file:

# Run emcmake with the normal cmake command as an argument.
emcmake cmake -B build

# Build the project using cmake --build (or make).
cmake --build build

Note

emcmake automatically passes the Emscripten CMake toolchain file (-DCMAKE_TOOLCHAIN_FILE=.../Emscripten.cmake) to CMake.

Other build systems

If your build system doesn’t use configure or CMake, then you can omit the configuration step and run emmake make (although then you may need to edit the Makefile manually if compiler variables like CC or CXX are hardcoded).

Tip

If you use emconfigure or emcmake during the configuration phase, emmake is generally not needed during the build phase because the compiler settings are stored in the generated build files. emmake is primarily needed when building projects that do not have a separate configuration phase.

Make generates Wasm object files. It may also link the object files into libraries and/or Wasm executables. Unless such a build system has been modified to also emit JavaScript output, you will need to run an additional emcc command as shown above, that will emit the final runnable JavaScript and WebAssembly.

Note

The file output from make might have a different suffix: .a for a static library archive, .so for a shared library, .o for object files (these file extensions are the same as gcc would use for the different types). Irrespective of the file extension, these files contain something that emcc can compile into the final JavaScript + WebAssembly (typically the contents will be Wasm object files, but if you build with LTO then they will contain LLVM bitcode).

Note

Some build systems may not properly emit Wasm object files using the above procedure, and you may see is not a valid input file warnings. You can run file to check what a file contains (also you can manually check if the contents start with \0asm to see if they are Wasm object files, or BC if they are LLVM bitcode). It is also worth running emmake make VERBOSE=1 which will print out the commands it runs - you should see emcc being used, and not the native system compiler. If emcc is not used, you may need to modify the configure or cmake scripts.

Emscripten linker output files

Unless run with certain specific flags (such as -c, -S, -r, or -shared) emcc will run the link phase which can produce more than just one file. The set of produced files changes depending on the final flags passed to emcc and the name of the specified output file. Here is a cheat sheet of which files are produced under which conditions:

  • emcc ... -o output.html builds a output.html file as an output, as well as an accompanying output.js launcher file, and a output.wasm WebAssembly file.

  • emcc ... -o output.js omits generating a HTML launcher file (expecting you to provide it yourself if you plan to run in browser), and produces two files, output.js and output.wasm. (that can be run in e.g. node.js shell)

  • emcc ... -o output.wasm omits generating either JavaScript or HTML launcher file, and produces a single Wasm file built in standalone mode as if the -sSTANDALONE_WASM setting had been used. The resulting file expects to be run with the WASI ABI - in particular, as soon as you initialize the module you must manually invoke either the _start export or (in the case of --no-entry) the _initialize export before doing anything else with it.

  • emcc ... -o output.{html,js} -sWASM=0 causes the compiler to target JavaScript, and therefore a .wasm file is not produced.

  • emcc ... -o output.{html,js} --emit-symbol-map produces a file output.{html,js}.symbols if WebAssembly is being targeted (-sWASM=0 not specified), or if JavaScript is being targeted and -Os, -Oz or -O2 or higher is specified, but debug level setting is -g1 or lower (i.e. if symbols minification did occur).

  • emcc ... -o output.{html,js} -gsource-map generates a source map file output.wasm.map. If targeting JavaScript with -sWASM=0, the filename is output.{html,js}.map.

  • emcc ... -o output.{html,js} --preload-file xxx directive generates a preloaded MEMFS filesystem file output.data.

  • emcc ... -o output.{html,js} -sWASM={0,1} -sSINGLE_FILE merges JavaScript and WebAssembly code in the single output file output.{html,js} (in base64) to produce only one file for deployment. (If paired with --preload-file, the preloaded .data file still exists as a separate file)

This list is not exhaustive, but illustrates most commonly used combinations.

Note

Regardless of the name of the output file emcc will always perform linking and produce a final executable, unless a specific flags (e.g. -c) direct it to do something else. This differs to previous behaviour where emcc would default to combining object files (essentially assuming -r) unless given a specific executable extension (e.g. .js or .html).

Building projects with optimizations

Emscripten performs compiler optimization at two levels: each source file is optimized by LLVM as it is compiled into an object file, and then JavaScript/WebAssembly-specific optimizations are applied when converting object files into the final JavaScript/WebAssembly.

In order to properly optimize code, it is usually best to use the same optimization flags and other compiler options when compiling source to object code, and object code to JavaScript (or HTML).

Consider the examples below:

# Sub-optimal - JavaScript/WebAssembly optimizations are omitted
emcc -O2 a.cpp -c -o a.o
emcc -O2 b.cpp -c -o b.o
emcc a.o b.o -o project.js

# Sub-optimal - LLVM optimizations omitted
emcc a.cpp -c -o a.o
emcc b.cpp -c -o b.o
emcc -O2 a.o b.o -o project.js

# Usually the right thing: The same options are provided at compile and link.
emcc -O2 a.cpp -c -o a.o
emcc -O2 b.cpp -c -o b.o
emcc -O2 a.o b.o -o project.js

However, sometimes you may want slightly different optimizations on certain files:

# Optimize the first file for size, and the rest using `-O2`.
emcc -Oz a.cpp -c -o a.o
emcc -O2 b.cpp -c -o b.o
emcc -O2 a.o b.o -o project.js

Note

Unfortunately each build-system defines its own mechanisms for setting compiler and optimization methods. You will need to work out the correct approach to set the LLVM optimization flags for your system.

  • Some build systems have a flag like ./configure --enable-optimize.

JavaScript/WebAssembly optimizations are specified in the final step (sometimes called “link”, as that step typically also links together a bunch of files that are all compiled together into one JavaScript/WebAssembly output). For example, to compile with -O1:

# Compile the object file to JavaScript with -O1 optimizations.
emcc -O1 project.o -o project.js

Building projects with debug information

Building a project containing debug information requires that debug flags are specified for both the LLVM and JavaScript compilation phases.

To make Clang and LLVM emit debug information in object files you need to compile the sources with -g (exactly the same as with clang or gcc normally).

Note

Each build-system defines its own mechanisms for setting debug flags. To get Clang to emit LLVM debug information, you will need to work out the correct approach for your system.

  • Some build systems have a flag like ./configure --enable-debug. In CMake-based build systems, set the CMAKE_BUILD_TYPE to "Debug".

To get emcc to include the debug information present in object files when generating the final JavaScript and WebAssembly, your final emcc command must specify -g or one of the -gN debug level options.

# Compile the Wasm object file to JavaScript+WebAssembly, with debug info
# -g or -gN can be used to set the debug level (N)
emcc -g project.o -o project.js

For more general information, see the topic Debugging.

Using libraries

Built-in support is available for a number of standard libraries: libc, libc++ and