Emscripten

From Uzebox Wiki
Revision as of 03:07, 15 September 2016 by Artcfox (talk | contribs) (Removed Debian-specific instructions for node.js, since the Emscripten build includes node and automatically configures its path correctly.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Emscripten is an LLVM-to-JavaScript compiler. It takes LLVM bitcode - which can be generated from C/C++, using llvm-gcc (DragonEgg) or clang, or any other language that can be converted into LLVM - and compiles that into JavaScript, which can be run on the web (or anywhere else JavaScript can run).

This is my attempt at documenting the entire process of installing and configuring Emscripten under Debian GNU/Linux 8.5 "Jessie" so anyone can use it to build a version of Uzem for the web.

Installing prerequisites

Open a terminal, and type the following to ensure you have the prerequisites necessary for building Emscripten and Uzem:

 sudo apt-get update
 sudo apt-get install build-essential cmake nodejs python2.7 default-jre git libsdl2-dev

Keep that terminal window open, because you will need to use it again soon!

Building Emscripten

Download the Portable Emscripten SDK for Linux and OS X (emsdk-portable.tar.gz) from the official Emscripten homepage.

(You can extract emsdk-portable.tar.gz and build it anywhere, but this document assumes that you will extract it inside your Downloads folder.)

Switch back to your terminal window and type the following to switch to your ~/Downloads directory, extract the Portable Emscripten SDK, and build it:

 cd ~/Downloads
 tar -xvf emsdk-portable.tar.gz
 cd emsdk_portable
 ./emsdk update
 ./emsdk install latest

The install command will take quite a bit of time, so go play some Uzebox games while you wait.

When that is complete, type the following to activate it, and to setup the correct environment variables for that terminal window:

 ./emsdk activate latest
 source ./emsdk_env.sh

Note that whenever you open a new terminal window, you will have to re-source the environment variables before you can use the Emscripten compiler from that terminal:

 source ./emsdk_env.sh

Configuring Uzem to be built with Emscripten

Currently only the uzem140 branch of Uzem has been modified to build with Emscripten, so you will need to first clone it using Git, and switch to that branch before building.

Type the following commands to create a new clone inside your emsdk_portable directory, and switch to the uzebox/tools/uzem/ directory of the uzem140 branch:

 cd ~/Downloads/emsdk_portable
 git clone https://github.com/Uzebox/uzebox.git
 cd uzebox/tools/uzem
 git checkout uzem140

At this point, Uzem can technically be built with Emscripten, but it will be missing the gamefile.uze and eeprom.bin files, so the only thing it will do is display its command line help message in the console, which isn't very useful.

Preloading the virtual filesystem with the necessary game files

Emscripten allows you to preload files into a virtual filesystem, which can then be accessed by the compiled application when it runs inside a web page. For additional information about this you may refer to the official documentation, but the instructions below should be enough for running Uzem.

The way Uzem's Makefile is currently configured, the Emscripten build expects the files: "gamefile.uze" and "eeprom.bin" to exist in the same directory as the Makefile when performing the build. For games which do not use the SD card, it is enough to simply copy the .uze file for the game and its associated eeprom.bin file into the ~/Downloads/emsdk_portable/uzebox/tools/uzem/ directory, and rename the .uze file to gamefile.uze, but if the game you are packaging uses the SD card and requires additional files to run, you will need to edit the line in the Makefile that defines EMSCRIPTEN_TARGET_EXTRAS.

For example if a game is expecting a datafile on the SD card called aedat.bin, you would change the line:

 EMSCRIPTEN_TARGET_EXTRAS=.html --preload-file gamefile.uze --preload-file eeprom.bin

to:

 EMSCRIPTEN_TARGET_EXTRAS=.html --preload-file gamefile.uze --preload-file eeprom.bin --preload-file aedat.bin

Once the Makefile has been configured (if any additional files needed to be preloaded), and all files to be preloaded exist in the uzem/ directory, type the following command to build a copy of Uzem that contains a virtual filesystem with those files preloaded:

 EMSCRIPTEN_BUILD=1 make release

This will generate the following files in your current directory:

 uzem.data
 uzem.html.mem
 uzem.js
 uzem.html

You may see warnings about unresolved posix_spawn* symbols, but those are harmless and can be ignored.

Configuring the command line arguments inside the generated uzem.html file

If you attempt to load uzem.html in your web browser now, you still wouldn't see anything except the command line help message printed in the JavaScript console (press F12 to access) because we have not yet configured the command line arguments inside the generated uzem.html file yet. Unfortunately, each time you run `make release` it will overwrite the uzem.html file, and you'll have to modify it again. If this gets annoying, you may wish to come up with an automated way to add these parameters to the generated uzem.html file.

Open the uzem.html file in your favorite editor and search for "var Module" and you should see this:

     var Module = {
       preRun: [],

Change it so it looks like this:

     var Module = {
       arguments: ["gamefile.uze"],
       preRun: [],

If you wish to pass additional arguments, put each of them in quotes, separating them with commas, and ensure "gamefile.uze" appears last.

For example this would run gamefile.uze with sounds disabled:

     var Module = {
       arguments: ["-n", "gamefile.uze"],
       preRun: [],

Testing the web version of Uzem

Now you are finally at a point where you can test out the game inside a web browser!

Type the following command to use Python to serve the resources over HTTP:

 python -m SimpleHTTPServer 8080

This python command is necessary for testing, because some web browsers will not load and execute all the resources if you just double-click on the uzem.html file.

Press Ctrl-C in the terminal to kill the Python server when you are done testing.

Publishing the web version of Uzem online

If you want to serve this up remotely, just upload the following files to your web host:

 uzem.data
 uzem.html.mem
 uzem.js
 uzem.html

Errors you may encounter

If the game does not load as expected in your web browser, press F12 to open the JavaScript console and you should see an error message, or the error message may be printed in the virtual console embedded directly in the web page.

Error: Cannot enlarge memory arrays

If you see the following error message:

 Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X 
 with X higher than the current value 16777216, (2) compile with
 ALLOW_MEMORY_GROWTH which adjusts the size at runtime but prevents some
 optimizations, or (3) set Module.TOTAL_MEMORY before the program runs.

You can solve it by editing the uzem.html file in a way similar to adding command line arguments.

For example, to increase TOTAL_MEMORY to 512 MB, open the uzem.html file in your favorite editor and search for "var Module" and add the line beginning with TOTAL_MEMORY like so:

     var Module = {
       arguments: ["gamefile.uze"],
       TOTAL_MEMORY: [537395200],
       preRun: [],

To the best of my knowledge, there is no way to know what TOTAL_MEMORY needs to be set to for a particular game, so just keep increasing it until it works. (I have no idea why I had to increase it to 512 MB for Alter Ego to run, but setting it to 256 MB didn't work.)

Do not try "(2) compile with ALLOW_MEMORY_GROWTH which adjusts the size at runtime but prevents some optimizations" because the resulting build will run slower than molasses.

The weird thing is that my web browser did not give me the "Cannot enlarge memory arrays" error until I uploaded all the files to a remote host, so be sure you test everything both locally and remotely.