These notes cover in detail how to set 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 https://github.com/emscripten-core/emsdk.git
(Or, add it as a git submodule to your existing repo):
git submodule add https://github.com/emscripten-core/emsdk.git
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/node-v14.18.2-win-x64.zip from https://storage.googleapis.com/webassembly/emscripten-releases-builds/deps/node-v14.18.2-win-x64.zip, 30570907 Bytes
[----------------------------------------------------------------------------]
Unpacking 'C:/MyProject/emsdk/zips/node-v14.18.2-win-x64.zip' 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/python-3.9.2-4-amd64+pywin32.zip from https://storage.googleapis.com/webassembly/emscripten-releases-builds/deps/python-3.9.2-4-amd64+pywin32.zip, 14413267 Bytes
[----------------------------------------------------------------------------]
Unpacking 'C:/MyProject/emsdk/zips/python-3.9.2-4-amd64+pywin32.zip' 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/portable_jre_8_update_152_64bit.zip from https://storage.googleapis.com/webassembly/emscripten-releases-builds/deps/portable_jre_8_update_152_64bit.zip, 69241499 Bytes
[----------------------------------------------------------------------------]
Unpacking 'C:/MyProject/emsdk/zips/portable_jre_8_update_152_64bit.zip' 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/8c9e0a76ebed2c5e88a718d43e8b62452def3771-wasm-binaries.zip from https://storage.googleapis.com/webassembly/emscripten-releases-builds/win/8c9e0a76ebed2c5e88a718d43e8b62452def3771/wasm-binaries.zip, 414615769 Bytes
[----------------------------------------------------------------------------]
Unpacking 'C:/MyProject/emsdk/zips/8c9e0a76ebed2c5e88a718d43e8b62452def3771-wasm-binaries.zip' 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:
node-14.18.2-64bit
python-3.9.2-nuget-64bit
java-8.152-64bit
releases-upstream-8c9e0a76ebed2c5e88a718d43e8b62452def3771-64bit
Run source ./emsdk_env.sh
:
$ source ./emsdk_env.sh
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 (https://github.com/llvm/llvm-project 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)
cocos2d
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" )
set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${USE_FLAGS}" )
set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${USE_FLAGS}" )
set ( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${USE_FLAGS}" )
set ( CMAKE_EXECUTABLE_SUFFIX .html )
else ()
find_package ( SDL2 REQUIRED )
find_package ( Freetype REQUIRED )
endif ()
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:
SDL2_PATH
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 ./emsdk_env.sh
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.