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)
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 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

ocaml_signature

Module compilation

supported

ocaml_module

Module composition

supported

no filename restrictions; e.g. can bind a.mli and b.ml to make module C (i.e. automatic renaming).

Library construction

supported

ocaml_library

Library composition

supported

no restriction on location of lib components; an ocaml_library may contain other ocaml_library targets

Namespaced libs

supported

ocaml_ns rule; ns attribute of ocaml_module, ocaml_signature. Automatic renaming.

Archiving

supported

ocaml_library; archiving is automatic and context-dependent

Executables

supported

ocaml_binary; ppx_executable (from @rules_ppx)

Test executables

supported

ocaml_test

Custom VM executables

supported

vm_linkage attrib of ocaml_binary

Custom VM runtimes

supported

rule ocaml_runtime (-make-runtime); attribute runtime of ocaml_binary

CC library deps

supported

Attached at point of use

.cmt, .cmti generation

supported

-bin-annot or global flag --no@rules_ocaml//cfg:cmt

ocamlcmt

supported

build with --output_groups=cmtinfo

ocamlobjinfo

supported

build with --output_groups. Any combination of modinfo, siginfo, archinfo

developer flags

supported

e.g. -dlambda

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.deps(pkg= {…​})

opam pkg dependencies

pkg.subpkg available as @opam.pkg//subpkg/lib

opam commands

$ bazel run @opam — <cmd> Ensures that correct opam binary, root, switch, and CAML_LD_LIBRARY_PATH are used (no interference from environment variables.)

OCaml SDK libs

@opam.ocamlsdk packages export SDK libraries; for example, @opam.ocamlsdk//compiler-libs:common, @opam.ocamlsdk//dynlink/lib, etc.

OCaml SDK tools

ocamlobjinfo, ocamlcmt support implemented as inline aspects

Platform tools

Integrated support for utop: '$ bazel run @utop'

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.

Aspects

OBazl uses aspects to ..

See Aspects for more info.

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
MODULE.bazel
opam_dev = use_extension("@tools_opam//extensions:opam.bzl",
                         "opam", dev_dependency = True)
opam_dev.ocaml()
use_repo(opam_dev, "ocaml")
cmd line
$ bazel run @ocaml
Launch a utop repl
MODULE.bazel
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")
cmd line
$ bazel run @utop
Launch ocamldebug
MODULE.bazel
opam_dev = use_extension("@tools_opam//extensions:opam.bzl",
                         "opam", dev_dependency = True)
opam_dev.dbg()
use_repo(opam_dev, "dbg")
cmd line
$ 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
.bazelrc
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
cmd line
$ 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
.bazelrc
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
cmd line
$ 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
MODULE.bazel
opam = use_extension("@tools_opam//extensions:opam.bzl", "opam")
use_repo(opam, "opam")
cmd line
$ 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
  • ocamloptocamlopt.opt bazel build --tc=ocamlopt (default)

  • ocamlopt.opt - bazel build --tc=ocamlopt.opt

  • ocamlc.optocamlc.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

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

Opam

Opam libraries are modeled as Bazel repos. The name conversion is straightforward:

Nix

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

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

ocaml-ctypes

rules_ctypes

menhir

rules_menhir

cppo

rules_cppo

cinaps

planned rules_cinaps

mdx

planned rules_mdx

ocamlc-protoc-plugin

planned rules_ocaml_protoc or rules_oprotoc

js_of_ocaml

rules_jsoo (pre-alpha)

rocq (coq)

planned rules_rocq

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