15

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?

Community
  • 1
  • 1
Boris
  • 5,094
  • 4
  • 45
  • 71
  • 2
    There are various libraries that provide the conio functions. Have you tried `Extra-Libraries: crtdll` or `Extra-Libraries: msvcrt`? By the way, according to MSDN, you should use `_getch` instead of `getch`, but the header file might do that for you. –  Nov 24 '12 at 19:42
  • 1
    Note that this only affects ghc/ghci in Windows, and the solution code doesn't work in WinHugs in particular, so you need it to only compile with this when it's specifically Windows/ghc. – AndrewC Nov 24 '12 at 20:14
  • 1
    @Tinctorius I have just tried `Extra-libraries: msvcrt` and `Extra-libraries: crtdll` alone and in combination. It didn't change the output of `cabal build`. I found `msvcrt.lib` and `crtdll.c` under my visual studio installation, and copied them to my folder, but it didn't change anything. – Boris Nov 24 '12 at 20:20
  • @Tinctorius In ghci and compiling with ghc, it seems to work with both `getch` and `_getch`. `conio.h` includes `getch` `#ifndef _NO_OLDNAMES`, so I guess that's not defined for me. But thanks, changed to `_getch`. – Boris Nov 24 '12 at 20:30
  • 3
    @Boris: if you say `Extra-Libraries: NAME`, Cabal should be looking for `...\Haskell Platform\2012.4.0.0\mingw\lib\libNAME.a`. I have `libcrtdll.a` in that directory, so a dependency on `crtdll` works fine for me. Have you tried supplying the `--extra-lib-dirs` parameter to `cabal`, pointing to that directory? –  Nov 24 '12 at 22:33
  • 1
    @Tinctorius: I now have `Extra-libraries: crtdll` in my cabal file. I have the file `libcrtdll.a` under `...\Haskell Platform\2012.4.0.0\mingw\lib`, and I have configured cabal with the `extra-lib-dirs="(Path to lib dir)"` flag. I now get the error: `parse error on input 'import`, so it's different. B.t.w., I thought cabal would include the `lib` folder by default? – Boris Nov 25 '12 at 00:50
  • 1
    @Tinctorius: I found out how to solve the `parse error on input 'import'` that I got. I just needed `extensions: ForeignFunctionInterface` in my cabal file. Now it works, even without the `--extra-lib-dirs` flag, it seems cabal looks in the `...\mingw\lib` folder automatically. If you write up your comments as an answer, I can accept it. Thanks. – Boris Nov 25 '12 at 01:38

1 Answers1

7

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
!
  • 1
    For future reference, is there somewhere I can look up which library provides a function? How did you find out that `_getch` is provided by msvcrt and crtdll, and that the latter is deprecated? Light googling didn't reveal anything. – Boris Nov 26 '12 at 10:52
  • 1
    MSDN is (a bad attempt at) the canonical Windows development manual, which usually gives the header file and library to use for a given function (you can search it with Google's `site:` operator). The article on [_getch and friends](http://msdn.microsoft.com/en-us/library/078sfkak%28v=vs.71%29.aspx) only mentions that "all versions of the C run-time libraries" support it. After finding `libcrtdll.a` and `libmsvcrt.a` in my `lib` directory (CRT = C run-time), I then searched Google for the difference between `crtdll` and `msvcrt`, and figured out the former is a relic from the Windows 95 era. –  Nov 26 '12 at 14:47
  • @Thanks, nice detective work. Yeah, MSDN seems not exactly designed to lead you straight to the information you need :) – Boris Nov 26 '12 at 19:51
  • I had the same issue with GHCJS, with "foreign import javascript" (reflex-platform) and solved now. Thanks! – spinus May 20 '17 at 02:24