/wasm-cross

Nix expressions for cross compiling to WebAssembly

Primary LanguageNix

wasm-cross

wasm-cross is a toolchain for cross compiling C and Haskell to WebAssembly, using the WebGHC and LLVM

The generated WebAssembly binary can be run on a browser using the webabi library.

It currently provides a minimal ABI, sufficient to run the Haskell code using the jsaddle library with jsaddle-wasm

Quick start

Setup nix cache

  • If you're using linux, a nix binary cache is available at https://nixcache.webghc.org with a public key of:

    hydra.webghc.org-1:knW30Yb8EXYxmUZKEl0Vc6t2BDjAUQ5kfC1BKJ9qEG8=

  • When using reflex-platform you may want to add its cache as well: https://nixcache.reflex-frp.org

    ryantrinkle.com-1:JJiAKaRv9mWgpVAz8dwewnZe0AzzEAzPkagE9SP5NWI=

Build an example app

git clone git@github.com:WebGHC/wasm-cross.git
cd wasm-cross;

Reflex TodoMVC

nix-build release.nix -A examples.wasm.reflex-todomvc

Miso 2048

nix-build release.nix -A examples.wasm._2048

Nix based build with TemplateHaskell support

Template Haskell

For Template Haskell support the save/load-splices infrastructure is used. This was initially created to support the arm cross-compilation, but it works equally well for WebGHC.

This works by first compiling the code for x86/64 arch, and then using the splices generated by it in the wasm compilation. It needs the reflex-platform's nix wizardry to achieve this. It works well for the straightforward use cases of Template Haskell, but may break for more complicated uses.

Note: use of reflex is not required to use reflex-platform, it should work well for Miso too.

In order to support TemplateHaskell the reflex-platform and nix based build is necessary.

See the example JSaddle app and the example Reflex app

Cabal based build without TemplateHaskell support

If you dont need TemplateHaskell support, then you can use only cabal to do the builds, which works much faster as it can do builds incrementally.

The reflex-platform is used here as it provides an easier to use interface for creating a nix-shell with all the dependencies necessary for a cabal project.

Get the latest reflex-platform from https://github.com/reflex-frp/reflex-platform/

  • For a JSaddle app, add the jsaddle-wasm in your executable's cabal file, and put the -threaded option in if !arch(wasm32) block.

Note: reflex-dom library from v0.7 onwards already includes jsaddle-wasm dependency, so it is not necessary to modify anything.

TODO: add instructions for miso and reflex-dom < 0.7

  • Use the work-on script from reflex-platform to obtain a shell with WebGHC
<reflex-platform>/scripts/work-on wasm ./.

Then do the cabal configure with following options, followed by cabal build

cabal configure --configure-option=--host=wasm32-unknown-unknown-wasm --with-ghc=wasm32-unknown-unknown-wasm-ghc --with-ghc-pkg=wasm32-unknown-unknown-wasm-ghc-pkg  --with-gcc=wasm32-unknown-unknown-wasm-cc --with-ld=wasm32-unknown-unknown-wasm-ld --with-ar=wasm32-unknown-unknown-wasm-ar --with-hsc2hs=wasm32-unknown-unknown-wasm-hsc2hs
cabal build

Limitations / Known issues

  • The -threaded option is not supported with WebGHC. You will have to put to modify the cabal file to exclude this option like this:
  if !arch(wasm32)
    ghc-options: -threaded
  • The -Werror option is known to cause compilation failure as it converts the clang warnings to errors. This is a temporary issue, as it should be possible to get rid of clang warnings altogether.

Other details

The C toolchain is made up of Clang / LLVM, LLD, and a fork of musl.

The Haskell compilation is done using WebGHC, which is a fork of GHC

GHC cross compilers are built from a fork using the Nix infrastructure largely developed by John Ericson (@Ericson2314), producing a working haskellPackages.

webabi is the "kernel" to support musl's syscalls.

Notes

  • We have forked nixpkgs just to support the wasm32-unknown-unknown-wasm compilation target, as this support cannot be provided by overlays. All other changes needed to nixpkgs, which cannot be done by overlays, have already been upstreamed.

    The choice of target triple is still under debate, with wasm32-unknown-unknown or wasm32-unknown-unknown-webghc being other candidates.

  • The wasm-cross contains some code for nodejs support, but unfortunately it has not been maintained. If there is interest in running the executables outside the browser environment, then this could be resurrected with a little effort.