Project aims to provide solution to
- compile modern Haskell libraries to Javascript files and use them in Ajax applications or
- develop entire Ajax application in Haskell
Previous version of project is located at vir.mskhug.ru.
This version of GHCJS can be built stand alone or integrated into GHC. You can install both (if you know you want both do Integrated first).
Integrated
- A full GHC that also outputs .js files and .jsexe directories when you build.
- A patched Cabal that installs the .js files along with the .hi ones.
- Takes a while to install (mostly just follow the regular GHC build instructions).
Stand Alone
- Uses GHC API to make a ghcjs executable.
- Quicker to install (because you don't have actually build 7.4).
- Good for trying out changes in the code generator itself.
- Still requires some messing with GHC source.
- Google Closure Compiler https://developers.google.com/closure/compiler/
- Google Closure Library https://developers.google.com/closure/library/
- Java (to run the Closure Compiler)
For the best JavaScript performance you should probably use a 32bit version of of ghc. If you use the 64bit build it will use goog.math.Long for Int and Word.
- GHC install capable of building GHC 7.4.1
- 3.5GB RAM (JavaScript linker is memory hungry)
- OS X 10.7 using 32bit GHC 7.4.1 to build 32bit GHC
- Ubuntu 12.04 64bit VM with 3.5GB of system RAM using 64bit GHC 7.4.1 to build 64bit GHC
git clone https://github.com/ghcjs/ghc cd ghc git checkout ghc-7.4 ./sync-all -r https://github.com/ghc get ./sync-all -r https://github.com/ghc get ./sync-all -r https://github.com/ghc get ./sync-all -r https://github.com/ghc get ./sync-all -r https://github.com/ghcjs --ghcjs get ./sync-all checkout ghc-7.4
(I find the gets from github often fail hence the 4 of them)
Build GHC as per the normal instructions (remember you can use this GHC to build binaries too).
Typical install goes something like this.
cp mk/build.mk.sample mk/build.mk perl boot ./configure --prefix=/home/hamish/ghcjs make make install hash -r
That last step takes a long time. To use this compiler add /home/hamish/ghcjs to your path ahead of any other ghc.
export PATH=/home/hamish/ghcjs/bin:$PATH
You should be able to switch back to your main compiler at any point by simply not including this in you path.
The global packages (including JavaScript) will be installed to something like
- ~/ghcjs/lib/ghc-7.4.1.20120501
User cabal packages are installed to something like
- ~/.ghc/i386-darwin-7.4.1.20120501
- ~/.cabal/lib/*/ghc-7.4.1.20120501
You need to make a version of cabal-install that uses the new Cabal package. So that which you run "cabal install" it will copy .js files and .jsexe directories to the install location.
It is a good idea not to put this "cabal" in your default PATH as it may not play nice with other version of GHC (since it will pass --enable-javascript). You can do the following to build it and put it next to the GHCJS enabled version of ghc.
export PATH=/home/hamish/ghcjs/bin:$PATH cd libraries/Cabal/cabal-install cabal install --prefix=/home/hamish/ghcjs hash -r
There is a catch. Because your old cabal install installed the dependencies the .js files for these libraries will not have been installed. So you should unregister then so they will be installed again with the new cabal-install.
You can get a list of all the packages that were installed by running
ghc-pkg list --user
The quickest way to do this delete the directory these are in
rm -rf ~/ghcjs/lib/ghc-7.4.1.20120501
or you can unregister them using ghc-pkg something like this
ghc-pkg unregister HTTP ghc-pkg unregister network ghc-pkg unregister parsec ghc-pkg unregister mtl ghc-pkg unregister transformers ghc-pkg unregister zlib ghc-pkg unregister random ghc-pkg unregister text ghc-pkg unregister time
- GHC 7.4.1 (it may work with earlier versions, but it has not been tested).
- GHC 7.4.1 source configured and used to do a build
If you built the integrated version you will have the source ready to go. If
not, you will need to follow the steps in Building Prelude. The unit tests
will look for the ghc source in ../ghc
and the for the closure compiler
in ~/closure-compiler/compiler.jar
and library in ~/closure-library
Code builds as standard haskell package
$ cabal install --enable-tests
To build the test Java Script run.
$ cabal test
Open test.html (for minified code) or test_raw.html for unminified code. You may need to open these via HTTP for them to work.
To compile Haskell module to Javascript use ghcjs
command.
$ ghcjs Test.hs
This command is merely equivalent to the following
$ ghc --make Test.hs
but it compiles to Javascript instead of native code.
The code is in alpha stage. Feel free to experiment with it as you wish.
Compiler is implemented as GHC backend using GHC API. And been tested with 32bit GHC 7.4.1.
To play with any Haskell code you'll need Haskell standard library. GHC implements standard library as a "base" package. You'll need to compile modules from base package.
-
Download ghc source distribution for the same version of ghc that you use to build ghcjs.
-
Customize build
$ cd ghc-x.xx.x $ cd mk $ cp build.mk.sample build.mk
Edit mk/build.mk
Set build flower to the quikest: uncomment the line:
BuildFlavour = quickest
-
Build ghc
$ ./configure $ make
-
You should try to use BuildPackages.hs script. You can build ghc-prim, integer-simple and base packages in examples directory with the following command
$ cd examples $ ./BuildPackages.hs <path-to-ghc-directory>
You can build packages manually using instructions below.
=== Building ghc-prim
$ cd ghc-x.xx.x
$ cd libraries/ghc-prim
$ ghcjs -odir <javascript files folder>/ghc-prim -hidir <javascript files folder>/ghc-prim -cpp -fglasgow-exts -package-name ghc-prim GHC/Types.hs
$ ghcjs -odir <javascript files folder>/ghc-prim -hidir <javascript files folder>/ghc-prim -cpp -fglasgow-exts -package-name ghc-prim GHC/*
=== Building base
$ cd ghc-x.xx.x
$ cd libraries/base
$ ghcjs -odir <javascript files folder>/base -hidir <javascript files folder>/base -hide-package base -package-name base -I./include -i./dist-install/build -XMagicHash -XExistentialQuantification -XRank2Types -XScopedTypeVariables -XUnboxedTuples -XForeignFunctionInterface -XUnliftedFFITypes -XDeriveDataTypeable -XGeneralizedNewtypeDeriving -XFlexibleInstances -XStandaloneDeriving -XPatternGuards -XEmptyDataDecls -XNoImplicitPrelude -XCPP Prelude.hs
This last magic command line was guessed using
cabal build -v
to see what options are passed to GHC.
We should really use
-odir /tmp
option to get read of useless object files, but it seems to be a bug in ghc that cause GHC to rely on odir to be the same as hidir wich is mostly the case in "normal" GHC usage.
vir.mskhug.ru contains the first implementation of the idea. This version is a rewrite that inherit little code from previous version. The differences are the following.
-
Threading
-
MVar
-
integer-gmp (using goog.math.Integer)
-
Weak pointers
-
Finalizers
-
Function level linker
-
File Input (uses HTTP and is text only)
-
ByteArrays (using JavaScript ArrayBuffers)
-
More closure compiler friendly output
-
Foreign function interface is not supported. Only minimum of primitive operations is supported.
-
Tail recursion optimization is on by default.
If you don't want it use
$ ghcjs --calling-convention=plain
It would be nice to use a source map to avoid reading the generated javascript at all. But until that is done here are some use things to know.
Many of the names used have been shortened to a single character to reduce the size of the java script. Here is a key so you can work out what they do.
Some functions include "info" strings that to try to make debugging easier (controlled with the HS_DEBUG flag). Also some of the functions have a list of things to keep alive with the thunk. This is also striped out if you disable support for finalizers and weak pointers (controlled with the HS_WEAKS flag). It is a nice feature of the Closure Compiler that if you pass more parameters than the function needs it will remove them from the call site.
Name | Description |
---|---|
$f | function (first param is the arity) |
$t | thunk |
$R and $r | are short for return |
$M | evaluates its first param (if not already evaluated) and and passes the result to the function provided |
$A | evaluates if not already evaluated and returns the result |
.C | calls the function with the list of arguments and passes the result to the function provided |
.J | jump t a function |