/haskell-ffi-cabal-foreign-library-examples

example project of using cabal 2.0`s foreign-library feature to build a haskell library and call it from c/c++

Primary LanguageHaskell

haskell-ffi-cabal-foreign-library-examples

This repository contains a Cabal 2.0 project showing how to build a haskell library to use in c/c++ using the new foreign-library feature. The foreign-library feature greatly simplifies the process of creating haskell libraries for use by other languages.

The lens and deepseq packages are included mainly as a test case for dependencies. The example contains code that is intended to test features I need for my own projects, in particular 🥔 PuzzleScript. I would love to merge a PR including more relevant examples 😘.

Usage

make run

I have only tested this on Mac. You may need to fadangle it a little on Linux/Win such that it can locate the library.

Walkthrough

potato.cabal defines our module using foreign-library

foreign-library potato
  type:                native-shared

  if os(Windows)
    options: standalone
    mod-def-file: PotatoLib.def

  other-modules:       Potato
  build-depends:
    base ^>=4.12.0.0
    , lens == 4.*
    , deepseq == 1.4.*
  hs-source-dirs:      src
  c-sources:           csrc/potato.cpp
  default-language:    Haskell2010

Please see cabal docs for a more thorough description explaining the meaning of each field. The important detail here is

c-sources:           csrc/potato.cpp

which points to a file that cabal build will build for us (and handle all linker/include issues for us). In this case, we wrap all exported methods from our 🥔 Haskell module and from ghc's HsFFI.h inside of helper functions. Only potato.h needs to be included by the user. In this manner, we are building a 🥔 c++ library that calls our 🥔 haskell library. If you don't wish to wrap the functionality, you can simply leave potato.cpp empty and call the exported methods from our 🥔 haskell library directly. In this case, you'll need to include Potato_stub.h which is generated by cabal build and likely HsFFI.h when you use the library.

void potatoInit(void); and void potatoExit(void); simply wrap hs_init and hs_exit which start and stop the haskell runtime respectively. void test() calls all the functions in our 🥔 haskell module.

The capp/ folder contains our cpp source that will call code from our haskell potato module. The makefile copies the libpotato.dylib file generated by cabal build into its local directory then builds using g++ with the needed flags.

g++ -g -Wall potatomain.cpp -o $@ \
-I../csrc \
-lpotato \
-L./

If your potatomain.cpp is using methods from the library directly, then you will need to add the flags -I../dist/build/potato/potato-tmp for Potato_stub.h and something like -I/usr/local/lib/ghc-8.4.4/include/ for HsFFI.h.

Finally, the makefile in the root directory runs cabal configure && cabal build and then calls make inside of capp. make run sets the correct DYLD_LIBRARY_PATH and runs the app it compiled in capp. I don't understand how linking shared libraries is suppose to work on Mac so this is the best I could do. If you know a better way to do this please let me know or fire up a PR 🔥🔥🔥!

THX

I used this guide as a starting point which includes links to many other resources I found helpful so I won't list them here. The guide contains a script for gathering the scattered libraries but I didn't seem to need it here. As far as I can tell, Cabal 2.0 will package everything that's needed into a single shared library.

Enjoy!