-
Notifications
You must be signed in to change notification settings - Fork 81
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-gmpbuild)
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 | shThe 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.ghcHaLVM240The 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> EOFWe 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.nixResult:
{ 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 filesFor more information on nix-build, please consult the following link.