Search code examples
haskellcabalfficonio

cabal FFI dependency


I am making a small Haskell game in Windows, where I would like to respond each time the user presses a key. Because getChar behaves strangely on Windows, I use FFI to get access to getch in conio.h, as described here. The relevant code is:

foreign import ccall unsafe "conio.h getch" c_getch :: IO CInt

This works fine, when I run it in ghci, or compile it with ghc. I also want to try making a cabal package out of it, so extending from this question, I include the following in my cabal file:

...
executable noughts
  Includes:          conio.h
  Extra-libraries    conio
...

But when I run cabal configure, it tells me:

cabal: Missing dependency on a foreign library:
* Missing C library: conio

It makes sense, because in my haskell platform directory, under ...\Haskell Platform\2012.4.0.0\mingw there is a conio.h file under the include directory, but no other conio file to provide the object code.

Am I doing this the right way, and if so, how can I find out which library to include in my cabal file?


Solution

  • First off, there is not always a one-to-one mapping between C header files and libraries. In this case, the functions declared in conio.h can be found in various runtime libraries, such as crtdll (deprecated) or msvcrt (preferred, I guess).

    With the Haskell Platform on Windows, Cabal will look for these libraries in .\mingw\lib (under your Haskell Platform directory): if you ask for msvcrt, it will look for .\mingw\lib\libmsvcrt.a. This specific library should already be shipped with your Haskell Platform. (If you want to point to other directories with lib*.a files, you can use Cabal's --extra-lib-dirs option.)

    A tiny example of this would be as follows; this is Main.hs:

    {-# LANGUAGE ForeignFunctionInterface #-}
    import Foreign.C.Types
    foreign import ccall unsafe "conio.h _putch" c_putch :: CInt -> IO ()
    
    main :: IO ()
    main = do
        c_putch . toEnum . fromEnum $ '!'
        c_putch . toEnum . fromEnum $ '\n'
    

    And this would be something-awesome.cabal:

    name:                something-awesome
    version:             0.1.0.0
    build-type:          Simple
    cabal-version:       >=1.8
    
    executable yay
      main-is:             Main.hs
      build-depends:       base ==4.5.*
    
      includes:            conio.h
      extra-libraries:     msvcrt
    

    This should work fine:

    c:\tmp\something-awesome> dir /B
    Main.hs
    something-awesome.cabal
    
    c:\tmp\something-awesome> cabal configure
    Resolving dependencies...
    Configuring something-awesome-0.1.0.0...
    
    c:\tmp\something-awesome> cabal build
    Building something-awesome-0.1.0.0...
    Preprocessing executable 'yay' for something-awesome-0.1.0.0...
    [1 of 1] Compiling Main             ( Main.hs, dist\build\yay\yay-tmp\Main.o )
    Linking dist\build\yay\yay.exe ...
    
    c:\tmp\something-awesome> dist\build\yay\yay.exe
    !