haskellffihaskell-stackghci

Haskell FFI: stack run is ok, but GHCi does not link properly


I am trying to learn how to structure a Haskell project/workflow that uses FFI. I am using stack, but I find myself unable to use GHCi when it comes to the imported foreign functions.

Here is a simplified version of the problem. Let's say that I have the following two files in $PROJECT_ROOT/cbits:

hello.h

#ifndef HELLO_H
#define HELLO_H

extern "C"
{
  int foo();
}

#endif /* HELLO_H */

hello.cpp

#include "hello.h"

#include <iostream>

int foo()
{
  std::cout << "extremely dangerous side effect" << std::endl;
  return 42;
}

My Main.hs file:

module Main where

import Foreign.C

foreign import ccall unsafe "foo" foo :: IO CInt

-- this does side effects and prints '42'
main = foo >>= print

The relevant (C++ specific) section of my package.yaml is:

include-dirs:
  - cbits
cxx-sources:
  - cbits/*.cpp
cxx-options:
  - -std=c++17
extra-libraries:
  - stdc++

I am using the souffle-haskell's package.yaml as a reference.

Compiling and running with stack run is ok and I get the expected output:

extremely dangerous side effect
42

But, in the GHCi session (run with stack ghci), calling main gives:

ghc: ^^ Could not load 'foo', dependency unresolved. See top entry above.


GHC.ByteCode.Linker: can't find label
During interactive linking, GHCi couldn't find the following symbol:
  foo
This may be due to you not asking GHCi to load extra object files,
archives or DLLs needed by your current session.  Restart GHCi, specifying
the missing library using the -L/path/to/object/dir and -lmissinglibname
flags, or simply by naming the relevant files on the GHCi command line.
Alternatively, this link failure might indicate a bug in GHCi.
If you suspect the latter, please report this as a GHC bug:
  https://www.haskell.org/ghc/reportabug

The problem is not present if I compile hello.cpp beforehand:

g++ -c cbits/hello.cpp -o cbits/hello.o

And then run stack ghci --ghci-options cbits/hello.o, as suggested by the GHCi error message.

Question is: do I really need to maintain a separate *.o file specifically for GHCi? Searching online I have found discussions addressing only the GHCi part or the stack/cabal part, but not both. The only useful answer that I have found is this one from 2013, which reaffirms the "solution" given by GHCi and does not mention stack or cabal.


Solution

  • Question is: do I really need to maintain a separate *.o file specifically for GHCi?

    Answer is: no. After several tries, the only thing that I had to change was the name of an option:

    - cxx-sources:
    + c-sources:
    

    This left the behaviour of stack run unchanged, and allowed GHCi to link properly to the compiled code.