bazel_dep(name = "rules_ocaml", version = "3.0.0") bazel_dep(name = "tools_opam", version = "1.0.0") opam = use_extension("@tools_opam//extensions:opam.bzl", "opam") opam.deps(pkgs = {"cmdliner": "1.3.0"}) # example: depend on opam pkg 'cmdliner' use_repo(opam, "opam", "opam.ocamlsdk", "opam.cmdliner") # import for use in BUILD.bazel files
OBazl Guide Last updated Mar 24, 2025
Purpose: high-level overview of the rules & tools of the OBazl SDK, and how they work together. |
Overview
The OBazl toolsuite is a collection of tools that extend Bazel to support OCaml software development. This puts Bazel at the center of the developer experience.
Neither Bazel nor the OBazl toolsuite is a “platform”. OBazl would be more accurately characterized as a kind of SDK. Tool integration is acheived by using Bazel’s extension facilities (custom rules, macros) rather than incorporation into a monolithic tool. But the effect is the same: one tool that drives the entire experience.
The heart of OBazl is rules_ocaml, a Bazel ruleset for OCaml. These are the rules responsible for managing build tasks: constructing compiler command lines, organizing dependencies, etc.
The brain of OBazel is
tools_opam, which provides
seamless support for opam. Since in practice
opam is used by virtually all OCaml projects, OBazl projects will always
(currently) depend on both rules_ocaml
and tools_opam
, which is
expressed by including the following lines in the project’s
MODULE.bazel
file:
MODULE.bazel (fragment)
OBazl integration makes tools available to the developer; but OBazl
is irenic; it does not compel the use of particular tools. If you want
to use a particular tool, you must say so by listing it as a
dependency in your MODULE.bazel
file.
The tools_opam module extension separately models an opam
switch, the OCaml distribution it contains, and the packages installed
in it.
|
Features
rules_ocaml features
Feature | Status | Comment |
---|---|---|
Signature compilation |
supported |
|
Module compilation |
supported |
|
Module composition |
supported |
no filename restrictions; e.g. can bind |
Library construction |
supported |
|
Library composition |
supported |
no restriction on location of lib components; an |
Namespaced libs |
supported |
ocaml_ns rule; ns attribute of |
Archiving |
supported |
|
Executables |
supported |
ocaml_binary; |
Test executables |
supported |
|
Custom VM executables |
supported |
|
Custom VM runtimes |
supported |
rule |
CC library deps |
supported |
Attached at point of use |
|
supported |
|
|
supported |
build with |
|
supported |
build with |
developer flags |
supported |
e.g. |
etc. |
tools_opam features
Feature | Comment |
---|---|
zero config |
The tool itself requires no configuration; you need only configure your (Bazel) module by listing the packages you need in |
opam pkg dependencies |
|
opam commands |
|
OCaml SDK libs |
|
OCaml SDK tools |
|
Platform tools |
Integrated support for |
The OBazl rules are deliberately low-level, in keeping with the goal of giving the developer complete control (i.e. no magic). OBazl build rules correspond more-or-less directly to the build commands they construct. The down side of sugar-free rules is a degree of inconvenience. For example, OBazl does not analyze implicit dependencies, so it is the responsibility of the developer to discover and list them. It does not support file globbing, so each source file must have a build rule. Most such inconveniences can and will be addressed over time by tooling built on the foundation of the primitive rules.
Dev tasks
Code fragments below assume that the following lines are in the root MODULE.bazel
:
bazel_dep(name = "rules_ocaml", version = "3.0.0") bazel_dep(name = "tools_opam", version = "1.0.0")
Compile, archive, link
Launch an ocaml repl
opam_dev = use_extension("@tools_opam//extensions:opam.bzl", "opam", dev_dependency = True) opam_dev.ocaml() use_repo(opam_dev, "ocaml")
$ bazel run @ocaml
Launch a utop repl
opam_dev = use_extension("@tools_opam//extensions:opam.bzl", "opam", dev_dependency = True) opam_dev.utop(version = "2.15.0-1", ocamlinit = ".config/ocamlinit") use_repo(opam_dev, utop="opam.utop")
$ bazel run @utop
Launch ocamldebug
opam_dev = use_extension("@tools_opam//extensions:opam.bzl", "opam", dev_dependency = True) opam_dev.dbg() use_repo(opam_dev, "dbg")
$ bazel run @dbg --@dbg//pgm=bin:hello
Generate interface code from a .ml
file
$ bazel build //pkg:tgt --output_groups=gensig
Generate .cmt
, .cmti
files
$ bazel build //pkg:tgt --output_groups=cmt,cmti
Apply ocamlobjinfo
build:modinfo --aspects @tools_opam//aspects:ocamlobjinfo.bzl%modinfo build:modinfo --output_groups=objinfo build:siginfo --aspects @tools_opam//aspects:ocamlobjinfo.bzl%siginfo build:siginfo --output_groups=objinfo build:archinfo --aspects @tools_opam//aspects:ocamlobjinfo.bzl%archinfo build:archinfo --output_groups=objinfo
$ bazel build //src:Hello --config=modinfo # Hello.cmx => Hello.cmx.objinfo $ bazel build //src:Hello --config=siginfo # Hello.cmi => Hello.cmi.objinfo $ bazel build //src:Hello_cmi --config=siginfo # Hello.cmi => Hello.cmi.objinfo $ bazel build //src:libGreetings --config=archinfo # Greetings.cmxa => Greeings.cmxa.objinfo
May be applied to opam packages:
$ bazel build @opam.ounit2//lib --config=archinfo # oUnit.cmxa => oUnit.cmxa.objinfo
Apply ocamlcmt
build:cmt_info --aspects @tools_opam//aspects:ocamlcmt.bzl%cmt build:cmt_info --output_groups=cmtinfo build:cmti_info --aspects @tools_opam//aspects:ocamlcmt.bzl%cmti build:cmti_info --output_groups=cmtinfo
$ bazel build //src:Hello --config=cmt_info # Hello.cmt => Hello.cmt.info $ bazel build //src:Hello --config=cmti_info # Hello.cmti => Hello.cmti.info
Run an opam
command
opam = use_extension("@tools_opam//extensions:opam.bzl", "opam") use_repo(opam, "opam")
$ bazel run @opam -- list
Other tasks
-
<3rd party tool> <file>
(e.g. ocp-indent) -
Apply a tool to
.cmt
,.cmti
files -
Generate intermediate diagnostic files, e.g.
-dparsetree
,-dlambda
, etc. -
PPX processing - see rules_ppx User Guide: Dev tasks
-
-stop-after
compilation phase -
Run build tools, e.g.
ocamlobjinfo
,ocamldoc
, etc. -
etc.
The OCaml SDK
The standard OCaml distribution (SDK) is modeled as a Bazel repo
(module) named @opam.ocamlsdk
Toolchains
rules_ocaml
declares a Bazel toolchain API for OCaml, but does not
define any specific toolchains. It’s up to the user to supply the
required toolchain definitions (implementations). That’s part of what
tools_opam
does.
SDK Compilers
-
ocamlopt
⇒ocamlopt.opt
bazel build --tc=ocamlopt
(default) -
ocamlopt.opt
-bazel build --tc=ocamlopt.opt
-
ocamlc.opt
⇒ocamlc.opt
-bazel build --tc=ocamlc
or--tc=ocamlc.opt
-
ocamlc.opt
-
ocamlc.byte
-
ocamlopt.byte
-
ocamlcp
- profiling compiler -
ocamloptp
- profiling compiler
SDK libraries
SDK libraries are modeled as Bazel packges within the @opam.ocamlsdk
module:
SDK libraries
-
@opam.ocamlsdk//bigarray/lib
(pre-5.0.0) -
@opam.ocamlsdk//compiler-libs:common
-
@opam.ocamlsdk//compiler-libs:bytecomp
-
@opam.ocamlsdk//compiler-libs:optcomp
-
@opam.ocamlsdk//compiler-libs:toplevel
-
@opam.ocamlsdk//compiler-libs:native-toplevel
-
@opam.ocamlsdk//dynlink
-
@opam.ocamlsdk//ffi
- C headers -
@opam.ocamlsdk//ocamldoc
-
@opam.ocamlsdk//profiling
-
@opam.ocamlsdk//runtime_events
-
@opam.ocamlsdk//str
-
@opam.ocamlsdk//stublibs
-
@opam.ocamlsdk//threads
-
@opam.ocamlsdk//unix
SDK Tools
SDK tools (ocamdebug
, ocamlobjinfo
, etc.) are modeled a little differently.
SDK tools
-
ocaml (repl/toplevel) see Launch an ocaml repl
-
ocamldoc: planned aspect:
bazel build foo/bar --aspects @tools_opam//aspects.bzl%ocamldoc
? -
ocamlprof: planned aspect:
bazel build foo/bar --aspects @tools_opam//aspects.bzl%ocamlprof
? -
ocamlmktop planned rule in
rules_ocaml
:ocaml_toplevel
or similar -
ocamlnat planned;
bazel run @repl --tc=ocamlopt
? -
ocamldep planned
-
ocamllex - see rules_ocaml
-
ocamlyacc - see rules_ocaml
-
FFI (C interface) -
deps = ["@opam.ocamlsdk//ffi/lib"]
-
ocamlobjinfo
see Apply ocamlobjinfo -
ocamlcmt
see Apply ocamlcmt
Unsupported: ocamlbuild
, ocamlmklib
Maybe: ocamlfind
, findlib
. Supported as an ordinary opam pkg, but
no special adapter for running ocamlfind
.
Related:
OCaml Platform support
OCaml platform tools
-
odoc: planned
bazel build foo/bar --aspects @tools_opam//aspects.bzl%odoc
? -
ocamlformat: planned
bazel build foo/bar --aspects @tools_opam//aspects.bzl%ocamlformat
? -
opam-publish: planned: custom rule in
tools_opam
-
merlin - ?
-
ocaml-lsp - ?
-
dune - not supported
Opam
Opam libraries are modeled as Bazel repos. The name conversion is straightforward:
Nix
-
How I Start: Nix excellent blog article for getting started. 2020/03/08
-
Nix-powered development with OCaml Blog article, 2023-03-04
Installing manpages: Getting documentation (nixpkgs manual)
Empty the store:
sudo nix-collect-garbage
Emits messages like `deleting '/nix/store/pg7zsw894hv5v6q5h1cmqibs2jis2mkb-libmpack-1.0.5.drv' `
but lots of stuff remains in /nix/store/
MacOS
-
Using Nix on macOS Blog article, 2022-07-23. Contains excellent detailed info about dealing with
zsh
(Fixing shell integration).
Other tools
Ordinary libraries are easy to use. You just list them as
dependencies, and then refer to them in your BUILD.bazel
files.
Tools and libraries that generate build tools (e.g. ctypes
) require a
more elaborate build protocol, so they’re good candidates for Bazel
customization using either custom rules or macros.
Examples: menhir
; ctypes
.
Tool integration: adapter v. plugin
Plugins are compiled; adapters are interpreted. Adapters are written in starlark. etc.
Tool support is implemented by defining a Bazel module, conventionally
called either rules_<tool>
or tools_<tool>
. For a few of the most
commonly used tools, however, support has been integrated directly
into tools_opam
. Currently that means opam
, utop
, and user (as
opposed to build) tools in the standard OCaml distributions, such as
ocamlobjinfo
and ocamlcmt
.
The tool integrations remain independent of rules_ocaml
; that is,
the build rules for OCaml remain independent and unaware of any other
tooling, and vice-versa. Integration is based on standardized and
public protocols and data formats, rather than linking to library code.
Any tool or library can be integrated into the Bazel platform with relatively little effort. Here are some examples:
Other tools and libraries
Tool | OBazl | Tool | OBazl | |
---|---|---|---|---|
planned |
||||
planned |
planned |
|||
rules_jsoo (pre-alpha) |
rocq ( |
planned |
Help
There’s a lot of stuff here. Help screens (planned):
-
bazel run @rules_ocaml//help
-
bazel run @rules_ocaml//help:ocaml_module
-
bazel run @opam.ocamlsdk//help
-
bazel run @opam.ocamlsdk//ffi:help
-
bazel run @opam//help
-
etc.
Manpages would be nice, but that would invove an installation step. Maybe we should support something like:
bazel run @rules_ocaml//help -- install