Skip to content

Latest commit

 

History

History
73 lines (46 loc) · 5.37 KB

build.md

File metadata and controls

73 lines (46 loc) · 5.37 KB

Bazel build instructions

The Semantic project supports builds with the Bazel build system. This is unconventional—most Haskell projects either use Cabal or Stack. However, for a project of Semantic's size, Bazel has many advantages. Some reasons you might want to use Bazel:

  • Bazel uses content-addressed hashing and reproducible builds to provide sophisticated caching. Situations where Cabal invalidates caches can result in cascading build requirements, causing many rebuilds of the language syntax packages.
  • Bazel's tooling is (on Emacs with lsp-mode and lsp-haskell) more reliable.
  • Bazel gets Haskell dependencies from Stackage LTS versions, so we avoid the rebuilds associated with living on the latest Hackage snapshot.

How do I get started?

Assuming you're on macOS, run the script located at script/bootstrap-bazel. This uses Homebrew to install Bazel and creates the .bazel-cache directory.

The first time you run bazel build, it'll take some time, as Bazel will compile all of Stackage. Fear not: you won't have to do this again.

cabalbazel cheatsheet

Operation cabal bazel
Build all cabal build all bazel build //...
Build TARGET library cabal build TARGET:lib bazel build //TARGET
Build semantic executable cabal build semantic:exe:semantic bazel build //semantic:exe
Build/run executable cabal run semantic -- ARGS bazel run //semantic:exe -- ARGS
Load REPL component script/ghci and :load bazel build //TARGET@repl
Run tests cabal test all bazel test //...
Build with optimizations cabal build --flags="+release" bazel build -c opt //...
Run all languages' AST tests ETOOLONGTOWRITE bazel test --test_tag_filters=language-test //...

Adding a new dependency

Here's a breakdown of how to add a new package.

  1. Make sure it's present in Stackage LTS 13.15. If not, add the package (versioned exactly) to the stack-snapshot.yaml file.
  2. Make sure it's linked into the WORKSPACE file, in the stack_snapshot call.
  3. Make sure it's present in your target's deps field.

If this seems complicated, don't worry: most of the time you'll be able to skip this first point, and you'll often be able to skip the second.

Things to know

  1. Don't generally run bazel clean. Since Bazel builds are reproducible, there's very little reason to clean, unless somehow your whole cache got irrevocably corrupted.
  2. You can load a REPL for any target by appending @repl. (with the exception of the language packages, due to this).
  3. Some packages come with GHC and are not loaded from Stackage. These include base, containers, and others. To depend on those packages, you use //:base, //:containers, etc. They are specified in the BAZEL.build at the project root. You probably won't need to add any more.
  4. Getting weird errors from the C compiler? Try setting export BAZEL_USE_CPP_ONLY_TOOLCHAIN=1 in your .profile or whatnot.

Quick reference links

Conventions

We give library targets the same name as their subproject. Test targets are called test, and executable targets are exe.

The default .bazelrc file imports a .bazelrc.local file if it's present; use that for any Bazel customizations you want.

The variables that the scripts under build/ export are SCREAMING_SNAKE_CASE. The functions are snake_case.

Shared variables

  • GHC_FLAGS: the standard set of Cabal flags that all targets should use.
  • EXECUTABLE_FLAGS: ditto, but with executable-specific flags.

Custom rules

We have two common custom rules, defined in build/common.bzl. The first, tree_sitter_node_types_release, uses the http_archive rule to download a specified tree-sitter grammar's node-types.json file. These calls declare new top-level targets, so they're only present in the top-level WORKSPACE file. The second, semantic_language_library, takes care of the boilerplate associated with declaring a target for a semantic-LANG language package (as these packages' contents are identical, their target declarations are almost identical).

For the purposes of setting up the examples upon which the parse-examples test depends, we have code in build/example_repos.bzl which defines them, checks them out, and computes the set of target names. You shouldn't need to change or modify this, unless you're adding new repos.

Protips

  • bazel build --output_filter=REGEXP does what it says on the tin.