Bringing back the eighties with Emscripten - setting things up

Posted on Tuesday, 21st January 2014

Introduction

Since a lot of work done with Emscripten and LLVM requires usage of command line and I loathe the Windows command line, the steps in this post will describe the process of setting up LLVM on Linux-based system. Specifically, I had some old virtual machine with Crunchbang Waldorf already installed, soI used this one. Since this distribution is based on Debian Wheezy, steps to set things up on Debian should be fairly simillar.

The main intention of this post is to show some LLVM and Enscripten problems I stumbled on before I actually was able to compile anything. Probably you can stumble on them on any environment. I followed the instructions from this Emscripten Wiki section so in case of any problems, you should take a look there in the first place.

If you already have all the things set up, you can skip this post and jump directly to the next one.

Prerequisites

The necessary prerequisites are:

  • Python
  • NodeJS
  • LLVM
  • clang
  • git

On Debian, all of them except NodeJS seemed to be already installed or avaliable in package manager, so you can apt-get install them easily.

To obtain NodeJS through apt, you need to add wheezy-backports repository to the package manager. In order to do that, edit /etc/apt/sources.list and add the following line there:

deb http://ftp.pl.debian.org/debian wheezy-backports main contrib non-free

Warning: Keep in mind that wheezy-backports repository contains the packages that are less stable than default ones avaliable. Probably you don`t want to do this on improtant, production machines.

Once you save the file, just run:

sudo apt-get update
sudo apt-get install llvm clang git nodejs

This should get you all the prerequisites.

Obtaining Emscripten

Once the prerequisites are done installing, you can obtain the Emscripten toolchain by cloning the git repository somewhere in your system. To do that, run the following commands:

git clone https://github.com/kripken/emscripten.git
cd emscripten
git checkout master

Emscripten is by default cloned with incoming branch which contains the developmen version of the project. You need to manually check-out the master to get the more stable branch.

Once that is done, Emscripten is ready to be used on your machine.

It doesn`t work!

Except, it did not work for me. I got the following error when compiled test file with emscripten C compiler:

cfiet@crunchbang:~/projects/emscripten$ ./em++ tests/hello_world.cpp
WARNING  root: (Emscripten: system change: 1.11.0|le32-unknown-nacl|/usr/bin|version vs 1.11.0|le32-unknown-nacl|/usr/lib/llvm-3.5/bin/|version, clearing cache)
WARNING  root: LLVM version appears incorrect (seeing "version", expected "3.2")
INFO     root: (Emscripten: Running sanity checks)
clang: error: no such file or directory: 'le32-unknown-nacl'
ERROR    root: compiler frontend failed to generate LLVM bitcode, halting

A quick check reveals the reason for this error

cfiet@crunchbang:~/projects/emscripten$ clang --version
Debian clang version 3.0-6.2 (tags/RELEASE_30/final) (based on LLVM 3.0)
Target: x86_64-pc-linux-gnu
Thread model: posix

So of course I have to install newer clang version - this is mentioned in documentation but I didn`t bother to check what was the default version obtained from repository. After some googling, I found this LLVM nightly packages repository for Debian. To add it to your system, just create a file /etc/apt/sources.list.d/llvm.list with the following content:

deb http://llvm.org/apt/wheezy/ llvm-toolchain-wheezy main

Warning: This installs an unstable version of LLVM on your machine. Use with caution.

Then run the following commands:

sudo apt-get remove clang
sudo apt-get install clang-3.5

After installation has finished, I tried to compile the test file again:

cfiet@crunchbang:~/projects/emscripten$ ./em++ tests/hello_world.cpp
/usr/bin/llvm-nm: /tmp/tmpwBqfBx/hello_world_0.o: Unknown bitstream version!
/usr/bin/opt: /tmp/tmpwBqfBx/a.out.bc: Unknown bitstream version!
Traceback (most recent call last):
    File "/home/cfiet/projects/emscripten/emcc", line 1479, in <module>
        shared.Building.llvm_opt(final, link_opts, final + '.link.ll')
    File "/home/cfiet/projects/emscripten/tools/shared.py", line 1173, in llvm_opt
        assert os.path.exists(target), 'Failed to run llvm optimizations: ' + output
        AssertionError: Failed to run llvm optimizations:

Bummer, still not working. Since the llvm-nm command seems to fail, I checked the version of the tool:

cfiet@crunchbang:~/projects/emscripten$ llvm-nm --version
Low Level Virtual Machine (http://llvm.org/):
 llvm version 3.0
  (Debian 3.0-10)Optimized build.
 Built Jul 13 2012 (11:36:32).
 Host: x86_64-pc-linux-gnu
 Host CPU: corei7-avx

Ok, so I still have an old version of LLVM installed togather as default, even though during clang-3.5 installation, llvm-3.5 was installed as well. So I tried to remove the old version with

sudo apt-get remove llvm

After that, attempting to compile the test file, failed with the following error:

cfiet@crunchbang:~/projects/emscripten$ ./em++ tests/hello_world.cpp
Traceback (most recent call last):
  File "/home/cfiet/projects/emscripten/emcc", line 1424, in <module>
    extra_files_to_link = system_libs.calculate(temp_files, in_temp, stdout, stderr)
  File "/home/cfiet/projects/emscripten/tools/system_libs.py", line 342, in calculate
    symbolses = map(lambda temp_file: shared.Building.llvm_nm(temp_file), temp_files)
  File "/home/cfiet/projects/emscripten/tools/system_libs.py", line 342, in <lambda>
    symbolses = map(lambda temp_file: shared.Building.llvm_nm(temp_file), temp_files)
  File "/home/cfiet/projects/emscripten/tools/shared.py", line 1217, in llvm_nm
    output = Popen([LLVM_NM, filename], stdout=stdout, stderr=stderr).communicate()[0]
  File "/usr/lib/python2.7/subprocess.py", line 679, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1259, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory

Seems that now, Emscripten cannot find llvm-nm at all, because the tool in PATH is called llvm-nm-3.5 that is a symbolic link to /usr/lib/llvm-3.5/bin/llvm-nm. However, Emscripten generates a file ~/.emscripten where you can customize paths to the tools it uses. So I just edited the file and modified the line that sets up the LLVM_ROOT variable:

#LLVM_ROOT = os.path.expanduser(os.getenv('LLVM') or '/usr/bin') # original version of the line
LLVM_ROOT = os.path.expanduser(os.getenv('LLVM') or '/usr/lib/llvm-3.5/bin')

Now, after trying to compile the test script I got the following:

cfiet@crunchbang:~/projects/emscripten$ ./em++ tests/hello_world.cpp
WARNING  root: (Emscripten: settings file has changed, clearing cache)
WARNING  root: LLVM version appears incorrect (seeing "version", expected "3.2")
INFO     root: (Emscripten: Running sanity checks)
cfiet@crunchbang:~/projects/emscripten$ node ./a.out.js
hello, world!

It`s alive!

Now, that Emscripten seem to work, and I`m all set to bring the Robbo back.