feat(third_party/bazel): Check in rules_haskell from Tweag
This commit is contained in:
parent
2eb1dc26e4
commit
f723b8b878
479 changed files with 51484 additions and 0 deletions
1
third_party/bazel/rules_haskell/docs/.gitignore
vendored
Normal file
1
third_party/bazel/rules_haskell/docs/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
_build
|
46
third_party/bazel/rules_haskell/docs/BUILD.bazel
vendored
Normal file
46
third_party/bazel/rules_haskell/docs/BUILD.bazel
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
load("@io_bazel_skydoc//skylark:skylark.bzl", "skylark_doc")
|
||||
|
||||
genrule(
|
||||
name = "guide_html",
|
||||
srcs = ["conf.py"] + glob(["*.rst"]),
|
||||
outs = ["guide_html.zip"],
|
||||
cmd = """
|
||||
set -euo pipefail
|
||||
# Nixpkgs_rules are pointing to every bins individually. Here
|
||||
# we are extracting the /bin dir path to append it to the $$PATH.
|
||||
CWD=`pwd`
|
||||
sphinxBinDir=$${CWD}/$$(echo $(locations @sphinx//:bin) | cut -d ' ' -f 1 | xargs dirname)
|
||||
dotBinDir=$${CWD}/$$(echo $(locations @graphviz//:bin) | cut -d ' ' -f 1 | xargs dirname)
|
||||
zipBinDir=$${CWD}/$$(echo $(locations @zip//:bin) | cut -d ' ' -f 1 | xargs dirname)
|
||||
PATH=$${PATH}:$${sphinxBinDir}:$${dotBinDir}:$${zipBinDir}
|
||||
sourcedir=$$(dirname $(location conf.py))
|
||||
builddir=$$(mktemp -d rules_haskell_docs.XXXX)
|
||||
sphinx-build -M html $$sourcedir $$builddir -W -N -q
|
||||
(cd $$builddir/html && zip -q -r $$CWD/$@ .)
|
||||
rm -rf $$builddir
|
||||
""",
|
||||
tools = [
|
||||
"@graphviz//:bin",
|
||||
"@sphinx//:bin",
|
||||
"@zip//:bin",
|
||||
],
|
||||
)
|
||||
|
||||
skylark_doc(
|
||||
name = "api_html",
|
||||
srcs = [
|
||||
|
||||
# The order of these files defines the order in which the corresponding
|
||||
# sections are presented in the docs.
|
||||
"//haskell:haskell.bzl",
|
||||
"//haskell:haddock.bzl",
|
||||
"//haskell:lint.bzl",
|
||||
"//haskell:toolchain.bzl",
|
||||
"//haskell:protobuf.bzl",
|
||||
"//haskell:cc.bzl",
|
||||
"//haskell:repositories.bzl",
|
||||
"//haskell:ghc_bindist.bzl",
|
||||
"//haskell:nixpkgs.bzl",
|
||||
],
|
||||
format = "html",
|
||||
)
|
41
third_party/bazel/rules_haskell/docs/conf.py
vendored
Normal file
41
third_party/bazel/rules_haskell/docs/conf.py
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
project = 'rules_haskell'
|
||||
|
||||
copyright = '2018, The rules_haskell authors'
|
||||
|
||||
source_suffix = '.rst'
|
||||
|
||||
extensions = [
|
||||
'sphinx.ext.graphviz',
|
||||
'sphinx.ext.todo',
|
||||
]
|
||||
|
||||
master_doc = 'index'
|
||||
|
||||
language = None
|
||||
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
html_theme = 'alabaster'
|
||||
|
||||
html_theme_options = {
|
||||
'show_powered_by': False,
|
||||
'github_user': 'tweag',
|
||||
'github_repo': 'rules_haskell',
|
||||
'github_banner': True,
|
||||
'github_type': "star",
|
||||
'show_related': False,
|
||||
'note_bg': '#FFF59C',
|
||||
}
|
||||
|
||||
html_show_sphinx = False
|
||||
|
||||
todo_include_todos = True
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass).
|
||||
latex_documents = [
|
||||
(master_doc, 'rules_haskell.tex', 'rules\\_haskell Documentation',
|
||||
'Tweag I/O', 'manual'),
|
||||
]
|
283
third_party/bazel/rules_haskell/docs/haskell-use-cases.rst
vendored
Normal file
283
third_party/bazel/rules_haskell/docs/haskell-use-cases.rst
vendored
Normal file
|
@ -0,0 +1,283 @@
|
|||
.. _use-cases:
|
||||
|
||||
Common Haskell Build Use Cases
|
||||
==============================
|
||||
|
||||
Picking a compiler
|
||||
------------------
|
||||
|
||||
Unlike Bazel's native C++ rules, rules_haskell does not auto-detect
|
||||
a Haskell compiler toolchain from the environment. This is by design.
|
||||
We require that you declare a compiler to use in your ``WORKSPACE``
|
||||
file.
|
||||
|
||||
There are two common sources for a compiler. One is to use the
|
||||
official binary distributions from `haskell.org`_. This is done using
|
||||
the `ghc_bindist`_ rule.
|
||||
|
||||
The compiler can also be pulled from Nixpkgs_, a set of package
|
||||
definitions for the `Nix package manager`_. Pulling the compiler from
|
||||
Nixpkgs makes the build more hermetic, because the transitive closure
|
||||
of the compiler and all its dependencies is precisely defined in the
|
||||
``WORKSPACE`` file. Use `rules_nixpkgs`_ to do so (where ``X.Y.Z``
|
||||
stands for any recent release)::
|
||||
|
||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
|
||||
http_archive(
|
||||
name = "io_tweag_rules_nixpkgs",
|
||||
strip_prefix = "rules_nixpkgs-X.Y.Z",
|
||||
urls = ["https://github.com/tweag/rules_nixpkgs/archive/vX.Y.Z.tar.gz"],
|
||||
)
|
||||
|
||||
load(
|
||||
"@io_tweag_rules_nixpkgs//nixpkgs:nixpkgs.bzl",
|
||||
"nixpkgs_git_repository",
|
||||
"nixpkgs_package"
|
||||
)
|
||||
|
||||
nixpkgs_git_repository(
|
||||
name = "nixpkgs",
|
||||
revision = "18.09", # Any tag or commit hash
|
||||
)
|
||||
|
||||
nixpkgs_package(
|
||||
name = "ghc",
|
||||
repositories = { "nixpkgs": "@nixpkgs//:default.nix" }
|
||||
attribute_path = "haskell.compiler.ghc843", # Any compiler version
|
||||
build_file = "@io_tweag_rules_haskell//haskell:ghc.BUILD",
|
||||
)
|
||||
|
||||
register_toolchains("//:ghc")
|
||||
|
||||
This workspace description specifies which Nixpkgs version to use,
|
||||
then exposes a Nixpkgs package containing the GHC compiler. The
|
||||
description assumes that there exists a ``BUILD`` file at the root of
|
||||
the repository that includes the following::
|
||||
|
||||
haskell_toolchain(
|
||||
name = "ghc",
|
||||
# Versions here and in WORKSPACE must match.
|
||||
version = "8.4.3",
|
||||
# Use binaries from @ghc//:bin to define //:ghc toolchain.
|
||||
tools = ["@ghc//:bin"],
|
||||
)
|
||||
|
||||
.. _Bazel+Nix blog post: https://www.tweag.io/posts/2018-03-15-bazel-nix.html
|
||||
.. _Nix package manager: https://nixos.org/nix
|
||||
.. _Nixpkgs: https://nixos.org/nixpkgs/manual/
|
||||
.. _ghc_bindist: http://api.haskell.build/haskell/ghc_bindist.html#ghc_bindist
|
||||
.. _haskell.org: https://haskell.org
|
||||
.. _haskell_binary: http://api.haskell.build/haskell/haskell.html#haskell_binary
|
||||
.. _haskell_library: http://api.haskell.build/haskell/haskell.html#haskell_library
|
||||
.. _rules_nixpkgs: https://github.com/tweag/rules_nixpkgs
|
||||
|
||||
Loading targets in a REPL
|
||||
-------------------------
|
||||
|
||||
Rebuilds are currently not incremental *within* a binary or library
|
||||
target (rebuilds are incremental across targets of course). Any change
|
||||
in any source file will trigger a rebuild of all source files listed
|
||||
in a target. In Bazel, it is conventional to decompose libraries into
|
||||
small units. In this way, libraries require less work to rebuild.
|
||||
Still, for interactive development full incrementality and fast
|
||||
recompilation times are crucial for a good developer experience. We
|
||||
recommend making all development REPL-driven for fast feedback when
|
||||
source files change.
|
||||
|
||||
Every `haskell_binary`_ and every `haskell_library`_ target has an
|
||||
optional executable output that can be run to drop you into an
|
||||
interactive session. If the target's name is ``foo``, then the REPL
|
||||
output is called ``foo@repl``.
|
||||
|
||||
Consider the following binary target::
|
||||
|
||||
haskell_binary(
|
||||
name = "hello",
|
||||
srcs = ["Main.hs", "Other.hs"],
|
||||
deps = ["//lib:some_lib"],
|
||||
)
|
||||
|
||||
The target above also implicitly defines ``hello@repl``. You can call
|
||||
the REPL like this (requires Bazel 0.15 or later)::
|
||||
|
||||
$ bazel run //:hello@repl
|
||||
|
||||
This works for any ``haskell_binary`` or ``haskell_library`` target.
|
||||
Modules of all libraries will be loaded in interpreted mode and can be
|
||||
reloaded using the ``:r`` GHCi command when source files change.
|
||||
|
||||
Building code with Hackage dependencies (using Nix)
|
||||
---------------------------------------------------
|
||||
|
||||
Each Haskell library or binary needs a simple build description to
|
||||
tell Bazel what source files to use and what the dependencies are, if
|
||||
any. Packages on Hackage don't usually ship with `BUILD.bazel` files.
|
||||
So if your code depends on them, you either need to write a build
|
||||
description for each package, generate one (see next section), or
|
||||
decide not to use Bazel to build packages published on Hackage. This
|
||||
section documents one way to do the latter.
|
||||
|
||||
Nix is a package manager. The set of package definitions is called
|
||||
Nixpkgs. This repository contains definitions for most actively
|
||||
maintained Cabal packages published on Hackage. Where these packages
|
||||
depend on system libraries like zlib, ncurses or libpng, Nixpkgs also
|
||||
contains package descriptions for those, and declares those as
|
||||
dependencies of the Cabal packages. Since these definitions already
|
||||
exist, we can reuse them instead of rewriting these definitions as
|
||||
build definitions in Bazel. See the `Bazel+Nix blog post`_ for a more
|
||||
detailed rationale.
|
||||
|
||||
To use Nixpkgs in Bazel, we need `rules_nixpkgs`_. See `Picking
|
||||
a compiler`_ for how to import Nixpkgs rules into your workspace and
|
||||
how to use a compiler from Nixpkgs. To use Cabal packages from
|
||||
Nixpkgs, replace the compiler definition with the following::
|
||||
|
||||
nixpkgs_package(
|
||||
name = "ghc",
|
||||
repositories = { "nixpkgs": "@nixpkgs//:default.nix" },
|
||||
nix_file = "//:ghc.nix",
|
||||
build_file = "@io_tweag_rules_haskell//haskell:ghc.BUILD",
|
||||
)
|
||||
|
||||
This definition assumes a ``ghc.nix`` file at the root of the
|
||||
repository. In this file, you can use the Nix expression language to
|
||||
construct a compiler with all the packages you depend on in scope::
|
||||
|
||||
with (import <nixpkgs> {});
|
||||
|
||||
haskellPackages.ghcWithPackages (p: with p; [
|
||||
containers
|
||||
lens
|
||||
text
|
||||
])
|
||||
|
||||
Each package mentioned in ``ghc.nix`` can then be imported using
|
||||
`haskell_toolchain_library`_ in ``BUILD`` files.
|
||||
|
||||
.. _haskell_toolchain_library: http://api.haskell.build/haskell/haskell.html#haskell_toolchain_library
|
||||
|
||||
Building code with Hackage dependencies (using Hazel)
|
||||
-----------------------------------------------------
|
||||
|
||||
.. todo::
|
||||
|
||||
Explain how to use Hazel instead of Nix
|
||||
|
||||
Generating API documentation
|
||||
----------------------------
|
||||
|
||||
The `haskell_doc`_ rule can be used to build API documentation for
|
||||
a given library (using Haddock). Building a target called
|
||||
``//my/pkg:mylib_docs`` would make the documentation available at
|
||||
``bazel-bin/my/pkg/mylib_docs/index/index.html``.
|
||||
|
||||
Alternatively, you can use the
|
||||
``@io_tweag_rules_haskell//haskell:haskell.bzl%haskell_doc_aspect``
|
||||
aspect to ask Bazel from the command-line to build documentation for
|
||||
any given target (or indeed all targets), like in the following:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ bazel build //my/pkg:mylib \
|
||||
--aspects @io_tweag_rules_haskell//haskell:haskell.bzl%haskell_doc_aspect
|
||||
|
||||
.. _haskell_doc: http://api.haskell.build/haskell/haddock.html#haskell_doc
|
||||
|
||||
Linting your code
|
||||
-----------------
|
||||
|
||||
The `haskell_lint`_ rule does not build code but runs the GHC
|
||||
typechecker on all listed dependencies. Warnings are treated as
|
||||
errors.
|
||||
|
||||
Alternatively, you can directly check a target using
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ bazel build //my/haskell:target \
|
||||
--aspects @io_tweag_rules_haskell//haskell:haskell.bzl%haskell_lint_aspect
|
||||
|
||||
.. _haskell_lint: http://api.haskell.build/haskell/lint.html#haskell_lint
|
||||
|
||||
Checking code coverage
|
||||
----------------------
|
||||
|
||||
"Code coverage" is the name given to metrics that describe how much source
|
||||
code is covered by a given test suite. A specific code coverage metric
|
||||
implemented here is expression coverage, or the number of expressions in
|
||||
the source code that are explored when the tests are run.
|
||||
|
||||
Haskell's ``ghc`` compiler has built-in support for code coverage analysis,
|
||||
through the hpc_ tool. The Haskell rules allow the use of this tool to analyse
|
||||
``haskell_library`` coverage by ``haskell_test`` rules. To do so, you have a
|
||||
few options. You can add
|
||||
``expected_covered_expressions_percentage=<some integer between 0 and 100>`` to
|
||||
the attributes of a ``haskell_test``, and if the expression coverage percentage
|
||||
is lower than this amount, the test will fail. Alternatively, you can add
|
||||
``expected_uncovered_expression_count=<some integer greater or equal to 0>`` to
|
||||
the attributes of a ``haskell_test``, and instead the test will fail if the
|
||||
number of uncovered expressions is greater than this amount. Finally, you could
|
||||
do both at once, and have both of these checks analyzed by the coverage runner.
|
||||
To see the coverage details of the test suite regardless of if the test passes
|
||||
or fails, add ``--test_output=all`` as a flag when invoking the test, and there
|
||||
will be a report in the test output. You will only see the report if you
|
||||
required a certain level of expression coverage in the rule attributes.
|
||||
|
||||
For example, your BUILD file might look like this: ::
|
||||
|
||||
haskell_library(
|
||||
name = "lib",
|
||||
srcs = ["Lib.hs"],
|
||||
deps = [
|
||||
"//tests/hackage:base",
|
||||
],
|
||||
)
|
||||
|
||||
haskell_test(
|
||||
name = "test",
|
||||
srcs = ["Main.hs"],
|
||||
deps = [
|
||||
":lib",
|
||||
"//tests/hackage:base",
|
||||
],
|
||||
expected_covered_expressions_percentage = 80,
|
||||
expected_uncovered_expression_count = 10,
|
||||
)
|
||||
|
||||
And if you ran ``bazel coverage //somepackage:test --test_output=all``, you
|
||||
might see a result like this: ::
|
||||
|
||||
INFO: From Testing //somepackage:test:
|
||||
==================== Test output for //somepackage:test:
|
||||
Overall report
|
||||
100% expressions used (9/9)
|
||||
100% boolean coverage (0/0)
|
||||
100% guards (0/0)
|
||||
100% 'if' conditions (0/0)
|
||||
100% qualifiers (0/0)
|
||||
100% alternatives used (0/0)
|
||||
100% local declarations used (0/0)
|
||||
100% top-level declarations used (3/3)
|
||||
=============================================================================
|
||||
|
||||
Here, the test passes because it actually has 100% expression coverage and 0
|
||||
uncovered expressions, which is even better than we expected on both counts.
|
||||
|
||||
There is an optional ``haskell_test`` attribute called
|
||||
``strict_coverage_analysis``, which is a boolean that changes the coverage
|
||||
analysis such that even having better coverage than expected fails the test.
|
||||
This can be used to enforce that developers must upgrade the expected test
|
||||
coverage when they improve it. On the other hand, it requires changing the
|
||||
expected coverage for almost any change.
|
||||
|
||||
There a couple of notes regarding the coverage analysis functionality:
|
||||
|
||||
- Coverage analysis currently is scoped to all source files and all
|
||||
locally-built Haskell dependencies (both direct and transitive) for a given
|
||||
test rule.
|
||||
- Coverage-enabled build and execution for ``haskell_test`` targets may take
|
||||
longer than regular. However, this has not effected regular ``run`` /
|
||||
``build`` / ``test`` performance.
|
||||
|
||||
.. _hpc: <http://hackage.haskell.org/package/hpc>
|
364
third_party/bazel/rules_haskell/docs/haskell.rst
vendored
Normal file
364
third_party/bazel/rules_haskell/docs/haskell.rst
vendored
Normal file
|
@ -0,0 +1,364 @@
|
|||
.. _guide:
|
||||
|
||||
Introduction to Bazel: Building a Haskell project
|
||||
=================================================
|
||||
|
||||
In this tutorial, you'll learn the basics of building Haskell
|
||||
applications with Bazel. You will set up your workspace and build
|
||||
a simple Haskell project that illustrates key Bazel concepts, such as
|
||||
targets and ``BUILD.bazel`` files. After completing this tutorial, take
|
||||
a look at :ref:`Common Haskell build use cases <use-cases>` for
|
||||
information on more advanced concepts such as writing and running
|
||||
Haskell tests.
|
||||
|
||||
What you'll learn
|
||||
-----------------
|
||||
|
||||
In this tutorial you'll learn how to:
|
||||
|
||||
* build a target,
|
||||
* visualize the project's dependencies,
|
||||
* split the project into multiple targets and packages,
|
||||
* control target visibility across packages,
|
||||
* reference targets through labels.
|
||||
|
||||
Before you begin
|
||||
----------------
|
||||
|
||||
To prepare for the tutorial, first `install Bazel`_ if you don't have
|
||||
it installed already. Then, retrieve the ``rules_haskell`` GitHub
|
||||
repository::
|
||||
|
||||
git clone https://github.com/tweag/rules_haskell/
|
||||
|
||||
The sample project for this tutorial is in the ``tutorial``
|
||||
directory and is structured as follows::
|
||||
|
||||
rules_haskell
|
||||
└── tutorial
|
||||
├── WORKSPACE
|
||||
├── main
|
||||
│ ├── BUILD.bazel
|
||||
│ └── Main.hs
|
||||
└── lib
|
||||
├── BUILD.bazel
|
||||
└── Bool.hs
|
||||
|
||||
The first thing to do is to::
|
||||
|
||||
$ cd tutorial
|
||||
|
||||
Build with Bazel
|
||||
----------------
|
||||
|
||||
Set up the workspace
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Before you can build a project, you need to set up its workspace.
|
||||
A workspace is a directory that holds your project's source files and
|
||||
Bazel's build outputs. It also contains files that Bazel recognizes as
|
||||
special:
|
||||
|
||||
* the ``WORKSPACE`` file, which identifies the directory and its
|
||||
contents as a Bazel workspace and lives at the root of the project's
|
||||
directory structure,
|
||||
|
||||
* one or more ``BUILD.bazel`` files, which tell Bazel how to build different
|
||||
parts of the project. (A directory within the workspace that
|
||||
contains a ``BUILD.bazel`` file is a *package*. You will learn about
|
||||
packages later in this tutorial.)
|
||||
|
||||
To designate a directory as a Bazel workspace, create an empty file
|
||||
named ``WORKSPACE`` in that directory.
|
||||
|
||||
When Bazel builds the project, all inputs and dependencies must be in
|
||||
the same workspace. Files residing in different workspaces are
|
||||
independent of one another unless linked, which is beyond the scope of
|
||||
this tutorial.
|
||||
|
||||
Understand the BUILD file
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
It is recommended to use a ``.bazel`` extension for each ``BUILD`` file to
|
||||
avoid clashing with files or folders already using that name.
|
||||
|
||||
A ``BUILD.bazel`` file contains several different types of instructions for
|
||||
Bazel. The most important type is the *build rule*, which tells Bazel
|
||||
how to build the desired outputs, such as executable binaries or
|
||||
libraries. Each instance of a build rule in the ``BUILD.bazel`` file is
|
||||
called a *target* and points to a specific set of source files and
|
||||
dependencies. A target can also point to other targets.
|
||||
|
||||
Take a look at the ``BUILD.bazel`` file in the ``tutorial/lib`` directory::
|
||||
|
||||
haskell_library(
|
||||
name = "booleans",
|
||||
srcs = ["Bool.hs"],
|
||||
)
|
||||
|
||||
In our example, the ``booleans`` target instantiates the
|
||||
`haskell_library`_ rule. The rule tells Bazel to build a reusable
|
||||
(statically or dynamically linked) library from the ``Bool.hs`` source
|
||||
file with no dependencies.
|
||||
|
||||
The attributes in the target explicitly state its dependencies and
|
||||
options. While the ``name`` attribute is mandatory, many are optional.
|
||||
For example, in the ``booleans`` target, ``name`` is self-explanatory,
|
||||
and ``srcs`` specifies the source file(s) from which Bazel builds the
|
||||
target.
|
||||
|
||||
Build the project
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
Let's build your sample project. Run the following command::
|
||||
|
||||
$ bazel build //lib:booleans
|
||||
|
||||
Notice the target label - the ``//lib:`` part is the location of our
|
||||
``BUILD.bazel`` file relative to the root of the workspace, and ``booleans``
|
||||
is what we named that target in the ``BUILD.bazel`` file. (You will learn
|
||||
about target labels in more detail at the end of this tutorial.)
|
||||
|
||||
Bazel produces output similar to the following::
|
||||
|
||||
INFO: Found 1 target...
|
||||
Target //lib:booleans up-to-date:
|
||||
bazel-bin/lib/libZSbooleans/libZSbooleans.conf
|
||||
bazel-bin/lib/libZSbooleans/package.cache
|
||||
INFO: Elapsed time: 2.288s, Critical Path: 0.68s
|
||||
|
||||
Congratulations, you just built your first Bazel target! Bazel places
|
||||
build outputs in the ``bazel-bin`` directory at the root of the
|
||||
workspace. Browse through its contents to get an idea for Bazel's
|
||||
output structure.
|
||||
|
||||
Review the dependency graph
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A successful build has all of its dependencies explicitly stated in
|
||||
the ``BUILD.bazel`` file. Bazel uses those statements to create the
|
||||
project's dependency graph, which enables accurate incremental builds.
|
||||
|
||||
Let's visualize our sample project's dependencies. First, generate
|
||||
a text representation of the dependency graph (run the command at the
|
||||
workspace root)::
|
||||
|
||||
bazel query --nohost_deps --noimplicit_deps \
|
||||
'deps(//lib:booleans)' --output graph
|
||||
|
||||
The above command tells Bazel to look for all dependencies for the
|
||||
target ``//lib:booleans`` (excluding host and implicit dependencies)
|
||||
and format the output as a graph.
|
||||
|
||||
Then, paste the text into GraphViz_.
|
||||
|
||||
On Ubuntu, you can view the graph locally by installing GraphViz and the xdot
|
||||
Dot Viewer::
|
||||
|
||||
sudo apt update && sudo apt install graphviz xdot
|
||||
|
||||
Then you can generate and view the graph by piping the text output above
|
||||
straight to xdot::
|
||||
|
||||
xdot <(bazel query --nohost_deps --noimplicit_deps \
|
||||
'deps(//lib:booleans)' --output graph)
|
||||
|
||||
As you can see, the first stage of the sample project has a single
|
||||
target that builds a single source file with no additional
|
||||
dependencies:
|
||||
|
||||
.. digraph:: booleans
|
||||
|
||||
node [shape=box];
|
||||
"//lib:booleans"
|
||||
"//lib:booleans" -> "//lib:Bool.hs"
|
||||
"//lib:Bool.hs"
|
||||
|
||||
Now that you have set up your workspace, built your project, and
|
||||
examined its dependencies, let's add some complexity.
|
||||
|
||||
Refine your Bazel build
|
||||
-----------------------
|
||||
|
||||
While a single target is sufficient for small projects, you may want
|
||||
to split larger projects into multiple targets and packages to allow
|
||||
for fast incremental builds (that is, only rebuild what's changed) and
|
||||
to speed up your builds by building multiple parts of a project at
|
||||
once.
|
||||
|
||||
Specify multiple build targets
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Let's split our sample project build into two targets. Take a look at
|
||||
the ``BUILD.bazel`` files in the ``tutorial/lib`` and ``tutorial/main``
|
||||
directories. The contents of both files could have been kept in
|
||||
a single ``BUILD.bazel`` as follows::
|
||||
|
||||
haskell_library(
|
||||
name = "booleans",
|
||||
srcs = ["Bool.hs"],
|
||||
)
|
||||
|
||||
haskell_toolchain_library(name = "base")
|
||||
|
||||
haskell_binary(
|
||||
name = "demorgan",
|
||||
srcs = ["Main.hs"],
|
||||
compiler_flags = ["-threaded"],
|
||||
deps = [":base", ":booleans"],
|
||||
)
|
||||
|
||||
With this single ``BUILD.bazel`` file, Bazel first builds the ``booleans``
|
||||
library (using the `haskell_library`_ rule), then the ``demorgan``
|
||||
binary (which as an example uses the ``booleans`` library to check one
|
||||
of the De Morgan laws). The ``deps`` attribute in the ``demorgan``
|
||||
target tells Bazel that the ``:booleans`` library is required to build
|
||||
the ``demorgan`` binary. The binary also requires the ``base``
|
||||
built-in library that ships with GHC, to perform I/O among other
|
||||
things. Libraries like ``base``, ``bytestring`` and others that ship
|
||||
with GHC are special in that they are prebuilt outside of Bazel. To
|
||||
import them as regular targets, we use the `haskell_toolchain_library`_ rule.
|
||||
|
||||
Let's build this new version of our project::
|
||||
|
||||
$ bazel build //main:demorgan
|
||||
|
||||
Bazel produces output similar to the following::
|
||||
|
||||
INFO: Found 1 target...
|
||||
Target //main:demorgan up-to-date:
|
||||
bazel-bin/main/demorgan
|
||||
INFO: Elapsed time: 2.728s, Critical Path: 1.23s
|
||||
|
||||
Now test your freshly built binary::
|
||||
|
||||
$ bazel-bin/main/demorgan
|
||||
|
||||
Or alternatively::
|
||||
|
||||
$ bazel run //main:demorgan
|
||||
|
||||
If you now modify ``Bool.hs`` and rebuild the project, Bazel will
|
||||
usually only recompile that file.
|
||||
|
||||
Looking at the dependency graph:
|
||||
|
||||
.. digraph:: demorgan
|
||||
|
||||
node [shape=box];
|
||||
"//main:demorgan"
|
||||
"//main:demorgan" -> "//main:base\n//main:Main.hs"
|
||||
"//main:demorgan" -> "//lib:booleans"
|
||||
"//lib:booleans"
|
||||
"//lib:booleans" -> "//lib:Bool.hs"
|
||||
"//lib:Bool.hs"
|
||||
"//main:base\n//main:Main.hs"
|
||||
|
||||
You have now built the project with two targets. The ``demorgan``
|
||||
target builds one source file and depends on one other target
|
||||
(``//lib:booleans``), which builds one additional source file.
|
||||
|
||||
Use multiple packages
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Let’s now split the project into multiple packages.
|
||||
|
||||
Notice that we actually have two sub-directories, and each contains
|
||||
a ``BUILD.bazel`` file. Therefore, to Bazel, the workspace contains two
|
||||
packages, ``lib`` and ``main``.
|
||||
|
||||
Take a look at the ``lib/BUILD.bazel`` file::
|
||||
|
||||
haskell_library(
|
||||
name = "booleans",
|
||||
srcs = ["Bool.hs"],
|
||||
visibility = ["//main:__pkg__"],
|
||||
)
|
||||
|
||||
And at the ``main/BUILD.bazel`` file::
|
||||
|
||||
haskell_toolchain_library(name = "base")
|
||||
|
||||
haskell_binary(
|
||||
name = "demorgan",
|
||||
srcs = ["Main.hs"],
|
||||
compiler_flags = ["-threaded"],
|
||||
deps = [":base", "//lib:booleans"],
|
||||
)
|
||||
|
||||
As you can see, the ``demorgan`` target in the ``main`` package
|
||||
depends on the ``booleans`` target in the ``lib`` package (hence the
|
||||
target label ``//lib:booleans``) - Bazel knows this through the
|
||||
``deps`` attribute.
|
||||
|
||||
Notice that for the build to succeed, we make the ``//lib:booleans``
|
||||
target in ``lib/BUILD.bazel`` explicitly visible to targets in
|
||||
``main/BUILD.bazel`` using the ``visibility`` attribute. This is because by
|
||||
default targets are only visible to other targets in the same
|
||||
``BUILD.bazel`` file. (Bazel uses target visibility to prevent issues such
|
||||
as libraries containing implementation details leaking into public
|
||||
APIs.)
|
||||
|
||||
You have built the project as two packages with three targets and
|
||||
understand the dependencies between them.
|
||||
|
||||
Use labels to reference targets
|
||||
-------------------------------
|
||||
|
||||
In ``BUILD.bazel`` files and at the command line, Bazel uses *labels* to
|
||||
reference targets - for example, ``//main:demorgan`` or
|
||||
``//lib:booleans``. Their syntax is::
|
||||
|
||||
//path/to/package:target-name
|
||||
|
||||
If the target is a rule target, then ``path/to/package`` is the path
|
||||
to the directory containing the ``BUILD.bazel`` file, and ``target-name`` is
|
||||
what you named the target in the ``BUILD.bazel`` file (the ``name``
|
||||
attribute). If the target is a file target, then ``path/to/package``
|
||||
is the path to the root of the package, and ``target-name`` is the
|
||||
name of the target file, including its full path.
|
||||
|
||||
When referencing targets within the same package, you can skip the
|
||||
package path and just use ``//:target-name``. When referencing targets
|
||||
within the same ``BUILD.bazel`` file, you can even skip the ``//`` workspace
|
||||
root identifier and just use ``:target-name``.
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
Congratulations! You now know the basics of building a Haskell project
|
||||
with Bazel. Next, read up on the most common :ref:`Common Haskell
|
||||
build use cases <use-cases>`. Then, check out the following:
|
||||
|
||||
* `External Dependencies`_ to learn more about working with local and
|
||||
remote repositories.
|
||||
|
||||
* The `Build Encyclopedia`_ to learn more about Bazel.
|
||||
|
||||
* The `C++ build tutorial`_ to get started with building C++
|
||||
applications with Bazel.
|
||||
|
||||
* The `Java build tutorial`_ to get started with building Java
|
||||
applications with Bazel.
|
||||
|
||||
* The `Android application tutorial`_ to get started with building
|
||||
mobile applications for Android with Bazel.
|
||||
|
||||
* The `iOS application tutorial`_ to get started with building mobile
|
||||
applications for iOS with Bazel.
|
||||
|
||||
Happy building!
|
||||
|
||||
.. note:: This tutorial is adapted from the Bazel `C++ build tutorial`_.
|
||||
|
||||
.. _install Bazel: https://docs.bazel.build/versions/master/install.html
|
||||
.. _haskell_binary: http://api.haskell.build/haskell/haskell.html#haskell_binary
|
||||
.. _haskell_toolchain_library: http://api.haskell.build/haskell/haskell.html#haskell_toolchain_library
|
||||
.. _haskell_library: http://api.haskell.build/haskell/haskell.html#haskell_library
|
||||
.. _graphviz: https://www.graphviz.org/
|
||||
.. _external dependencies: https://docs.bazel.build/versions/master/external.html
|
||||
.. _build encyclopedia: https://docs.bazel.build/versions/master/be/overview.html
|
||||
.. _C++ build tutorial: https://docs.bazel.build/versions/master/tutorial/cpp.html
|
||||
.. _Java build tutorial: https://docs.bazel.build/versions/master/tutorial/java.html
|
||||
.. _Android application tutorial: https://docs.bazel.build/versions/master/tutorial/android-app.html
|
||||
.. _iOS application tutorial: https://docs.bazel.build/versions/master/tutorial/ios-app.html
|
23
third_party/bazel/rules_haskell/docs/index.rst
vendored
Normal file
23
third_party/bazel/rules_haskell/docs/index.rst
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
.. meta::
|
||||
:description: User guide for building Haskell code with Bazel.
|
||||
|
||||
Build Haskell Using Bazel
|
||||
=========================
|
||||
|
||||
Bazel_ is a tool for automating the *building* and the *testing* of
|
||||
software. Follow :ref:`this guide <guide>` to get started building
|
||||
small Haskell projects using Bazel. For a deeper dive and solutions to
|
||||
more advanced use cases, see :ref:`Common Haskell Build Use Cases
|
||||
<use-cases>`. Refer to the `Bazel documentation`_ for more about
|
||||
Bazel.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
why-bazel
|
||||
haskell
|
||||
haskell-use-cases
|
||||
|
||||
.. _Bazel: https://bazel.build
|
||||
.. _Bazel documentation: https://docs.bazel.build/versions/master/getting-started.html
|
102
third_party/bazel/rules_haskell/docs/why-bazel.rst
vendored
Normal file
102
third_party/bazel/rules_haskell/docs/why-bazel.rst
vendored
Normal file
|
@ -0,0 +1,102 @@
|
|||
.. _why-bazel:
|
||||
|
||||
Is Bazel right for me?
|
||||
======================
|
||||
|
||||
Nearly as many build tools exist as there are programming languages
|
||||
out there. C++ has Autotools_/Make_, CMake_ and many others. Java has
|
||||
Ant_, Maven_, Gradle_ and several more. Haskell has Cabal_, Stack_,
|
||||
Shake_ and several more. Each of these originated in a given language
|
||||
community but are in some cases generic enough to support building any
|
||||
language. Are any of them the right choice for your use case? Should
|
||||
you be combining several systems? That's what this document should
|
||||
help you answer.
|
||||
|
||||
Rule of thumb
|
||||
-------------
|
||||
|
||||
If a combination of the following apply, then you're better off using
|
||||
Cabal_ or Stack_:
|
||||
|
||||
* your project is an independently publishable single library, or
|
||||
small set of libraries;
|
||||
* your project is open source code and has at most small static
|
||||
assets (hence publishable on Hackage);
|
||||
* your project is nearly entirely Haskell code with perhaps a little
|
||||
bit of C;
|
||||
* your project has many dependencies on other packages found on
|
||||
Hackage but few if any system dependencies (like zlib, libpng etc);
|
||||
|
||||
Bazel works well for the following use cases:
|
||||
|
||||
* projects that cannot be hosted on Hackage (games with large static
|
||||
assets, proprietary code etc);
|
||||
* projects with a very large amount of code hosted in a single
|
||||
repository;
|
||||
* projects in which you or your team are writing code in two or more
|
||||
languages (e.g. Haskell/PureScript, or Haskell/Java, or
|
||||
Haskell/C++/FORTRAN);
|
||||
|
||||
Rationale
|
||||
---------
|
||||
|
||||
For all the benefits it can bring, Bazel also has an upfront cost.
|
||||
Don't pay that cost if the benefits don't justify it.
|
||||
|
||||
If you don't have much code to build, any build tool will do. Build
|
||||
issues like lack of complete reproducibility are comparatively easier
|
||||
to debug, and working around build system bugs by wiping the entire
|
||||
build cache first is entirely viable in this particular case. So might
|
||||
as well use low-powered Haskell-native build tools that ship with GHC.
|
||||
You won't *need* sandboxed build actions to guarantee build system
|
||||
correctness, completely hermetic builds for good reproducibility,
|
||||
build caching, test result caching or distributed builds for faster
|
||||
build and test times. Those features start to matter for larger
|
||||
projects, and become essential for very large monorepos_.
|
||||
|
||||
Why exactly do these features matter?
|
||||
|
||||
* **Hermetic builds** are builds that do not take any part of the
|
||||
host's system configuration (set of installed system libraries and
|
||||
their versions, content of ``/etc``, OS version, etc) as an input.
|
||||
If all build actions are deterministic, hermeticity guarantees that
|
||||
builds are reproducible anywhere, anytime. More developers on
|
||||
a project means more subtly different system configurations to cope
|
||||
with. The more system configurations, the more likely that the build
|
||||
will fail in one of these configurations but not in others... Unless
|
||||
the build is completely hermetic.
|
||||
* **Sandboxing build actions** guarantees that all inputs to all build
|
||||
actions are properly declared. This helps prevent build system
|
||||
correctness bugs, which are surprisingly and exceedingly common in
|
||||
most non-sandboxing build systems, especially as the build system
|
||||
becomes more complex. When a build system *might* be incorrect,
|
||||
users regularly have to wipe the entire build cache to work around
|
||||
issues. As the codebase becomes very large, rebuilding from scratch
|
||||
can cost a lot of CPU time.
|
||||
* **Distributed build caches** make building the code from a fresh
|
||||
checkout trivially fast. Continuous integration populates the build
|
||||
cache at every branch push, so that building all artifacts from
|
||||
fresh checkouts seldom needs to actually build anything at all
|
||||
locally. In the common case, builds become network-bound instead of
|
||||
CPU-bound.
|
||||
* **Distributed build action execution** mean that average build times
|
||||
can stay constant even as the codebase grows, because you can
|
||||
seamlessly distribute the build on more machines.
|
||||
* **Test result caching** is the key to keeping continuous
|
||||
integration times very low. Only those tests that depend on code
|
||||
that was modified need be rerun.
|
||||
|
||||
On their own hermetic and sandboxed builds can already save quite
|
||||
a few headaches. But crucially, without them one can't even hope to
|
||||
have any of the other features that follow them above.
|
||||
|
||||
.. _Autotools: https://en.wikipedia.org/wiki/GNU_Build_System
|
||||
.. _Make: https://en.wikipedia.org/wiki/Make_(software)
|
||||
.. _CMake: https://cmake.org/
|
||||
.. _Ant: https://ant.apache.org/
|
||||
.. _Maven: https://maven.apache.org/index.html
|
||||
.. _Gradle: https://gradle.org/
|
||||
.. _Cabal: https://www.haskell.org/cabal/
|
||||
.. _Stack: http://haskellstack.org/
|
||||
.. _Shake: https://shakebuild.com/
|
||||
.. _monorepos: https://en.wikipedia.org/wiki/Monorepo
|
Loading…
Add table
Add a link
Reference in a new issue