|
| 1 | +<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> |
| 2 | + |
| 3 | +# Haskell and C code |
| 4 | + |
| 5 | +## Haskell packages with C code |
| 6 | + |
| 7 | +A Haskell package can include C source code. For example, consider a simple |
| 8 | +one-package Stack project named `c-example`, created by `stack new c-example` |
| 9 | +but with these changes: |
| 10 | + |
| 11 | +A C header file `my-library.h` added in new directory `include`: |
| 12 | +~~~c |
| 13 | +#ifndef MY_LIBRARY_HEADER |
| 14 | +#define MY_LIBRARY_HEADER |
| 15 | +int max(int, int); |
| 16 | +#endif |
| 17 | +~~~ |
| 18 | + |
| 19 | +A C source code file `my-library.c` added in new directory `c-source`: |
| 20 | +~~~c |
| 21 | +#include "my-library.h" |
| 22 | + |
| 23 | +/* Function returning the larger of two integers */ |
| 24 | +int max(int x1, int x2) { |
| 25 | + if (x1 > x2) |
| 26 | + return x1; |
| 27 | + else |
| 28 | + return x2; |
| 29 | +} |
| 30 | +~~~ |
| 31 | +
|
| 32 | +A different Haskell module in source file `src/Lib.hs`, including a Haskell |
| 33 | +foreign import declaration making use of the C `max` function: |
| 34 | +~~~haskell |
| 35 | +module Lib ( c_max ) where |
| 36 | +
|
| 37 | +foreign import ccall "max" c_max :: Int -> Int -> Int |
| 38 | +~~~ |
| 39 | + |
| 40 | +A different Haskell module in source file `app/Main.hs`, making use of the |
| 41 | +Haskell function `c_max` exported from module `Lib`: |
| 42 | +~~~haskell |
| 43 | +module Main ( main ) where |
| 44 | + |
| 45 | +import Lib ( c_max ) |
| 46 | + |
| 47 | +main :: IO () |
| 48 | +main = print $ c_max 10 100 |
| 49 | +~~~ |
| 50 | + |
| 51 | +The package's `package.yaml` file (simplied), used to create the package's |
| 52 | +Cabal file, might look like this: |
| 53 | +~~~yaml |
| 54 | +spec-version: 0.36.0 |
| 55 | + |
| 56 | +name: c-example |
| 57 | +version: 0.1.0.0 |
| 58 | + |
| 59 | +extra-source-files: |
| 60 | +- include/my-library.h |
| 61 | + |
| 62 | +dependencies: |
| 63 | +- base >= 4.7 && < 5 |
| 64 | + |
| 65 | +library: |
| 66 | + source-dirs: src |
| 67 | + include-dirs: # Where to look for C header files? |
| 68 | + - include |
| 69 | + c-sources: # What C source code files to be compiled and linked? |
| 70 | + - c-source/my-library.c |
| 71 | + |
| 72 | +executables: |
| 73 | + c-example-exe: |
| 74 | + main: Main.hs |
| 75 | + source-dirs: app |
| 76 | + ghc-options: |
| 77 | + - -threaded |
| 78 | + - -rtsopts |
| 79 | + - -with-rtsopts=-N |
| 80 | + dependencies: |
| 81 | + - c-example |
| 82 | +~~~ |
| 83 | + |
| 84 | +The project's `stack.yaml` file only needs to identify a snapshot: |
| 85 | +~~~yaml |
| 86 | +snapshot: lts-22.26 # GHC 9.6.5 |
| 87 | +~~~ |
| 88 | + |
| 89 | +This example project can be built with Stack in the normal way (`stack build`), |
| 90 | +and the built executable can then be executed in the Stack environment in the |
| 91 | +normal way (`stack exec c-example-exe`). |
| 92 | + |
| 93 | +## Haskell packages with C `main` function |
| 94 | + |
| 95 | +A Haskell package can include an executable which has a `main` function written |
| 96 | +in C. For example, consider a simple one-package Stack project named |
| 97 | +`c-example`, with: |
| 98 | + |
| 99 | +A `package.yaml` describing a library and two executables, named `haskell-exe` |
| 100 | +and `c-exe`: |
| 101 | + |
| 102 | +~~~yaml |
| 103 | +spec-version: 0.36.0 |
| 104 | + |
| 105 | +name: c-example |
| 106 | +version: 0.1.0.0 |
| 107 | + |
| 108 | +dependencies: base |
| 109 | + |
| 110 | +library: |
| 111 | + source-dirs: src |
| 112 | + # The Lib_stub.h header must be put by GHC somewhere where Cabal can find it. |
| 113 | + # This tells GHC to put it in the autogen-stubs directory of the project |
| 114 | + # directory. |
| 115 | + ghc-options: |
| 116 | + - -stubdir autogen-stubs |
| 117 | + |
| 118 | +executables: |
| 119 | + haskell-exe: |
| 120 | + main: Main.hs |
| 121 | + source-dirs: app |
| 122 | + ghc-options: |
| 123 | + - -threaded |
| 124 | + - -rtsopts |
| 125 | + - -with-rtsopts=-N |
| 126 | + dependencies: c-example |
| 127 | + c-exe: |
| 128 | + main: main.c |
| 129 | + source-dirs: c-app |
| 130 | + ghc-options: -no-hs-main |
| 131 | + # This specifies that directory autogen-stubs should be searched for header |
| 132 | + # files. |
| 133 | + include-dirs: autogen-stubs |
| 134 | + dependencies: c-example |
| 135 | +~~~ |
| 136 | + |
| 137 | +A Haskell module souce file named `Lib.hs` in directory `src`: |
| 138 | +~~~haskell |
| 139 | +module Lib |
| 140 | + ( myMax -- Exported only for the use of the 'Haskell' executable |
| 141 | + ) where |
| 142 | + |
| 143 | +myMax :: Int -> Int -> Int |
| 144 | +myMax x1 x2 = if x1 > x2 then x1 else x2 |
| 145 | + |
| 146 | +foreign export ccall myMax :: Int -> Int -> Int |
| 147 | +~~~ |
| 148 | + |
| 149 | +A Haskell module source file named `Main.hs` in directory `app`: |
| 150 | +~~~haskell |
| 151 | +module Main ( main ) where |
| 152 | + |
| 153 | +import Lib ( myMax ) |
| 154 | + |
| 155 | +main :: IO () |
| 156 | +main = print $ myMax 10 100 |
| 157 | +~~~ |
| 158 | + |
| 159 | +A C source file named `main.c` in directory `c-app`: |
| 160 | +~~~c |
| 161 | +// Based in part on |
| 162 | +// https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/ffi.html#using-your-own-main |
| 163 | + |
| 164 | +#include <stdio.h> // Provides printf() |
| 165 | + |
| 166 | +#include <HsFFI.h> // Provides hs_init() and hs_exit(). See the Haskell 2010 |
| 167 | + // Report, 8.7. |
| 168 | + |
| 169 | +// Parts specific to GHC |
| 170 | +#ifdef __GLASGOW_HASKELL__ |
| 171 | +#include "Lib_stub.h" // Automatically generated by GHC, given use of |
| 172 | + // foreign export ... in module Lib.hs ... |
| 173 | +#endif |
| 174 | + |
| 175 | +int main(int argc, char *argv[]) { |
| 176 | + // Initialises the Haskell system and provides it with the available command |
| 177 | + // line arguments |
| 178 | + hs_init(&argc, &argv); |
| 179 | + |
| 180 | + // Use our foreign export from module Lib.hs ... |
| 181 | + printf("%lld\n", myMax(10,100)); |
| 182 | + |
| 183 | + // De-initialise the Haskell system |
| 184 | + hs_exit(); |
| 185 | + return 0; |
| 186 | +} |
| 187 | +~~~ |
| 188 | +
|
| 189 | +The `foreign export` declaration in Haskell module `Lib` will cause GHC to |
| 190 | +generate a 'stub' C header file named `Lib_stub.h`. The GHC option `-stubdir` |
| 191 | +will cause GHC to put that file in the specified directory (`autogen-stubs`, in |
| 192 | +this example). |
| 193 | +
|
| 194 | +!!! info |
| 195 | +
|
| 196 | + If GHC's `-stubdir` option is omitted, GHC will put the generated C header |
| 197 | + file together with the other build artefacts for the module. However, that |
| 198 | + location cannot be specified reliably using the `include-dirs` key. |
| 199 | +
|
| 200 | +That generated C header file will have content like: |
| 201 | +~~~c |
| 202 | +#include <HsFFI.h> |
| 203 | +#if defined(__cplusplus) |
| 204 | +extern "C" { |
| 205 | +#endif |
| 206 | +extern HsInt myMax(HsInt a1, HsInt a2); |
| 207 | +#if defined(__cplusplus) |
| 208 | +} |
| 209 | +#endif |
| 210 | +~~~ |
| 211 | + |
| 212 | +The `include-dirs` key will cause the specified directory (again, |
| 213 | +`autogen-stubs` in this example) to be searched for C header files. |
| 214 | + |
| 215 | +The project's `stack.yaml` file only needs to identify a snapshot: |
| 216 | +~~~yaml |
| 217 | +snapshot: lts-22.26 # GHC 9.6.5 |
| 218 | +~~~ |
| 219 | + |
| 220 | +This example project can be built with Stack in the normal way (`stack build`), |
| 221 | +and the built executables can then be executed in the Stack environment in the |
| 222 | +normal way (`stack exec haskell-exe` for the 'Haskell' executable and |
| 223 | +`stack exec c-exe` for the 'C' executable). |
0 commit comments