In this age of the stack domination, not everyone remembers the role of Cabal and cabal-install`. And still cabal and cabal-install do progress and introduce new features, that may benefit entire ecosystem. And it’s quite unfortunate that some of the changes remain unnoticed.

The big list of changes in Cabal-2.0 can be found here. In this post I’d like to look into one single change: Foreign libraries support.

TLDR: you can find all documentation in the cabal user-guide, which is a very good resource to read anyway.

Let’s look into the problem solved by this feature. Sometimes we need to use Haskell in projects written in other language. This can be done either by making an Haskell executable and using some RPC to communicate between different components or by making a shared object. For a long time cabal did not support shared objects definition. Common solution for that problem was to create an executable section in cabal file, add ghc-options -shared and then play games with -fPIC support. That solution was doable but painfull and required knowledge of low level details.

And finally in cabal-2.0 a support for that usecase was introduced. Now we have a new stanza, foreign-library which means that Cabal will build a standalone library to be used with foreign languages.

Inside foreign-library block we can define type of the library:

Currently only native-shared are supported.

If you are using windows you should remember to add options: standlone stanza. With this option libHSrts will not be required. You should not set this option otherwise.

A simple example:

foreign.cabal:

foreign-library     mylib
  type:             native-shared
  lib-version-info: 6:3:2
  build-depends:    base >=4.9 && <4.10
  hs-source-dirs:   src
  other-modules     Lib
  default-language: Haskell2010

If you have foreign exported modules, then you should also provide:

library
  build-depends: base >= 4.9 && <4.10
  hs-source-dirs: src
  other-modules:  Lib
  install-includes: Lib_stub.h
  default-language: Haskell2010

We need Lib_stub.h so header file will be installed in the system. See #5299. This workaround helps with the problem but still you will not be able to call cabal sdist because it will try to copy autogenerated file that does not exist in filesystem.

src/Lib.hs:

module Lib (fac) where

foreign export ccall fac :: Int -> Int
fac :: Int -> Int
fac n = product [1..n]

After cabal install you’ll get:

~/.cabal/
~//cabal/libmylib.so -> libmylib.so.4.2.3
~/.cabal/libmylib.so.4 -> libmylib.so.4.2.3
~/.cabal/libmylib.so.4.2.3
~/.cabal/lib/x86_64-linux-ghc-8.0.2/foreign-0.1.0.0-22cwmsmBVpIHoSDwDYFUR6/include/Lib_stub.h

Enjoy!

Update: problem with sdist can be worked around by using a buildinfo trick

  1. create foreign.buildinfo file with the following contents:

       install-includes: Lib_stub.h
  2. add section to cabal:

    extra-tmp-files: foreign.buildinfo
  3. remove install-includes stanza - now sdist works.

Enjoy again!




comments powered by Disqus