[Nix-dev] Announcing support for GHCJS

Bas van Dijk v.dijk.bas at gmail.com
Thu Jan 8 10:52:32 CET 2015


Hi Oliver,

At work (I'm now at Lumi Guide) I currently use the following setup. I
use a Makefile which defines some convenient targets for building the
client and server and for entering shells for development:

Makefile:
# Builds the server.
.PHONY: server
server:
    nix-build -I . packages.nix -A server

# Builds the Haskell (GHCJS) client (*.html / *.js):
.PHONY: client
client:
    nix-build -I . packages.nix -A client

# Enters a shell which has access to all server dependencies
# (like python and the client):
.PHONY: server.dev
server.dev:
    nix-shell -I . packages.nix -A server

# Enters a shell which has access to all client dependencies
# (like GHCJS, cabal-js, etc.):
.PHONY: client.dev
client.dev:
    nix-shell -I . packages.nix -A client

Note that I have a clone of nixpkgs checked out as a git submodule in
the "nixpkgs" directory. I update nixpkgs every two weeks or so.

The top-level "packages.nix" file defines the server and client projects:

packages.nix:
{ originalPkgs ? import <nixpkgs> {} }:

let pkgs = import ./nix { pkgs = originalPkgs; }; in

rec {
  client = pkgs.haskellPackages_ghcjs.callPackage ./client {
    cabalInstall_HEAD =
pkgs.haskellPackages_ghcjs.ghc.ghc.parent.cabalInstall_HEAD;
    ...

  server = pkgs.callPackage ./server { inherit client }
  };
...

The client uses a regular cabal.mkDerivation but since it's called
with pkgs.haskellPackages_ghcjs.callPackage it uses GHCJS.

client/default.nix:
{
# Tools
  cabal

, cabalInstall_HEAD

# Library dependencies
, aeson, attoparsec, blazeHtml, blazeReact
, either, ghcjsFfiqq, ghcjsJquery, httpTypes, languagePython, lens
, lumiFacilityCommon, networkUri, process, safe, scientific
, semigroups, systemFilepath, text, transformers
, unorderedContainers, vector, zenc, void
}:

cabal.mkDerivation (self: {
  pname = "lumi-facility-client";
  version = "0.0.1";
  src = ./.;
  isLibrary = false;
  isExecutable = true;
  buildDepends = [
    aeson attoparsec blazeHtml blazeReact either ghcjsFfiqq ghcjsJquery
    httpTypes languagePython lens networkUri process safe scientific
    semigroups systemFilepath text transformers unorderedContainers vector
    zenc void
];

  buildTools = [
    # The following is only needed when running in nix-shell to give
    # access to cabal with GHCJS support.
    #
    # TODO (BvD): See if we can use a shell.nix file which uses
    #   haskellPackages_ghcjs.ghcWithPackages
    # so that we don't need to use this hack...
    cabalInstall_HEAD
  ];

  preConfigure = ''
    rm -r dist

    # Remove symoblic link to locally build client. This prevents the
    # link from being deployed.
    rm ./static/hs
  '';

  postInstall = ''
    mkdir $out/static

    cp -r ./generated $out
    cp ./static/{index.html,default.css,favicon.ico} $out/static
  '';

  meta = {
    license = self.stdenv.lib.licenses.unfree;
    platforms = self.ghc.meta.platforms;
  };
})

"nix" is the directory below which all extensions to nixpkgs are defined:

nix/default.nix:
{ pkgs ? import <nixpkgs> {} }:

# This returns the set of packages from nixpkgs
# extended with packages defined by us.
let self = pkgs // {
      callPackage = self.lib.callPackageWith self;

      haskellPackages       = import ./haskell pkgs.haskellPackages;
      haskellPackages_ghcjs = import ./haskell pkgs.haskellPackages_ghcjs;

      ...
    };
in self

"nix/haskell" is the directory below which all extensions to
haskellPackages are defined:

nix/haskell/default.nix:
# This function takes a haskellPackages collection like
# pkgs.haskellPackages or pkgs.haskellPackages_ghcjs and returns the
# same collection extended with our custom Haskell packages.
hsPkgs : hsPkgs.override {
  extension = self: super: {
    languagePython  = self.callPackage ./language-python {};
    zenc            = self.callPackage ./zenc            {};
    ghcjsJquery     = self.callPackage ./ghcjs-jquery    {};
    GenericPretty   = self.callPackage ./GenericPretty   {};
    ghcjsFfiqq      = self.callPackage ./ghcjs-ffiqq     {};
    blazeReact      = self.callPackage ./blaze-react     {};
  };
}

Note that "nix/haskell/default.nix" can be used to extend both
pkgs.haskellPackages as well as pkgs.haskellPackages_ghcjs.

Note that I'm using Simon Meier's and Alex Sayer's awesome blaze-react
library. See [1] for a video tutorial on blaze-react. Here's the
derivation I use to build it:

nix/haskell/blaze-react/default.nix:
{ cabal, either, fetchgit, ghcjsFfiqq, lens
, mtl, prettyShow, text, time, transformers
, stdenv, nodePackages, writeTextFile
}:

let src = ./blaze-react;
    # TODO (BvD): Once blaze-react stabilizes we should move to get
    # the sources from git instead of using a git submodule:
    #
    # fetchgit {
    #   url = "https://github.com/LumiGuide/blaze-react.git";
    #   sha256 =
"a918fe77fb8b3fdd63a5ba5f20ad925a83f547492114b56170d35ba101ea52ba";
    #   rev = "1624db561f4153eaa8133b9b5f3047c7fea9c8be";
    # };

    version = "0.1.0.0";
in

cabal.mkDerivation (self: rec {
  inherit src;
  inherit version;
  pname = "blaze-react";
  isLibrary = true;
  isExecutable = false;
  buildDepends = [
    either ghcjsFfiqq lens mtl prettyShow text time transformers
  ];

  reactjsBindings = stdenv.mkDerivation {
    name = "reactjs-bindings-${version}";
    inherit src;
    buildInputs = [ nodePackages.browserify nodePackages.react ];
    builder = writeTextFile {
      name = "reactjs-bindings-builder.sh";
      text = ''
        source $stdenv/setup
        cd $src/reactjs-bindings
        mkdir $out
        browserify lib.require.js -o $out/lib.js
      '';
    };
  };

  preConfigure = ''
    ln -s $reactjsBindings/lib.js reactjs-bindings/lib.js
  '';

  meta = {
    description = "Experimental ReactJS bindings for GHCJS";
    license = self.stdenv.lib.licenses.mit;
    platforms = self.ghc.meta.platforms;
  };
})

BTW doing client development with GHCJS and blaze-react is awesome!. I
used to be Haskell back-end developer at Better not just because I
really liked working on servers but also a bit because of fear of
loosing my sanity with JavaScript. Now that I can use Haskell and all
the goodies of GHC like threads, stm and a lot of excellent libraries
from hackage I'm quite enjoying being a front-end developer also :-)

Cheers,

Bas

[1] https://www.youtube.com/watch?v=4nTnC0t7pzY

On 6 January 2015 at 12:06, Oliver Charles <ollie at ocharles.org.uk> wrote:
> Ertugrul Söylemez <ertesx at gmx.de> writes:
>
>> Hi everybody,
>>
>>> I was going to try it out and found the compiler itself, but couldn't
>>> figure out how to make a derivation for a JS package.  Is there any
>>> documentation on that?  If not, in which files should I look?  Something
>>> like ghcjsWithPackages and cabal.mkJsDerivation would be great.
>>
>> Nevermind, I think I got it.
>
> Could you update this thread with your solution, for posterity?
>
> -- ocharles
>
> _______________________________________________
> nix-dev mailing list
> nix-dev at lists.science.uu.nl
> http://lists.science.uu.nl/mailman/listinfo/nix-dev
>


More information about the nix-dev mailing list