User Guide: @coswitch

The OBazl rules for OCaml can be used with both system and local OPAM switches:

  • "system" switches are installed in the OPAM "root", which is usually $HOME/.opam.

  • local switches are installed in the project directory, in a subdirectory usually named _opam. See OPAM Local Switches for more information.

Support for OPAM integration is provided by rule ocaml_import, which makes precompiled artifacts available to Bazel build actions. To import an (installed) OPAM package, all that is needed is

  • a MODULE.bazel file in the package root directory, that defines the package as a Bazel module and lists its dependencies;

  • a BUILD.bazel file containing an ocaml_import target that imports the files of the package;

  • if the package contains subpackages, a BUILD.bazel file for each; and

  • a record in a Bazel module registry to make the package accessible via the Bazel module dependency management system.

The @coswitch module contains a @coswitch//new target that automatically generates these files and installs them locally.

Nothing is written to the OPAM switch. All generated files and directories are written to a dedicated directory in $XDG_DATA_HOME or in the .config/ subdirectory of the project (for local OPAM switches). Symlinks are used to expose files in the switch.

Installation

In MODULE.bazel:

bazel_dep(name = "coswitch", version = "1.0.0")

This module is not yet registered with the Bazel Central Registry; until it is, put the following in .bazelrc:

common --enable_bzlmod
common --registry=https://raw.githubusercontent.com/obazl/registry/main/
common --registry=https://bcr.bazel.build

Usage

The @coswitch Bazel module provides the following bazel run targets:

  • @coswitch//new - generates an OBazl coswitch corresponding to the current switch, or a switch passed via -s or --switch.

The generated coswitch contains one Bazel module for each package in the switch. Build target labels follow this schema: for packages, @<pkg>//lib/<pkg>; for segmented subpackages, @<pkg>//lib/<seg1>/<seg2>. For example:

  • yojson@yojson//lib/yojson

  • mtime.clock.os@mtime//lib/clock/os

Coswitches

Coswitch registries

bazel run @coswitch//new generates a Bazel module registry for the packages in the switch. For system switches it is located at

$XDG_DATA_HOME/obazl/registry/<switch>

Usually $XDG_DATA_HOME is $HOME/.local, so for example the registry for switch 5.1.0 would be at:

$HOME/.local/obazl/registry/5.1.0

For local switches, the registry is located at <root>/.config/obazl/registry; note that the switch name is omitted from the path. So with a local opam switch, regardless of compiler version, the registry would be at

<root>/.config/obazl/registry

Bazel registries contain a bazel_registry.json file in the registry root directory; for local registries like the coswitch registry, this file controls resolution of module labels (like @yojson) to directories on the local file system. It will contain an entry like the following:

"module_base_path": "/path/to/coswitch/root"

The bazel_registry.json file in non-local registries (such as the official Bazel Central Registry) will not contain a "module_base_path" entry, because registry entries will contain information required to retrieve the module source from the network.

Registry records

Each Bazel module must be registered by adding a registry record to a bzlmod registry. Such records have the form described by Index registry.

For coswitch registries: for example, the record for yojson in shared coswitch 5.1.0~rc2 will have the following form:

<$HOME>/.local/share/obazl/registry/5.1.0~rc2/modules/yojson
├── 0.0.0
│   ├── MODULE.bazel
│   └── source.json
└── metadata.json

The same, in a local registry:

<projroot>/.config/obazl/registry/modules/yojson
├── 0.0.0
│   ├── MODULE.bazel
│   └── source.json
└── metadata.json

In both cases, source.json will look like this:

source.json
{
    "type": "local_path",
    "path": "yojson"
}

Here local_path tells Bazel that the source location is to be resolved by appending the path string to the module_base_path field of the bazel_registry.json file in the registry root.

For non-local registries, the source.json file will look like the following:

{
    "url": "...",
    "integrity": "...",
    "strip_prefix": "...",
}

Registering coswitch registries

Registries other than the Bazel Central Registry (a/k/a BCR) must be registered with Bazel to take effect; this can be done by adding a --registry directive to any bazelrc file; for example:

common --registry=<protocol>:///path/to/registry

OBazl modules (such as @coswitch itself) have not yet been added to the BCR; until they are, they can be used by adding the following to a bazelrc file:

common --registry=https://raw.githubusercontent.com/obazl/registry/main/

OBazl convention is to register coswitch registries by adding something like the following to <projroot>/.config/coswitch_registry.bazelrc:

common --registry=file:///<home>/<uid>/.local/share/obazl/registry/5.1.0~rc2

and to load this by adding the following to <projroot>/.bazelrc:

try-import .config/coswitch_registry.bazelrc

Finally, if you have used --registry directives, you must also add the BCR:

common --registry=https://bcr.bazel.build

Module resolution

When Bazel encounters a bazel_dep directive in a MODULE.bazel file, it searches the registered repositories for the corresponding registry record.

record for the yojson package in switch 5.1.0 would be:

The --registry directive

Once the registry has been generated, you must direct Bazel to use it for module lookups. The OBazl convention is to write the registry directive to <root>/.config/coswitch_registry.bazelrc, and to add try-import .config/coswitch_registry.bazelrc to the root .bazelrc file. The @coswitch//new command will overwrite .config/coswitch_registry.bazelrc with the path to the generated registry. An example is:

common --registry=file:///<home>/<uid>/.local/share/obazl/registry/5.0.0