diff options
author | Vincent Ambo <tazjin@google.com> | 2019-07-04T10·18+0100 |
---|---|---|
committer | Vincent Ambo <tazjin@google.com> | 2019-07-04T10·18+0100 |
commit | f723b8b878a3c4a4687b9e337a875500bebb39b1 (patch) | |
tree | e85204cf042c355e90cff61c111e7d8cd15df311 /third_party/bazel/rules_haskell/docs | |
parent | 2eb1dc26e42ffbdc168f05ef744bd4b4f3e4c36f (diff) |
feat(third_party/bazel): Check in rules_haskell from Tweag r/17
Diffstat (limited to 'third_party/bazel/rules_haskell/docs')
-rw-r--r-- | third_party/bazel/rules_haskell/docs/.gitignore | 1 | ||||
-rw-r--r-- | third_party/bazel/rules_haskell/docs/BUILD.bazel | 46 | ||||
-rw-r--r-- | third_party/bazel/rules_haskell/docs/conf.py | 41 | ||||
-rw-r--r-- | third_party/bazel/rules_haskell/docs/haskell-use-cases.rst | 283 | ||||
-rw-r--r-- | third_party/bazel/rules_haskell/docs/haskell.rst | 364 | ||||
-rw-r--r-- | third_party/bazel/rules_haskell/docs/index.rst | 23 | ||||
-rw-r--r-- | third_party/bazel/rules_haskell/docs/why-bazel.rst | 102 |
7 files changed, 860 insertions, 0 deletions
diff --git a/third_party/bazel/rules_haskell/docs/.gitignore b/third_party/bazel/rules_haskell/docs/.gitignore new file mode 100644 index 000000000000..e35d8850c968 --- /dev/null +++ b/third_party/bazel/rules_haskell/docs/.gitignore @@ -0,0 +1 @@ +_build diff --git a/third_party/bazel/rules_haskell/docs/BUILD.bazel b/third_party/bazel/rules_haskell/docs/BUILD.bazel new file mode 100644 index 000000000000..e21093c8ec6c --- /dev/null +++ b/third_party/bazel/rules_haskell/docs/BUILD.bazel @@ -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", +) diff --git a/third_party/bazel/rules_haskell/docs/conf.py b/third_party/bazel/rules_haskell/docs/conf.py new file mode 100644 index 000000000000..acfe5dc7af88 --- /dev/null +++ b/third_party/bazel/rules_haskell/docs/conf.py @@ -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'), +] diff --git a/third_party/bazel/rules_haskell/docs/haskell-use-cases.rst b/third_party/bazel/rules_haskell/docs/haskell-use-cases.rst new file mode 100644 index 000000000000..a8c4340cf70f --- /dev/null +++ b/third_party/bazel/rules_haskell/docs/haskell-use-cases.rst @@ -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> diff --git a/third_party/bazel/rules_haskell/docs/haskell.rst b/third_party/bazel/rules_haskell/docs/haskell.rst new file mode 100644 index 000000000000..353561111a21 --- /dev/null +++ b/third_party/bazel/rules_haskell/docs/haskell.rst @@ -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 diff --git a/third_party/bazel/rules_haskell/docs/index.rst b/third_party/bazel/rules_haskell/docs/index.rst new file mode 100644 index 000000000000..f9292871ef55 --- /dev/null +++ b/third_party/bazel/rules_haskell/docs/index.rst @@ -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 diff --git a/third_party/bazel/rules_haskell/docs/why-bazel.rst b/third_party/bazel/rules_haskell/docs/why-bazel.rst new file mode 100644 index 000000000000..2ad4bc598be3 --- /dev/null +++ b/third_party/bazel/rules_haskell/docs/why-bazel.rst @@ -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 |