PureScript 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 PureScript.
Add the following to your WORKSPACE
and select a $VERSION
(e.g. tag name or
commit hash) appropriately:
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "com_habito_rules_purescript",
strip_prefix = "rules_purescript-$VERSION",
urls = ["https://github.com/heyhabito/rules_purescript/archive/$VERSION.tar.gz"],
)
load(
"@com_habito_rules_purescript//purescript:repositories.bzl",
"purescript_repositories",
)
purescript_repositories()
The purescript_toolchain
rule allows you to define toolchains for compiling
PureScript code. Currently build tools and executables can be provided by:
- Nix
This is the technique rules_purescript
itself uses, so see this repository for
a working example of using Nix. First the following to your WORKSPACE
to load
rules for working with nixpkgs
(again, $VERSION
is the version of
rules_nixpkgs
you wish to use, and could be e.g. a tag or a commit hash):
http_archive(
name = "io_tweag_rules_nixpkgs",
strip_prefix = "rules_nixpkgs-$VERSION",
urls = ["https://github.com/tweag/rules_nixpkgs/archive/$VERSION.tar.gz"],
)
load(
"@io_tweag_rules_nixpkgs//nixpkgs:nixpkgs.bzl",
"nixpkgs_local_repository",
"nixpkgs_package",
)
rules_nixpkgs
provides a number of ways for working with nixpkgs
, but we'll
use nixpkgs_local_repository
, which allows us to point to a .nix
file in our
repository that exposes a nixpkgs
expression (here a pinned version of
nixpkgs
):
WORKSPACE
:
nixpkgs_local_repository(
name = "nixpkgs",
nix_file = "//nix:nixpkgs.nix",
)
nix/nixpkgs.nix
($SHA
is the hash of the nixpkgs
version we wish to pin):
import (fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/$SHA.tar.gz";
})
Now we can use the nixpkgs_package
rule to pull the packages needed to form a
purescript_toolchain
-- currently these are just PureScript and GNU Tar:
WORKSPACE
:
nixpkgs_package(
name = "nixpkgs_purescript",
repositories = {"nixpkgs": "@nixpkgs//:nixpkgs.nix"},
attribute_path = "purescript",
)
nixpkgs_package(
name = "nixpkgs_tar",
repositories = {"nixpkgs": "@nixpkgs//:nixpkgs.nix"},
attribute_path = "gnutar",
)
Now we can declare the toolchain in a BUILD.bazel
:
load(
"@com_habito_rules_purescript//purescript:purescript.bzl",
"purescript_toolchain",
)
purescript_toolchain(
name = "purescript",
version = "0.12.1",
tools = [
"@nixpkgs_purescript//:bin",
"@nixpkgs_tar//:bin",
],
)
and register it in the WORKSPACE
using Bazel's register_toolchains
:
register_toolchains("//:purescript")
rules_purescript
supports generating Bazel definitions for a modified
version of PureScript packagesets (as specified by psc-package),
with a sha256
field added to each package.
In order for Nix to verify the downloaded package is correct it needs a hash
for each package. This hash is checked against nix-hash <directory> --type sha256 --base32
where directory is a checkout of the
repoat the specified
version(with submodules) with the
.git` directory removed.
Psc-Prefetch is a tool that can enrich a given package set with
the necessary hashes.
The purescript_nixpkgs_packageset
rule can be used to reify a Nix expression
describing a PureScript packageset:
WORKSPACE
:
load(
"@com_habito_rules_purescript//purescript:nixpkgs.bzl",
"purescript_nixpkgs_packageset",
)
purescript_nixpkgs_packageset(
name = "psc-package",
nix_file = "//nix:purescript-packages.nix",
base_attribute_path = "purescriptPackages",
repositories = {"nixpkgs": "@nixpkgs//:nixpkgs.nix"},
)
load(
"@psc-package-imports//:packages.bzl",
"purescript_import_packages",
)
purescript_import_packages(
base_attribute_path = "purescriptPackages",
)
The nix_file
argument specifies a .nix
file in the repository providing an
expression representing a function from a Bazel context to a Nix set with a
PureScript packageset (with hashes) in the attribute named by base_attribute_path
:
nix/purescript-packages.nix
:
{ ctx }:
with import <nixpkgs> {};
let
genBazelBuild =
callPackage <bazel_purescript_wrapper> { ctx = ctx; };
packagesJSON =
builtins.fromJSON (builtins.readFile (builtins.fetchurl {
url = "https://raw.githubusercontent.com/heyhabito/package-sets/master/packages-with-sha256.json";
sha256 = "0km8pnvn5wlprwc18bw9vng47dang1hp8x24k73njc50l3fi6rhh";
}));
in {
purescriptPackages = genBazelBuild packagesJSON;
}
In this file, the <bazel_purescript_wrapper>
repository will be replaced by a
function capable of transforming a packages.json
into a set of Nix derivations
representing those PureScript packages. It must be passed the Bazel context as
shown (here { ctx = ctx; }
).
The purescript_nixpkgs_packageset
rule generates an external repository named
<name>-imports
from which a Bazel-compatible set of rules for the packageset
can be imported and executed. Once executed, the external repository <name>
can be used to reference packages in the set, e.g. given the name = "psc-package"
argument above, we could use:
purescript_library(
...,
deps = [
"@psc-package//:prelude",
],
...,
)
load(
"@com_habito_rules_purescript//purescript:purescript.bzl",
"purescript_library",
)
purescript_library(
name = "library-name",
src_strip_prefix = "src",
srcs = [
"src/Library/Module.purs",
"src/Library/AnotherModule.purs",
],
foreign_srcs = [
"src/Library/Module.js",
],
deps = [
"//path/to:library-dependency",
"@psc-package//:prelude",
],
)
load(
"@com_habito_rules_purescript//purescript:purescript.bzl",
"purescript_bundle",
)
purescript_bundle(
name = "bundle-name",
entry_point_module = "Main",
main_module = "Main",
src_strip_prefix = "src",
srcs = [
"src/Main.purs",
],
foreign_srcs = [
"src/Main.js",
],
deps = [
"//path/to:bundle-dependency",
"@psc-package//:prelude",
],
)
Many rules generate targets for running REPLs. For example, the invocation:
purescript_library(
name = "library-name",
...,
)
will generate a target library-name@repl
, which can be run to load a REPL
targeting that library:
$ bazel run //:library-name@repl
Note that to run psci
, you'll need to pass your purescript_toolchain
a
reference to a copy of the psci-support
package's source (.purs
) files,
which are needed to boot psci
.
If you're using a packageset, psci-support
should be included and the
purescript_nixpkgs_packageset
rule will generate .purs
targets for all
packages, which expose package source files:
purescript_toolchain(
...,
psci_support = "@psc-package//:psci-support.purs",
...
)