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.
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.