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.
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 AnimalClub. I would love to merge a PR including more relevant examples
It was tested on Mac and Linux (Ubuntu 19.04 with cabal 2.4 and ghc 8.6.5).
potato.cabal defines our module using
foreign-library potato type: native-shared if os(Windows) options: standalone mod-def-file: PotatoLib.def other-modules: Potato build-depends: base ^>=188.8.131.52 , lens == 4.* , deepseq == 1.4.* hs-source-dirs: src c-sources: csrc/potato.cpp default-language: Haskell2010
Note, to support stack pipeline, this is done using the
verbatim field in
Please see cabal docs for a more thorough description explaining the meaning of each field. The important detail here is
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
HsFFI.h inside of helper functions. Only
potato.h needs to be included by the user. In this manner, we are building a
potato.cpp empty and call the exported methods from our
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_exit which start and stop the haskell runtime respectively.
void test() calls all the functions in our
capp/ folder contains our cpp source that will call code from our haskell
potato module. The makefile builds the cpp app using
g++ with the needed flags. Note that it expects the hs library files to be in this directory to work.
g++ -g -Wall potatomain.cpp -o $@ \ -I../csrc \ -lpotato \ -L./
As mentioned earlier, if you want your
capp/potatomain.cpp to use methods from the Haskell library directly instead of calling through
csrc/potato.h, then you will need to add the flags
Potato_stub.h and something like
HsFFI.h. I don't recommend this since it's unclear to me where to fetch these dependencies from in a build pipeline.
Finally, the makefile in the root directory runs
stack build and copies the compiled library into the
capp folder. Then it calls
make inside of
make run runs the app it compiled in
make usingcabal will do the same thing with cabal instead of stack. It uses a different .cabal file but you could probably make it work with the stack generated one too.
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.