-
Notifications
You must be signed in to change notification settings - Fork 80
Building HaLVMs with Nix
An optional approach to building HaLVM's is with the Nix
package manager.
When building complex projects (i.e. with multiple GHCs) it is often desirable to have a build tool that can abstract over different compilers / language ecosystems. Nix is one such tool.
Nix
is both a language and a package manager that was designed with the intent of building packages in a reproducible way.
Nix
is a pure, dynamically-typed functional programming language that consists of expression evaluation and derivation
building.
A derivation
is an attribute set (set of key-value pairs) that when evaluated, produce build artifacts. All derivations are evaluated through the following script.
For more information on what a derivation
is, please consult the following link.
For more information on Nix, the package manager itself, please consult the following link.
The HaLVM
has been given its own derivation on the official nixpkgs
repository. This derivation can be used to build unikernels that run on the Xen
hypervisor.
Nixpkgs
is a collection of thousands of ready-made derivations that can be evaluated to build packages for a large number of language ecosystems (including Haskell packages).
Most packages on nixpkgs
are available from a binary cache, and will be fetched as opposed to built, HaLVM-2.4.0
included.
For more information about and/or contributing to nixpkgs
please consult the following link.
HaLVM 2.4.0
is available for download via the Nix package manager in two flavors.
haskell.compiler.integer-simple.ghcHaLVM240
-
haskell.compiler.ghcHaLVM240
(The defaultinteger-gmp
build)
Nix does not use stack
nor cabal-install
to build Haskell projects. It builds, configures and installs Haskell packages into the /nix/store
using GHC
, a Setup.hs
script, the Cabal
library and a carefully populated GHC package database.
For more information on this process, please consult the following link.
$ curl https://nixos.org/nix/install | sh
The contents of this script can be found here. It is recommended to read this before installing.
The nix-shell
command allows one to enter into an isolated shell environment with a package from the /nix/store
available on the user's PATH
, and other environment variables properly set.
nix-shell -p haskell.compiler.integer-simple.ghcHaLVM240
The above command will retrieve a pre-built binary of HaLVM-2.4.0
into /nix/store
, bypassing building. It will then place the user into a shell with the HaLVM
bin directory on the user's PATH
.
For more information about nix-shell
, please consult the following link.
Inside of the nix-shell
we can now construct a simple unikernel that prints to the Xen
console.
λ nixos ~ → cat > Main.hs <<EOF
heredoc> module Main where
heredoc>
heredoc> import Hypervisor.Console
heredoc> import Control.Concurrent
heredoc> import Control.Monad
heredoc>
heredoc> main :: IO ()
heredoc> main = do
heredoc> () <$ initXenConsole
heredoc> forever $ do
heredoc> threadDelay (1000 * 1000)
heredoc> putStrLn "Hello world!"
heredoc> EOF
We can now compile the above unikernel in our shell enviroment.
$ halvm-ghc Main.hs -o main
Exiting the shell will leave our unikernel in the same directory.
Nix derivations can be generated from cabal
files. This is useful for building Haskell projects with Nix
.
cabal2Nix
is a simple command line utility for generating Nix
files from a cabal
file.
Given our simple Hello World
application above, we can provide a HelloWorld.cabal
that looks like the following:
name: HelloWorld
version: 0.1.0.0
author: User
maintainer: [email protected]
build-type: Simple
extra-source-files: ChangeLog.md
cabal-version: >=1.10
executable HelloWorld
main-is: Main.hs
build-depends: base >=4.9 && <5
default-language: Haskell2010
We can construct the HelloWorld.nix
file with the following command:
cabal2nix . > HelloWorld.nix
Result:
{ mkDerivation, base, stdenv }:
mkDerivation {
pname = "HelloWorld";
version = "0.1.0.0";
src = ./.;
isLibrary = false;
isExecutable = true;
executableHaskellDepends = [ base ];
license = stdenv.lib.licenses.bsd3;
}
In order to evaluate the above derivation
we must pass it in as an argument to callPackage
, and then call nix-build
.
$ cat > default.nix
{ pkgs ? import <nixpkgs> {} }:
pkgs.haskell.packages.integer-simple.ghcHaLVM240.callPackage ./HelloWorld.nix {}
Calling nix-build
will build our Haskell project, store the resulting unikernel in the /nix/store
and place a symlink named result
in the current working directory.
$ nix-build
...........
Building HelloWorld-0.1.0.0...
Preprocessing executable 'HelloWorld' for HelloWorld-0.1.0.0...
[1 of 1] Compiling Main ( Main.hs, dist/build/HelloWorld/HelloWorld-tmp/Main.o )
Linking dist/build/HelloWorld/HelloWorld ...
haddockPhase
installing
Installing executable(s) in
/nix/store/7n9ljx8ynrqa6hmximlrgrvsyyyf5hns-HelloWorld-0.1.0.0/bin
Warning: The directory
/nix/store/7n9ljx8ynrqa6hmximlrgrvsyyyf5hns-HelloWorld-0.1.0.0/bin is not in
the system search path.
post-installation fixup
shrinking RPATHs of ELF executables and libraries in /nix/store/7n9ljx8ynrqa6hmximlrgrvsyyyf5hns-HelloWorld-0.1.0.0
shrinking /nix/store/7n9ljx8ynrqa6hmximlrgrvsyyyf5hns-HelloWorld-0.1.0.0/bin/HelloWorld
cannot find section .dynamic
stripping (with flags -S) in /nix/store/7n9ljx8ynrqa6hmximlrgrvsyyyf5hns-HelloWorld-0.1.0.0/bin
patching script interpreter paths in /nix/store/7n9ljx8ynrqa6hmximlrgrvsyyyf5hns-HelloWorld-0.1.0.0
/nix/store/7n9ljx8ynrqa6hmximlrgrvsyyyf5hns-HelloWorld-0.1.0.0
λ nixos HelloWorld → tree result
result
|-- bin
| `-- HelloWorld
`-- share
`-- doc
`-- x86_64-halvm-ghc-8.0.2
`-- HelloWorld-0.1.0.0
`-- LICENSE
5 directories, 2 files
For more information on nix-build
, please consult the following link.