Haskell rules for Bazel
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.
The full reference documentation for rules is at https://haskell.build.
You'll need Bazel >= 0.22.0 installed.
In a fresh directory, run:
$ curl https://haskell.build/start | sh
This will generate initial WORKSPACE
and BUILD
files for you. See the
examples and the API reference below to adapt these for
you project. Then,
$ bazel build //... # Build all targets
$ bazel test //... # Run all tests
You can learn more about Bazel's command line
syntax here. Common commands are
build
, test
, run
and coverage
.
This rule set supports 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 to permanently set that flag.
Add the following to your WORKSPACE
file, and select a $VERSION
(or even an arbitrary commit hash) accordingly.
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.
We provide a tutorial for writing your first rules. The corresponding source code is in ./tutorial.
A collection of example rules is in ./examples.
See https://api.haskell.build for the reference documentation on provided rules. Using ./serve-docs.sh, you can also view this documentation locally.
We may be supporting interop with other languages in one way or another. Please see languages listed below about how.
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.
You can supply java_*
rule targets in deps
of
haskell_binary and
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.
This repository contains no special support for building Cabal packages. This is provided by downstream rule sets. We recommend Hazel for generating rules to build packages published on Hackage, or part of Stackage snapshots, using Bazel.
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:
# 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"], # <----
)
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:
haskell_toolchain(
name = "ghc",
tools = "@ghc//:bin",
version = "8.4.1",
compiler_flags = ["-optc-std=c99"], # <----
)
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.
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.
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
file in
this repository with a .bazelrc.local
file. This file is ignored by
Git.
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
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
.
To run the test suite for these rules, you'll need 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.
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
. 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.
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.
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.