Today was a fun day. I decided to give [emscripten](
https://en.wikipedia.org/wiki/Emscripten) a go, and got a taste of the particularly potent trio of CMake, SDL, and emscripten. These tools are quite powerful in their own right, but they're also not too fancy to play nice together, and when they do, the results are pretty encouraging. I thought I'd share my experience, and a couple of the (minor) obstacles that popped up. Hoping you find this useful and/or informative

Getting Started
Bootstrapping is pretty simple, following the instructions [here](
https://github.com/kripken/emscripten/wiki/Tutorial). Emscripten isn't the easiest tool ever to get rolling, but it's darn close. It's a bit immature to deploy in a single command...no aptitude, pacman or yum here

Instead you're going to grab the latest from github, along with its main dependencies--python, clang, and nodejs, which SHOULD live in your package manager of choice.
Run `./emcc` once to configure paths. This is mostly automatic, but you may need to intervene if it can't find something.
Run `./emcc` again to verify the paths you set up. I tried to sneak by without installing nodejs and was duly reprimanded at this step. Once this is done, we're ready to go.
Using EmscriptenI built a couple of the test files as advised, and all seemed to work well. `emcc` claims to be a "drop-in replacement" for `gcc`, which is quite a bold statement, but I couldn't prove it wrong, and I have little interest in trying. Building single files really is as simple as `emcc hello.cpp -o hello.js`. You'll get a huge, honkin' output file (understandably so), which you can then run locally with something like `node hello.js`.
After I got past my initial excitement at the ease of use, I felt a chill run down my spine. What happens when we graduate to larger projects with several source files? JS doesn't have any concept of linking, so surely things are about to get ugly.
Fortunately, not the case. In fact, they've got it all figured out -
https://github.com/kripken/emscripten/wiki/Building-ProjectsThere's surprisingly little magic that goes on here. The tools are just python wrappers around shell commands. For example, the following is what powers `emmake`, where args is the command line you pass in:
681 def make(args, stdout=None, stderr=None, env=None):
682 if env is None:
683 env = Building.get_building_env()
684 if not args:
685 print >> sys.stderr, 'Error: Executable to run not specified.'
686 sys.exit(1)
687 #args += ['VERBOSE=1']
688 try:
689 process = Popen(args, stdout=stdout, stderr=stderr, env=env)
690 process.communicate()
691 except Exception, e:
692 print >> sys.stderr, 'Error: Exception thrown when invoking Popen in make with args: "%s"!' % ' '.join(args)
693 raise
694 if process.returncode is not 0:
695 raise subprocess.CalledProcessError(cmd=args, returncode=process.returncode)
Firing Things UpNot only can you use your existing Makefile, you can likely use your existing Makefile *generator*. For me, this is CMake, so my first attempt looked like this
bobvi% emconfigure 'cmake ..'
Error: Exception thrown when invoking Popen in configure with args: "cmake .. -DCMAKE_TOOLCHAIN_FILE=/tmp/tmpsYw1ki.cmaketoolchain.txt"!
Traceback (most recent call last):
File "/home/cheezmeister/stuff/code/python/emscripten/emconfigure", line 24, in <module>
shared.Building.configure(sys.argv[1:])
File "/home/cheezmeister/stuff/code/python/emscripten/tools/shared.py", line 671, in configure
process = Popen(args, stdout=stdout, stderr=stderr, env=env)
File "/usr/lib/python2.7/subprocess.py", line 711, in __init__
errread, errwrite)
File "/usr/lib/python2.7/subprocess.py", line 1308, in _execute_child
raise child_exception_
OSError: [Errno 2] No such file or directory
bobvi%
Oh no! emmake doesn't process the command line you give it, so zsh wound up choking on those single quotes. Let's try again.
bobvi% emconfigure cmake ..
-- The C compiler identification is Clang 3.2.0
-- The CXX compiler identification is Clang 3.2.0
-- Check for working C compiler: /home/cheezmeister/stuff/code/python/emscripten//emcc
-- Check for working C compiler: /home/cheezmeister/stuff/code/python/emscripten//emcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /home/cheezmeister/stuff/code/python/emscripten//em++
-- Check for working CXX compiler: /home/cheezmeister/stuff/code/python/emscripten//em++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Looking for include file pthread.h
-- Looking for include file pthread.h - found
-- Looking for pthread_create
-- Looking for pthread_create - not found
-- Looking for pthread_create in pthreads
-- Looking for pthread_create in pthreads - not found
-- Looking for pthread_create in pthread
-- Looking for pthread_create in pthread - found
-- Found Threads: TRUE
CMake Error at /usr/share/cmake-2.8/Modules/FindPackageHandleStandardArgs.cmake:97 (message):
Could NOT find SDL (missing: SDL_LIBRARY) (found version "1.2.15")
Call Stack (most recent call first):
/usr/share/cmake-2.8/Modules/FindPackageHandleStandardArgs.cmake:291 (_FPHSA_FAILURE_MESSAGE)
/usr/share/cmake-2.8/Modules/FindSDL.cmake:176 (FIND_PACKAGE_HANDLE_STANDARD_ARGS)
CMakeLists.txt:36 (find_package)
-- Configuring incomplete, errors occurred!
Getting there. But what's this about SDL? CMake wants to locate it, but emscripten doesn't provide it. Nuts! This had me stumped for longer than it should have, if I had properly rtfm'd. "Building Projects" states: *Built-in support exists for libc, libc++ and SDL, and for those you do not even need to add -lSDL or such - they will just work. But for other libraries, you need to build and link them.* In other words, it's even easier than you thought it was.
Makes perfect sense. As soon as I zapped the find_package(SDL) and related lines from `CMakeLists.txt`, things were back on track. Of course, I don't want to have to put them back in for regular native builds because I'm lazy, so I just added an option for it:
option(USE_EMSCRIPTEN "Set this true if deploying to JS with emscripten" OFF)
My build command now looks like this
bobvi% emconfigure cmake -DUSE_EMSCRIPTEN=TRUE ..
-- The C compiler identification is Clang 3.2.0
-- The CXX compiler identification is Clang 3.2.0
-- Check for working C compiler: /home/cheezmeister/stuff/code/python/emscripten//emcc
-- Check for working C compiler: /home/cheezmeister/stuff/code/python/emscripten//emcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /home/cheezmeister/stuff/code/python/emscripten//em++
-- Check for working CXX compiler: /home/cheezmeister/stuff/code/python/emscripten//em++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Could NOT find PortAudio (missing: PORTAUDIO_LIBRARIES PORTAUDIO_INCLUDE_DIR)
-- Could NOT find JNI (missing: JAVA_AWT_LIBRARY JAVA_JVM_LIBRARY)
-- Configuring done
-- Generating done
-- Build files have been written to: /home/cheezmeister/stuff/code/c/nextris-js/js
Cool! No dice with portaudio out of the box, but I can deal with that later. Let's try building!
bobvi% emmake make
[ 10%] Building CXX object CMakeFiles/nextris.dir/src/game.cpp.o
emcc: warning: -I or -L of an absolute path encountered. If this is to a local system header/library, it may cause problems (local system files make sense for compiling natively on your system, but not necessarily to JavaScript)
/home/cheezmeister/stuff/code/c/nextris-js/src/game.cpp:75:7: error: use of undeclared identifier 'SDL_GetKeyState'; did you mean 'SDL_GetKeyName'?
ks = SDL_GetKeyState(NULL);
Dagnabbit, I'm still using SDL 1.2. In SDL 2.0, and thus emscripten, that function was renamed to SDL_GetKeyboardState, but otherwise it behaves the same, so it's an easy fix. I suppose it's time for me to migrate anyway. After replacing that call, behold, it builds!
bobvi% emmake make
Scanning dependencies of target nextris
[ 10%] Building CXX object CMakeFiles/nextris.dir/src/main.cpp.o
emcc: warning: -I or -L of an absolute path encountered. If this is to a local system header/library, it may cause problems (local system files make sense for compiling natively on your system, but not necessarily to JavaScript)
...
[100%] Building CXX object CMakeFiles/nextris.dir/src/display.cpp.o
emcc: warning: -I or -L of an absolute path encountered. If this is to a local system header/library, it may cause problems (local system files make sense for compiling natively on your system, but not necessarily to JavaScript)
Linking CXX executable nextris-1.1.0.js
Warning: .ll contains floating-point values with more than 64 bits. Faking values for them. If they are used, this will almost certainly break horribly!
[100%] Built target nextris
bobvi%
Awesome! Notice how I got a js file as my output. That comes from changing the name of the CMake target to have a js extension, which causes the final emcc call to generate JS rather than bitcode.
As a side note, those warnings were benign in my case. Run a `emmake make VERBOSE=1` to check what flags are causing them and whether you can safely ignore them.
So...let's try it, I guess? It came as no big surprise that I couldn't blindly run the game from the terminal:
bobvi% node nextris-1.1.0.js
/home/cheezmeister/stuff/code/c/nextris-js/js/nextris-1.1.0.js:155847
throw e;
^
ReferenceError: document is not defined
at _SDL_Init (/home/cheezmeister/stuff/code/c/nextris-js/js/nextris-1.1.0.js:2438:9)
at __ZN4Game3runEv (/home/cheezmeister/stuff/code/c/nextris-js/js/nextris-1.1.0.js:13663:14)
at __Z11nextris_runv (/home/cheezmeister/stuff/code/c/nextris-js/js/nextris-1.1.0.js:13619:14)
at Object._main (/home/cheezmeister/stuff/code/c/nextris-js/js/nextris-1.1.0.js:4341:10)
at Object.callMain (/home/cheezmeister/stuff/code/c/nextris-js/js/nextris-1.1.0.js:155839:26)
at doRun (/home/cheezmeister/stuff/code/c/nextris-js/js/nextris-1.1.0.js:155878:20)
at run (/home/cheezmeister/stuff/code/c/nextris-js/js/nextris-1.1.0.js:155901:12)
at Object.<anonymous> (/home/cheezmeister/stuff/code/c/nextris-js/js/nextris-1.1.0.js:155917:1)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
Sure, it wants some DOM-age to bond to. I tried sticking it in a web page, with this slightly more primising error :
[20:26:29.175] TypeError: Module.canvas is undefined
OK. How do I define the canvas? I can stick a canvas in my HTML, sure, but how do I let the framework know it exists? On a whim, I tried something silly like this:
9 <canvas>
10 <script type="text/javascript" src="nextris-1.1.0.js"></script>
11 </canvas>
No such luck

Alright, it's back to the manual, there must be something I [missed](
https://github.com/kripken/emscripten/wiki/Tutorial#generating-html)...
Well then. In that case, I'm just one extension change away from letting the framework do everything for me. i.e., spit out a canvas and the requisite boilerplate to tie it into the emscripten module. Let's give it a go. I got some far more satisfying results this time around
Linking CXX executable nextris-1.1.0.html
/usr/bin/cmake -E cmake_link_script CMakeFiles/nextris.dir/link.txt --verbose=1
/home/cheezmeister/stuff/code/python/emscripten/em++ -g CMakeFiles/nextris.dir/src/main.cpp.o CMakeFiles/nextris.dir/src/block.cpp.o CMakeFiles/nextris.dir/src/score.cpp.o CMakeFiles/nextris.dir/src/game.cpp.o CMakeFiles/nextris.dir/src/debug.cpp.o CMakeFiles/nextris.dir/src/field.cpp.o CMakeFiles/nextris.dir/src/utils.cpp.o CMakeFiles/nextris.dir/src/audio.cpp.o CMakeFiles/nextris.dir/src/controls.cpp.o CMakeFiles/nextris.dir/src/display.cpp.o -o nextris-1.1.0.html -rdynamic
Warning: .ll contains floating-point values with more than 64 bits. Faking values for them. If they are used, this will almost certainly break horribly!
make[2]: Leaving directory `/home/cheezmeister/stuff/code/c/nextris-js/js'
/usr/bin/cmake -E cmake_progress_report /home/cheezmeister/stuff/code/c/nextris-js/js/CMakeFiles 1 2 3 4 5 6 7 8 9 10
[100%] Built target nextris
make[1]: Leaving directory `/home/cheezmeister/stuff/code/c/nextris-js/js'
/usr/bin/cmake -E cmake_progress_start /home/cheezmeister/stuff/code/c/nextris-js/js/CMakeFiles 0
Open this in your favorite browser, you'll be good to go. Just don't try to view the source from Firefox, or it'll choke on some 20,000 lines of asm.js and die a horrific death.
But I have one issue left:
[20:53:14.613] uncaught exception: SDL_Delay called! Potential infinite loop, quitting. _SDL_Delay@file:///home/cheezmeister/stuff/code/c/nextris-js/js/nextris-1.1.0.html:2598
__Z6updateP11SDL_Surface@file:///home/cheezmeister/stuff/code/c/nextris-js/js/nextris-1.1.0.html:13895
__ZN4Game3runEv@file:///home/cheezmeister/stuff/code/c/nextris-js/js/nextris-1.1.0.html:13787
__Z11nextris_runv@file:///home/cheezmeister/stuff/code/c/nextris-js/js/nextris-1.1.0.html:13710
_main@file:///home/cheezmeister/stuff/code/c/nextris-js/js/nextris-1.1.0.html:4432
callMain@file:///home/cheezmeister/stuff/code/c/nextris-js/js/nextris-1.1.0.html:155930
doRun@file:///home/cheezmeister/stuff/code/c/nextris-js/js/nextris-1.1.0.html:155969
@file:///home/cheezmeister/stuff/code/c/nextris-js/js/nextris-1.1.0.html:155988
So, SDL_Delay is off-limits, too. As the tail end of [this post](
https://hacks.mozilla.org/2012/04/porting-me-my-shadow-to-the-web-c-to-javascriptcanvas-via-emscripten/) explains, our main loop needs to be async, which makes sense if it's to be running in a browser. Up until now, I was able to get away with a fixed timestep, but now it looks like I'll be taking a break for a wee bit o' refactorin'. But for now, sleep. More on this story as it develops.