about summary refs log tree commit diff
path: root/third_party/bazel/rules_haskell/README.md
blob: 5357946424a8c561ad8cdfa50a2d68a11cd8ec0a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
<p align="left"><img src="logo/horizontal.png" alt="rules_haskell" height="100px"></p>

# Haskell rules for [Bazel][bazel]

[![CircleCI](https://circleci.com/gh/tweag/rules_haskell.svg?style=svg)](https://circleci.com/gh/tweag/rules_haskell)
[![Build Status](https://dev.azure.com/tweag/rules_haskell/_apis/build/status/tweag.rules_haskell?branchName=master)](https://dev.azure.com/tweag/rules_haskell/_build/latest?definitionId=1?branchName=master)

Bazel automates building and testing software. It scales to very large
multi-language projects. This project extends Bazel with build rules
for Haskell. Get started building your own project using these rules
wih the [setup script below](#setup).

[bazel]: https://bazel.build/
[bazel-getting-started]: https://docs.bazel.build/versions/master/getting-started.html
[bazel-cli]: https://docs.bazel.build/versions/master/command-line-reference.html
[external-repositories]: https://docs.bazel.build/versions/master/external.html
[nix]: https://nixos.org/nix

## Rule summary

The full reference documentation for rules is at https://haskell.build.

## Setup

You'll need [Bazel >= 0.24][bazel-getting-started] installed.

### The easy way

In a fresh directory, run:

```console
$ curl https://haskell.build/start | sh
```

This will generate initial `WORKSPACE` and `BUILD` files for you. See the
[examples](./tests) and the [API reference](#Rules) below to adapt these for
you project. Then,

```console
$ bazel build //...    # Build all targets
$ bazel test //...     # Run all tests
```

You can learn more about Bazel's command line
syntax [here][bazel-cli]. Common [commands][bazel-cli-commands] are
`build`, `test`, `run` and `coverage`.

### Nixpkgs

This rule set supports [Nixpkgs][nixpkgs]. If you are on NixOS, or if
you are using Nixpkgs on your project, consider passing the following
argument on the command-line to select a Nixpkgs-based toolchain for
the build:

```
$ bazel build --host_platform=@io_tweag_rules_haskell//haskell/platforms:linux_x86_64_nixpkgs //...
```

See [below](#saving-common-command-line-flags-to-a-file) to
permanently set that flag.

[bazel-cli-commands]: https://docs.bazel.build/versions/master/command-line-reference.html#commands
[nixpkgs]: https://nixos.org/nixpkgs/

### Doing it manually

Add the following to your `WORKSPACE` file, and select a `$VERSION`
(or even an arbitrary commit hash) accordingly.

```bzl
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
  name = "io_tweag_rules_haskell",
  strip_prefix = "rules_haskell-$VERSION",
  urls = ["https://github.com/tweag/rules_haskell/archive/v$VERSION.tar.gz"],
)

load(
    "@io_tweag_rules_haskell//haskell:haskell.bzl",
	"haskell_repositories",
	"haskell_register_toolchains",
)

haskell_repositories()

haskell_register_toolchains()
```

You will then need to write one `BUILD` file for each "package" you
want to define. See below for examples.

## Tutorial and Examples

We provide a [tutorial for writing your first rules][tutorial].
The corresponding source code is in [./tutorial](./tutorial).

A collection of example rules is in [./examples](./examples).

[tutorial]: https://rules-haskell.readthedocs.io/en/latest/

## Rules

See https://api.haskell.build for the reference documentation on provided
rules. Using [./serve-docs.sh](./serve-docs.sh), you can also view
this documentation locally.

## Language interop

We may be supporting interop with other languages in one way or
another. Please see languages listed below about how.

### C/C++

C/C++ libraries can be specified as dependencies. Exporting Haskell libraries
as C/C++ dependencies currently requires the `cc_haskell_import` rule. This is
a temporary workaround to Bazel limitations.

### Java

You can supply `java_*` rule targets in `deps` of
[haskell_binary](#haskell_binary) and
[haskell_library](#haskell_library). This will make jars produced by
those dependencies available during Haskell source compilation phase
(i.e. not during linking &c. but it's subject to change) and set the
CLASSPATH for that phase as well.

## Troubleshooting

### No such file or directory

If you see error messages complaining about missing `as` (`ld` or indeed
some other executable):

```
cc: error trying to exec 'as': execvp: No such file or directory
`cc' failed in phase `Assembler'. (Exit code: 1)
```

It means that your `gcc` cannot find `as` by itself. This happens only on
certain operating systems which have `gcc` compiled without `--with-as` and
`--with-ld` flags. We need to make `as` visible manually in that case:

```bzl
# Create a symlink to system executable 'as'
genrule(
    name = "toolchain_as",
    outs = ["as"],
    cmd = "ln -s /usr/bin/as $@",
)

# Make it visible to rules_haskell rules:
haskell_toolchain(
    name = "ghc",
    tools = ["@ghc//:bin"],
    version = "8.4.1",
    extra_binaries = [":toolchain_as"], # <----
)
```

### `__STDC_VERSION__` does not advertise C99 or later

If you see an error message like this:

```
/root/.cache/bazel/_bazel_root/b8b1b1d6144a88c698a010767d2217af/external/ghc/lib/ghc-8.4.1/include/Stg.h:29:3: error:
     error: #error __STDC_VERSION__ does not advertise C99 or later
     # error __STDC_VERSION__ does not advertise C99 or later
       ^
   |
29 | # error __STDC_VERSION__ does not advertise C99 or later
   |   ^
```

It means that your `gcc` selects incorrect flavor of C by default. We need
C99 or later, as the error message says, so try this:

```bzl
haskell_toolchain(
    name = "ghc",
    tools = ["@ghc//:bin"],
    version = "8.4.1",
    compiler_flags = ["-optc-std=c99"], # <----
)
```

### `bazel` fails because some executable cannot be found

Make sure you run your build in a pure nix shell
(`nix-shell --pure shell.nix`). If it still doesn’t build,
it is likely a bug.

### A Haskell dependency fails with strange error messages

If you get cabal error messages the likes of:

```
CallStack (from HasCallStack):
  dieNoWrap, called at libraries/Cabal/Cabal/Distribution/Utils/LogProgress.hs:61:9 in Cabal-2.0.1.0:Distribution.Utils.LogProgress
Error:
    The following packages are broken because other packages they depend on are missing. These broken packages must be rebuilt before they can be used.
installed package lens-labels-0.2.0.1 is broken due to missing package profunctors-5.2.2-HzcVdviprlKb7Ap1woZu4, tagged-0.8.5-HviTdonkllN1ZD6he1Zn8I
```

you’ve most likely hit GHC’s
[infamous non-deterministic library ID bug](https://nixos.org/nixpkgs/manual/#how-to-recover-from-ghcs-infamous-non-deterministic-library-id-bug).

### Warning about home modules during non-sandboxed builds

Say you have a folder that mixes source files for two different
libraries or for a library and an executable. If you build with
sandboxing turned off, it is possible that GHC will use the source
files for one library during the build of the other. The danger in
this situation is that because GHC used inputs that Bazel didn't know
about, incremental rebuilds might not be correct. This is why you get
a warning of the following form if this happens:

```
<no location info>: warning: [-Wmissing-home-modules]
    Modules are not listed in command line but needed for compilation: Foo
```

Turning sandboxing on (this is Bazel's default on Linux and macOS)
protects against this problem. If sandboxing is not an option, simply
put the source files for each target in a separate directory (you can
still use a single `BUILD` file to define all targets).

## For `rules_haskell` developers

### Saving common command-line flags to a file

If you find yourself constantly passing the same flags on the
command-line for certain commands (such as `--host_platform` or
`--compiler`), you can augment the [`.bazelrc`](./.bazelrc) file in
this repository with a `.bazelrc.local` file. This file is ignored by
Git.

### Reference a local checkout of `rules_haskell`

When you develop on `rules_haskell`, you usually do it in the context
of a different project that has `rules_haskell` as a `WORKSPACE`
dependency, like so:

```
http_archive(
    name = "io_tweag_rules_haskell",
    strip_prefix = "rules_haskell-" + version,
    sha256 = …,
    urls = …,
)
```

To reference a local checkout instead, use the
[`--override_repository`][override_repository] command line option:
   
```
bazel build/test/run/sync \
  --override_repository io_tweag_rules_haskell=/path/to/checkout
```
   
If you don’t want to type that every time, [temporarily add it to
`.bazelrc`][bazelrc].

[override_repository]: https://docs.bazel.build/versions/master/command-line-reference.html#flag--override_repository
[local_repository]: https://docs.bazel.build/versions/master/be/workspace.html#local_repository
[bazelrc]: https://docs.bazel.build/versions/master/best-practices.html#bazelrc

### Test Suite

To run the test suite for these rules, you'll need [Nix][nix]
installed. First, from the project’s folder start a pure nix shell:

```
$ nix-shell --pure shell.nix
```

This will make sure that bazel has the exact same environment
on every development system (`python`, `ghc`, `go`, …).

To build and run tests locally, execute:

```
$ bazel test //...
```

Skylark code in this project is formatted according to the output of
[buildifier]. You can check that the formatting is correct using:

```
$ bazel run //:buildifier
```

If tests fail then run the following to fix the formatting:

```
$ git rebase --exec "bazel run //:buildifier-fix" <first commit>
```

where `<first commit>` is the first commit in your pull request.
This fixes formatting for each of your commits separately, to keep
the history clean.

[buildifier]: https://github.com/bazelbuild/buildtools/tree/master/buildifier

### <a name="nixpkgs-pin" />How to update the nixpkgs pin

You have to find a new git commit where all our `shell.nix`
dependencies are available from the official NixOS Hydra binary cache.

At least for `x86-linux` this is guaranteed for the `unstable`
channels. You can find the `nixpkgs` git commit of current `unstable`
here:

https://nixos.org/channels/nixos-unstable/git-revision

That might be too old for your use-case (because all tests have to
pass for that channel to be updated), so as a fallback there is:

https://nixos.org/channels/nixos-unstable-small/git-revision

You copy that hash to `url` in
[`./nixpkgs/default.nix`](./nixpkgs/default.nix). Don’t forget to
change the `sha256` or it will use the old version. Please update the
date comment to the date of the `nixpkgs` commit you are pinning to.

### CircleCI

Pull Requests are checked by CircleCI.

If a check fails and you cannot reproduce it locally (e.g. it failed on Darwin
and you only run Linux), you can [ssh into CircleCI to aid debugging][ci-ssh].

[ci-ssh]: https://circleci.com/docs/2.0/ssh-access-jobs/

#### “unable to start any build”

```
error: unable to start any build; either increase '--max-jobs' or enable remote builds
```

We set `--builders ""` and `--max-jobs 0` on CI to be sure all
dependencies are coming from binary caches. You might need to add an
exception (TODO: where to add exception) or [switch to a different
nixpkgs pin](#nixpkgs-pin).