Setting up Emscripten with CMake in Git Bash on Windows 10
All notes in this series:
- (1) Setting up Emscripten with CMake in Git Bash on Windows 10
- (2) Setting up Emscripten with CMake on Linux
- (3) Porting a simple SDL2 game to Emscripten
- (4) Setting up CMocka with CMake for Linux and Windows
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.
- Note that use of
-sUSE_SDL=2
to enable#include "SDL2/SDL.h"
for using SDL2. - Note that
SDL2_image
andSDL2_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 thecmake
call when generating the make files. - Do not use
emcmake
to wrap thecmake
call when performing a build.
All notes in this series:
- (1) Setting up Emscripten with CMake in Git Bash on Windows 10
- (2) Setting up Emscripten with CMake on Linux
- (3) Porting a simple SDL2 game to Emscripten
- (4) Setting up CMocka with CMake for Linux and Windows