Skip to content

Latest commit

 

History

History
173 lines (140 loc) · 4.97 KB

README.md

File metadata and controls

173 lines (140 loc) · 4.97 KB

Native dependencies

Nix can also provision non-Haskell dependencies for your project. This project provides an example of a small program that invokes libtar to open and close a TAR file.

The first difference is that we have to extend the cabal file to add an extra-libraries clause:

name: project2
version: 1.0.0
license: BSD3
license-file: LICENSE
cabal-version: >= 1.18
build-type: Simple
extra-source-files: cbits

executable project2
    build-depends: base < 5
    default-language: Haskell2010
    c-sources: cbits/check.c
    extra-libraries: tar
    main-is: Main.hs

We also use the c-sources line so that Cabal can build and link in a small test C program located at cbits/check.c for demonstration purposes. This program uses libtar to open and close a tar file named example.tar:

#include <libtar.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

void check() {
	int ret;
	TAR *t;

	ret = tar_open(&t, "example.tar", NULL, O_RDONLY, 0, TAR_VERBOSE);
	if (ret == -1) {
		fprintf(stderr, "tar_open failed\n");
		exit(1);
	}
	printf("Successfully opened example.tar\n");

	ret = tar_close(t);
	if (ret == -1) {
		fprintf(stderr, "tar_close failed\n");
		exit(1);
	}
	printf("Successfully closed example.tar\n");
}

... and then we refer to the check function from the Haskell code like this:

module Main where

foreign import ccall "check" check :: IO ()

main :: IO ()
main = check

Like before, we transform the project2.cabal file into a Nix expression by running cabal2nix:

$ cabal2nix . > project2.nix

cabal2nix is smart and notices that our project2.cabal file has an extra-libraries section and adds tar as a dependency of our package in project2.nix:

{ mkDerivation, base, lib, tar }:
mkDerivation {
  pname = "project2";
  version = "1.0.0";
  src = ./.;
  isLibrary = false;
  isExecutable = true;
  executableHaskellDepends = [ base ];
  executableSystemDepends = [ tar ];
  license = lib.licenses.bsd3;
}

The tar dependency goes in the executableSystemDepends section, which indicates that this is a dependency on a native library and not a Haskell dependency.

However, this alone is not enough to thread libtar to our package. We need to update our release.nix to specify which Nix package the tar dependency comes from:

let
  config = {
    packageOverrides = pkgs: rec {
      haskellPackages = pkgs.haskellPackages.override {
        overrides = haskellPackagesNew: haskellPackagesOld: rec {
          project2 =
            haskellPackagesNew.callPackage ./project2.nix {
              tar = pkgs.libtar;
            };
        };
      };
    };
  };

  pkgs = import <nixpkgs> { inherit config; };

in
  rec {
    project2 = pkgs.haskellPackages.project2;

    test = pkgs.runCommand "check" {} ''
      touch example
      tar cf example.tar example
      ${project2}/bin/project2 | tee $out
    '';
  }

In fact, our release.nix file goes a step further and includes two attributes instead of one:

  • the project2 attribute builds our project
  • the test attribute runs a simple test to verify that the program works

If we only care about building the project, then we would just run:

$ nix-build --attr project2 release.nix

... but if we want to build and test our project, we can run:

$ nix-build --attr test release.nix
these derivations will be built:
  /nix/store/6z0jc3s2ya8j226rgma73w03pdphqwx8-project2-1.0.0.drv
  /nix/store/k82nnkiszzfcnjxq29gcbmgnh5cbfc51-check.drv
building path(s) ‘/nix/store/83bf1b1cp0qs6jgryb6yr0jfb759x4k5-project2-1.0.0’
...
configureFlags: --verbose --prefix=/nix/store/83bf1b1cp0qs6jgryb6yr0jfb759x4k5-project2-1.0.0 --libdir=$prefix/lib/$compiler --libsubdir=$pkgid --with-gcc=gcc --package-db=/tmp/nix-build-project2-1.0.0.drv-0/package.conf.d --ghc-option=-optl=-Wl,-rpath=/nix/store/83bf1b1cp0qs6jgryb6yr0jfb759x4k5-project2-1.0.0/lib/ghc-8.0.1/project2-1.0.0 --ghc-option=-j1 --enable-split-objs --disable-library-profiling --disable-profiling --enable-shared --enable-library-vanilla --enable-executable-dynamic --enable-tests --extra-include-dirs=/nix/store/pb3jxhy4z54i24i9s0kyszdmxd2xajc5-libtar-1.2.20/include --extra-lib-dirs=/nix/store/pb3jxhy4z54i24i9s0kyszdmxd2xajc5-libtar-1.2.20/lib
...
building path(s) ‘/nix/store/zc7ai4h44fydbm89a85b5bik5683klyk-check’
Successfully opened example.tar
Successfully closed example.tar
/nix/store/zc7ai4h44fydbm89a85b5bik5683klyk-check

Notice how the configureFlags passed to project2 ensure that Nix's libtar is on cabal's search path when building the library. Then the tests are run and the output is saved to /nix/store/zc7ai4h44fydbm89a85b5bik5683klyk-check, which we can also reference via the result symlink that nix-build creates:

$ cat result 
Successfully opened example.tar
Successfully closed example.tar

Conclusion

This concludes managing native dependencies using Nix. The next section covers customizing Haskell projects.