Skip to content

Commit 84ef9c9

Browse files
committed
Add further on Haskell and C code
1 parent 2e0c04a commit 84ef9c9

File tree

3 files changed

+224
-97
lines changed

3 files changed

+224
-97
lines changed

doc/GUIDE.md

Lines changed: 0 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,103 +1091,6 @@ modified version of a dependency that hasn't yet been released upstream.
10911091
See [stack.yaml documentation](yaml_configuration.md#packages) for more
10921092
details.
10931093

1094-
## Haskell packages with C code
1095-
1096-
A Haskell package can include C source code. For example, consider a simple
1097-
one-package project named `c-example`, created by `stack new c-example` but with
1098-
these changes:
1099-
1100-
A C header file `my-library.h` added in new directory `include`:
1101-
~~~c
1102-
#ifndef MY_LIBRARY_HEADER
1103-
#define MY_LIBRARY_HEADER
1104-
int max(int, int);
1105-
#endif
1106-
~~~
1107-
1108-
A C source code file `my-library.c` added in new directory `c-source`:
1109-
~~~c
1110-
#include "my-library.h"
1111-
1112-
/* Function returning the larger of two integers */
1113-
int max(int x1, int x2) {
1114-
if (x1 > x2)
1115-
return x1;
1116-
else
1117-
return x2;
1118-
}
1119-
~~~
1120-
1121-
A different `src/Lib.hs` Haskell module, including a Haskell foreign import
1122-
declaration making use of the C `max` function:
1123-
~~~haskell
1124-
module Lib
1125-
( someFunc
1126-
, c_max
1127-
) where
1128-
1129-
someFunc :: IO ()
1130-
someFunc = putStrLn "someFunc"
1131-
1132-
foreign import ccall "max" c_max :: Int -> Int -> Int
1133-
~~~
1134-
1135-
A different `app/Main.hs` Haskell module, making use of the Haskell `c_max`
1136-
function now exported from module `Lib`:
1137-
~~~haskell
1138-
module Main ( main ) where
1139-
1140-
import Lib ( c_max, someFunc )
1141-
1142-
main :: IO ()
1143-
main = do
1144-
someFunc
1145-
print $ c_max 10 100
1146-
~~~
1147-
1148-
The package's `package.yaml` file (simplied), used to create the package's
1149-
Cabal file, might look like this:
1150-
~~~yaml
1151-
spec-version: 0.36.0
1152-
1153-
name: c-example
1154-
version: 0.1.0.0
1155-
1156-
# Hpack 0.36.1 and earlier does not support Cabal's 'includes' field
1157-
extra-source-files:
1158-
- include/my-library.h
1159-
1160-
dependencies:
1161-
- base >= 4.7 && < 5
1162-
1163-
library:
1164-
source-dirs: src
1165-
include-dirs: # Where to look for C header files?
1166-
- include
1167-
c-sources: # What C source code files to be compiled and linked?
1168-
- c-source/my-library.c
1169-
1170-
executables:
1171-
c-example-exe:
1172-
main: Main.hs
1173-
source-dirs: app
1174-
ghc-options:
1175-
- -threaded
1176-
- -rtsopts
1177-
- -with-rtsopts=-N
1178-
dependencies:
1179-
- c-example
1180-
~~~
1181-
1182-
The project's `stack.yaml` file only needs to identify a snapshot:
1183-
~~~yaml
1184-
snapshot: lts-22.26 # GHC 9.6.5
1185-
~~~
1186-
1187-
This example project can be built with Stack in the normal way (`stack build`),
1188-
and the built executable can then be executed in the Stack environment in the
1189-
normal way (`stack exec c-example-exe`).
1190-
11911094
## Flags and GHC options
11921095

11931096
There are two common ways to alter how a package will install: with Cabal flags

doc/haskell_and_c_code.md

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
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 `src/Lib.hs` Haskell module, including a Haskell foreign import
33+
declaration making use of the C `max` function:
34+
~~~haskell
35+
module Lib
36+
( someFunc
37+
, c_max
38+
) where
39+
40+
someFunc :: IO ()
41+
someFunc = putStrLn "someFunc"
42+
43+
foreign import ccall "max" c_max :: Int -> Int -> Int
44+
~~~
45+
46+
A different `app/Main.hs` Haskell module, making use of the Haskell `c_max`
47+
function now exported from module `Lib`:
48+
~~~haskell
49+
module Main ( main ) where
50+
51+
import Lib ( c_max, someFunc )
52+
53+
main :: IO ()
54+
main = do
55+
someFunc
56+
print $ c_max 10 100
57+
~~~
58+
59+
The package's `package.yaml` file (simplied), used to create the package's
60+
Cabal file, might look like this:
61+
~~~yaml
62+
spec-version: 0.36.0
63+
64+
name: c-example
65+
version: 0.1.0.0
66+
67+
# Hpack 0.36.1 and earlier does not support Cabal's 'includes' field
68+
extra-source-files:
69+
- include/my-library.h
70+
71+
dependencies:
72+
- base >= 4.7 && < 5
73+
74+
library:
75+
source-dirs: src
76+
include-dirs: # Where to look for C header files?
77+
- include
78+
c-sources: # What C source code files to be compiled and linked?
79+
- c-source/my-library.c
80+
81+
executables:
82+
c-example-exe:
83+
main: Main.hs
84+
source-dirs: app
85+
ghc-options:
86+
- -threaded
87+
- -rtsopts
88+
- -with-rtsopts=-N
89+
dependencies:
90+
- c-example
91+
~~~
92+
93+
The project's `stack.yaml` file only needs to identify a snapshot:
94+
~~~yaml
95+
snapshot: lts-22.26 # GHC 9.6.5
96+
~~~
97+
98+
This example project can be built with Stack in the normal way (`stack build`),
99+
and the built executable can then be executed in the Stack environment in the
100+
normal way (`stack exec c-example-exe`).
101+
102+
## Haskell packages with C `main` function
103+
104+
A Haskell package can include an executable which has a `main` function written
105+
in C. For example, consider a simple one-package Stack project named
106+
`c-example`, with:
107+
108+
A `package.yaml` describing a library and two executables, named `haskell-exe`
109+
and `c-exe`:
110+
111+
~~~yaml
112+
spec-version: 0.36.0
113+
114+
name: c-example
115+
version: 0.1.0.0
116+
117+
dependencies: base
118+
119+
library:
120+
source-dirs: src
121+
# The Lib_stub.h header must be put by GHC somewhere where Cabal can find it.
122+
# This tells GHC to put it in the stubs directory of the project directory.
123+
ghc-options:
124+
- -stubdir autogen-stubs
125+
126+
executables:
127+
haskell-exe:
128+
main: Main.hs
129+
source-dirs: app
130+
ghc-options:
131+
- -threaded
132+
- -rtsopts
133+
- -with-rtsopts=-N
134+
dependencies: c-example
135+
c-exe:
136+
main: main.c
137+
source-dirs: c-app
138+
ghc-options: -no-hs-main
139+
# This specifies that directory stubs should be searched for header files.
140+
include-dirs: autogen-stubs
141+
dependencies: c-example
142+
~~~
143+
144+
A `Lib.hs` module in directory `src`:
145+
~~~haskell
146+
module Lib
147+
( myMax -- Exported only for the use of the 'Haskell' executable
148+
) where
149+
150+
myMax :: Int -> Int -> Int
151+
myMax x1 x2 = if x1 > x2 then x1 else x2
152+
153+
foreign export ccall myMax :: Int -> Int -> Int
154+
~~~
155+
156+
A Haskell module `Main.hs` in directory `app`:
157+
~~~haskell
158+
module Main ( main ) where
159+
160+
import Lib ( myMax )
161+
162+
main :: IO ()
163+
main = print $ myMax 10 100
164+
~~~
165+
166+
A C source file `main.c` in directory `c-app`:
167+
~~~c
168+
// Based in part on
169+
// https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/ffi.html#using-your-own-main
170+
171+
#include <stdio.h> // Provides printf()
172+
173+
#include <HsFFI.h> // Provides hs_init() and hs_exit(). See the Haskell 2010
174+
// Report, 8.7.
175+
176+
// Parts specific to GHC
177+
#ifdef __GLASGOW_HASKELL__
178+
#include "Lib_stub.h" // Automatically generated by GHC, given use of
179+
// foreign export ... in module Lib.hs ...
180+
#endif
181+
182+
int main(int argc, char *argv[]) {
183+
// Initialises the Haskell system and provides it with the available command
184+
// line arguments
185+
hs_init(&argc, &argv);
186+
187+
// Use our foreign export from module Lib.hs ...
188+
printf("%lld\n", myMax(10,100));
189+
190+
// Deinitialise the Haskell system
191+
hs_exit();
192+
return 0;
193+
}
194+
~~~
195+
196+
The `foreign export` declaration in Haskell module `Lib.hs` will cause GHC to
197+
generate a 'stub' C header file named `Lib_stub.h`. The GHC option `-stubdir`
198+
will cause GHC to put that file in the specified directory (`autogen-stubs`).
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 (`autogen-stubs`) to
213+
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).

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ nav:
8989
- Azure CI: azure_ci.md
9090
- Lock files: lock_files.md
9191
- Stack work directories: stack_work.md
92+
- Haskell and C code: haskell_and_c_code.md
9293
- How Stack works (advanced):
9394
- Build overview: build_overview.md
9495
- Stack's code (advanced):

0 commit comments

Comments
 (0)