Emscripten CMake integration - under the hood
2021-10-18 21:52
Take any existing CMake-based C/C++ project, and chances are that you will be able to use Emscripten, and call emcmake cmake, and end up with a JS file that you can run in the browser.
This seems a bit magical, so let’s peek under the hood.
The emcmake shell script is a wrapper for a python script emcmake.py. This is a small script, and the main thing it does is to call cmake with the flag -DCMAKE_TOOLCHAIN_FILE=<path/to/Emscripten.cmake>. Emscripten.cmake is where a lot of the magic happens.
The CMAKE_TOOLCHAIN_FILE variable specifies a path to a “toolchain” file, which is a collection of compilers and utilties to be used by CMake to build the project. Emscripten behaves as a cross compiler in this case.
Emscripten.cmake does a lot of stuff, at a high level, it sets a bunch of variables so that existing C/C++ projects that are not aware of WebAssembly/JavaScript can compile just fine:
- things like
set(WIN32),set(APPLE), andset(UNIX 1), because a lot of times Emscripten can emulate this well enough - find where the Emscripten compiler actually is, Emscripten relies on other tools like LLVM and Binaryen, which will be found adjacent to where Emscripten is
- sets the compiler to be
emcc, that way whenever a C/C++ file is compiled,emccis used, rather thanclangorgcc - sets various C/C++ compile features
- sets various size of data structures
- helper functions to link JS libraries into the output JS file (pre/post/link)
- sets node.js to be the executable for the output JS file
With all these setup, when we eventually ask CMake to build the project, individual C/C++ files are compiled using emcc, which generates intermediate Wasm files, and then those files are linked again by emcc, to generate .wasm and .js files.