packages feed

stack-3.5.1: doc/topics/haskell_and_c_code.md

<div class="hidden-warning"><a href="https://docs.haskellstack.org/"><img src="https://cdn.jsdelivr.net/gh/commercialhaskell/stack/doc/img/hidden-warning.svg"></a></div>

# Haskell and C code

## Haskell packages with C code

A Haskell package can include C source code. For example, consider a simple
one-package Stack project named `c-example`, created by `stack new c-example`
but with these changes:

A C header file `my-library.h` added in new directory `include`:
~~~c
#ifndef MY_LIBRARY_HEADER
#define MY_LIBRARY_HEADER
int max(int, int);
#endif
~~~

A C source code file `my-library.c` added in new directory `c-source`:
~~~c
#include "my-library.h"

/* Function returning the larger of two integers */
int max(int x1, int x2) {
  if (x1 > x2)
    return x1;
  else
    return x2;
}
~~~

A different Haskell module in source file `src/Lib.hs`, including a Haskell
foreign import declaration making use of the C `max` function:
~~~haskell
module Lib ( c_max ) where

foreign import ccall "max" c_max :: Int -> Int -> Int
~~~

A different Haskell module in source file `app/Main.hs`, making use of the
Haskell function `c_max` exported from module `Lib`:
~~~haskell
module Main ( main ) where

import Lib ( c_max )

main :: IO ()
main = print $ c_max 10 100
~~~

The package's `package.yaml` file (simplied), used to create the package's
Cabal file, might look like this:
~~~yaml
spec-version: 0.36.0

name: c-example
version: 0.1.0.0

extra-source-files:
- include/my-library.h

dependencies:
- base >= 4.7 && < 5

library:
  source-dirs: src
  include-dirs: # Where to look for C header files?
  - include
  c-sources: # What C source code files to be compiled and linked?
  - c-source/my-library.c

executables:
  c-example-exe:
    main: Main.hs
    source-dirs: app
    ghc-options:
    - -threaded
    - -rtsopts
    - -with-rtsopts=-N
    dependencies:
    - c-example
~~~

The project's `stack.yaml` file only needs to identify a snapshot:
~~~yaml
snapshot: lts-23.14 # GHC 9.8.4
~~~

This example project can be built with Stack in the normal way (`stack build`),
and the built executable can then be executed in the Stack environment in the
normal way (`stack exec c-example-exe`).

## Haskell packages with C `main` function

A Haskell package can include an executable which has a `main` function written
in C. For example, consider a simple one-package Stack project named
`c-example`, with:

A `package.yaml` describing a library and two executables, named `haskell-exe`
and `c-exe`:

~~~yaml
spec-version: 0.36.0

name: c-example
version: 0.1.0.0

dependencies: base

library:
  source-dirs: src
  # The Lib_stub.h header must be put by GHC somewhere where Cabal can find it.
  # This tells GHC to put it in the autogen-stubs directory of the project
  # directory.
  ghc-options:
  - -stubdir autogen-stubs

executables:
  haskell-exe:
    main: Main.hs
    source-dirs: app
    ghc-options:
    - -threaded
    - -rtsopts
    - -with-rtsopts=-N
    dependencies: c-example
  c-exe:
    main: main.c
    source-dirs: c-app
    ghc-options: -no-hs-main
    # This specifies that directory autogen-stubs should be searched for header
    # files.
    include-dirs: autogen-stubs
    dependencies: c-example
~~~

!!! warning

    `Cabal-3.12.0.0`, a boot package of GHC 9.10.1, ignores `source-dirs` when
    the `main` file is not a Haskell source code file. This was a regression and
    fixed in subsequent versions of Cabal (the library).

A Haskell module souce file named `Lib.hs` in directory `src`:
~~~haskell
module Lib
  ( myMax -- Exported only for the use of the 'Haskell' executable
  ) where

myMax :: Int -> Int -> Int
myMax x1 x2 = if x1 > x2 then x1 else x2

foreign export ccall myMax :: Int -> Int -> Int
~~~

A Haskell module source file named `Main.hs` in directory `app`:
~~~haskell
module Main ( main ) where

import Lib ( myMax )

main :: IO ()
main = print $ myMax 10 100
~~~

A C source file named `main.c` in directory `c-app`:
~~~c
// Based in part on
// https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/ffi.html#using-your-own-main

#include <stdio.h> // Provides printf()

#include <HsFFI.h> // Provides hs_init() and hs_exit(). See the Haskell 2010
                   // Report, 8.7.

// Parts specific to GHC
#ifdef __GLASGOW_HASKELL__
#include "Lib_stub.h" // Automatically generated by GHC, given use of
                      // foreign export ... in module Lib.hs ...
#endif

int main(int argc, char *argv[]) {
  // Initialises the Haskell system and provides it with the available command
  // line arguments
  hs_init(&argc, &argv);

  // Use our foreign export from module Lib.hs ...
  printf("%lld\n", myMax(10,100));

  // De-initialise the Haskell system
  hs_exit();
  return 0;
}
~~~

The `foreign export` declaration in Haskell module `Lib` will cause GHC to
generate a 'stub' C header file named `Lib_stub.h`. The GHC option `-stubdir`
will cause GHC to put that file in the specified directory (`autogen-stubs`, in
this example).

!!! info

    If GHC's `-stubdir` option is omitted, GHC will put the generated C header
    file together with the other build artefacts for the module. However, that
    location cannot be specified reliably using the `include-dirs` key.

That generated C header file will have content like:
~~~c
#include <HsFFI.h>
#if defined(__cplusplus)
extern "C" {
#endif
extern HsInt myMax(HsInt a1, HsInt a2);
#if defined(__cplusplus)
}
#endif
~~~

The `include-dirs` key will cause the specified directory (again,
`autogen-stubs` in this example) to be searched for C header files.

The project's `stack.yaml` file only needs to identify a snapshot:
~~~yaml
snapshot: lts-23.14 # GHC 9.8.4
~~~

This example project can be built with Stack in the normal way (`stack build`),
and the built executables can then be executed in the Stack environment in the
normal way (`stack exec haskell-exe` for the 'Haskell' executable and
`stack exec c-exe` for the 'C' executable).