Sam Hooke

Setting up Emscripten with CMake in Git Bash on Windows 10

Install prerequisites §

Install Python 3.10 for Windows.

Open Git Bash (included with Git for Windows).

Create alias for python and python3 following steps from here, i.e.:

echo "alias python='winpty python.exe'" >> ~/.bashrc
echo "alias python3='winpty python.exe'" >> ~/.bashrc

Then restart Git Bash so it automatically loads the .bashrc.

(Note that if you’ve not done this before, Git Bash may give a warning next time you start to tell you it has automatically created a ~/.bash_profile which loads the .bashrc for you).

In Windows, go to Settings -> Manage app execution aliases. Disable “App installer” for python.exe and python3.exe, to prevent the Windows Store from popping up every time you run python or python3 in Git Bash.

Follow Emscripten instructions using Git Bash §

Follow along with the official instructions, inside Git Bash.

Clone the emsdk repo:

git clone

(Or, add it as a git submodule to your existing repo):

git submodule add

Enter the emsdk directory:

cd emsdk

Run ./emsdk install latest:

$ ./emsdk install latest
Resolving SDK alias 'latest' to '3.1.8'
Resolving SDK version '3.1.8' to 'sdk-releases-upstream-8c9e0a76ebed2c5e88a718d43e8b62452def3771-64bit'
Installing SDK 'sdk-releases-upstream-8c9e0a76ebed2c5e88a718d43e8b62452def3771-64bit'..
Installing tool 'node-14.18.2-64bit'..
Downloading: C:/MyProject/emsdk/zips/ from, 30570907 Bytes
Unpacking 'C:/MyProject/emsdk/zips/' to 'C:/MyProject/emsdk/node/14.18.2_64bit'
Done installing tool 'node-14.18.2-64bit'.
Installing tool 'python-3.9.2-nuget-64bit'..
Downloading: C:/MyProject/emsdk/zips/ from, 14413267 Bytes
Unpacking 'C:/MyProject/emsdk/zips/' to 'C:/MyProject/emsdk/python/3.9.2-nuget_64bit'
Done installing tool 'python-3.9.2-nuget-64bit'.
Installing tool 'java-8.152-64bit'..
Downloading: C:/MyProject/emsdk/zips/ from, 69241499 Bytes
Unpacking 'C:/MyProject/emsdk/zips/' to 'C:/MyProject/emsdk/java/8.152_64bit'
Done installing tool 'java-8.152-64bit'.
Installing tool 'releases-upstream-8c9e0a76ebed2c5e88a718d43e8b62452def3771-64bit'..
Downloading: C:/MyProject/emsdk/zips/ from, 414615769 Bytes
Unpacking 'C:/MyProject/emsdk/zips/' to 'C:/MyProject/emsdk/upstream'
Done installing tool 'releases-upstream-8c9e0a76ebed2c5e88a718d43e8b62452def3771-64bit'.
Done installing SDK 'sdk-releases-upstream-8c9e0a76ebed2c5e88a718d43e8b62452def3771-64bit'.

Run ./emsdk activate latest:

$ ./emsdk activate latest
The changes made to environment variables only apply to the currently running shell instance. Use the 'emsdk_env.bat' to re-enter this environment later, or if you'd like to register this environment permanently, rerun this command with the option --permanent.
Resolving SDK alias 'latest' to '3.1.8'
Resolving SDK version '3.1.8' to 'sdk-releases-upstream-8c9e0a76ebed2c5e88a718d43e8b62452def3771-64bit'
Setting the following tools as active:

Run source ./

$ source ./
Adding directories to PATH:
PATH += /c/MyProject/emsdk
PATH += /c/MyProject/emsdk/upstream/emscripten
PATH += /c/MyProject/emsdk/node/14.18.2_64bit/bin

Setting environment variables:
PATH = /c/MyProject/emsdk:/c/MyProject/emsdk/upstream/emscripten:/c/MyProject/emsdk/node/14.18.2_64bit/bin:/c/Program Files/Git/mingw64/bin:/c/Program Files/Git/usr/local/bin:/c/Program Files/Git/usr/bin:/c/WINDOWS/system32:/c/WINDOWS:/c/WINDOWS/System32/Wbem:/c/WINDOWS/System32/WindowsPowerShell/v1.0:/c/Program Files/Java/jre7/bin:/c/Program Files (x86)/Windows Live/Shared:/c/WINDOWS/System32/OpenSSH:/c/Program Files/Git/cmd:/c/Program Files (x86)/CMake/bin
EMSDK = C:/MyProject/emsdk
EM_CONFIG = C:\MyProject\emsdk\.emscripten
EMSDK_NODE = C:/MyProject/emsdk/node/14.18.2_64bit/bin/node.exe
EMSDK_PYTHON = C:/MyProject/emsdk/python/3.9.2-nuget_64bit/python.exe
JAVA_HOME = C:/MyProject/emsdk/java/8.152_64bit

Tutorial §

Now follow through the Emscripten tutorial.

$ cd upstream/emscripten
$ ./emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.8 (3ff7eff373d31d8c0179895165462019221f192e)
clang version 15.0.0 ( 80ec0ebfdc5692a58e0832125f2c6a991df9d63f)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: C:\MyProject\emsdk\upstream\bin

Command line “hello world” §

Build the included hello world:

$ ./emcc tests/hello_world.c
shared:INFO: (Emscripten: Running sanity checks)

Run it:

$ node a.out.js
hello, world!

HTML “hello world” §

Build as HTML:

$ ./emcc tests/hello_world.c -o hello.html

Note that on most browsers you can’t just open the hello.html because they do not support file:// XHR requests. As a workaround, instead run python -m http.server in the build directory to start a Python3 webserver. Then go in the browser to: http://localhost:8000/hello.html

Setting up SDL §

SDL1 vs SDL2 in Emscripten §

SDL1 is included in Emscripten but SDL2 is not. However, SDL2 is included in ports.

Here is an SDL2 example.

  • Note that use of -sUSE_SDL=2 to enable #include "SDL2/SDL.h" for using SDL2.
  • Note that SDL2_image and SDL2_ttf are separate ports.

SDL2 in Emscripten ports §

Run ./emcc --show-ports to see all available ports:

$ ./emcc --show-ports
Available ports:
    Boost headers v1.70.0 (USE_BOOST_HEADERS=1; Boost license)
    bullet (USE_BULLET=1; zlib license)
    bzip2 (USE_BZIP2=1; BSD license)
    freetype (USE_FREETYPE=1; freetype license)
    giflib (USE_GIFLIB=1; MIT license)
    harfbuzz (USE_HARFBUZZ=1; MIT license)
    icu (USE_ICU=1; Unicode License)
    libjpeg (USE_LIBJPEG=1; BSD license)
    libmodplug (USE_MODPLUG=1; public domain)
    libpng (USE_LIBPNG=1; zlib license)
    mpg123 (USE_MPG123=1; zlib license)
    ogg (USE_OGG=1; zlib license)
    regal (USE_REGAL=1; Regal license)
    SDL2 (USE_SDL=2; zlib license)
    SDL2_gfx (zlib license)
    SDL2_image (USE_SDL_IMAGE=2; zlib license)
    SDL2_mixer (USE_SDL_MIXER=2; zlib license)
    SDL2_net (zlib license)
    SDL2_ttf (USE_SDL_TTF=2; zlib license)
    vorbis (USE_VORBIS=1; zlib license)
    zlib (USE_ZLIB=1; zlib license)

If using cmake, need to add extra options to enable the ports, e.g. -sUSE_SDL=2:

Something like:

if( ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
    set(USE_FLAGS "-s USE_SDL=2 -s USE_FREETYPE=1")
    find_package(SDL2 REQUIRED)
    find_package(Freetype REQUIRED)

Note the use of the if statement for the flags that only apply to Emscripten.

Building §

Overview §

First, for comparison, if you’re not using Emscripten you might be used to building by running the following commands:

cmake -S . -B build
cmake --build build

(Note that this way is preferred over the mkdir build && cd build && cmake .. && cmake --build method that seems to be most commonly used).

By comparison, for Emscripten we do:

emcmake cmake -S . -B build-em
cmake --build build-em

(Note that it is not necessary to use build-em as the directory name, but I prefer using it as a clear distinction from the build directory commonly used for native builds).

What does emcmake do? §

The emcmake command is a very simple wrapper, which adds some additional options to the subsequent call to cmake:

  • -G "MinGW Makefiles", which is necessary for generating the right makefiles for Emscripten.
  • -DCMAKE_TOOLCHAIN_FILE=C:\MyProject\emsdk\upstream\emscripten\cmake\Modules\Platform\Emscripten.cmake.
  • -DCMAKE_CROSSCOMPILING_EMULATOR=C:/MyProject/emsdk/node/14.18.2_64bit/bin/node.exe;--experimental-wasm-threads

There is no need to specify the location of SDL2 (e.g. by passing in -DSDL2_PATH="C:\mingw_dev_lib_32"), because emcmake automatically finds SDL2 since we have -sUSE_SDL=2 enabled.

If you do leave the -DSDL2_PATH option in, it will generate the warning:

CMake Warning:
  Manually-specified variables were not used by the project:


How not to build §

The emcmake cmake -S . -B build-em command only needs to have been called once. Then you can just build as normal with:

cmake --build build-em

Note that you should not use emcmake for this command, else you will get the error:

$ emcmake cmake --build build-em
configure: cmake --build build-em -DCMAKE_TOOLCHAIN_FILE=C:\MyProject\emsdk\upstream\emscripten\cmake\Modules\Platform\Emscripten.cmake -DCMAKE_CROSSCOMPILING_EMULATOR=C:/MyProject/emsdk/node/14.18.2_64bit/bin/node.exe;--experimental-wasm-threads -G "MinGW Makefiles"
Unknown argument -DCMAKE_TOOLCHAIN_FILE=C:\MyProject\emsdk\upstream\emscripten\cmake\Modules\Platform\Emscripten.cmake

Recap §

  • There is some tweaking required to get Emscripten to play nicely under Git Bash, but it’s quite straight-forward.
  • Emscripten is installed via git, either by cloning or via a submodule.
  • The first time you install Emscripten, you must ./emsdk install latest and ./emsdk activate latest.
  • Any time you open a new shell, you must source ./ for the Emscripten commands to work.
  • SDL1 is included in Emscripten, but SDL2 is included in Emscripten ports, which requires putting additional options in your CMakeLists.txt file.
  • Use emcmake to wrap the cmake call when generating the make files.
  • Do not use emcmake to wrap the cmake call when performing a build.

These are rough notes from whatever I was working on, interested in or thinking about at the time. They vary greatly in quality and length, but prove useful to me, and hopefully to you too!

← Previous: Network adapter disappears after reboot
Next: Setting up Emscripten with CMake on Linux →