Bazel-OPAM integration.
@coswitch
only converts existing OPAM switches, so to use it you
must install and configure a switch. See below for instructions. You
must rerun coswitch whenever you change your switch (by installing or
removing packages).
Once you have installed a coswitch, you can express dependencies on
OPAM packages by adding bazel_dep
directives to your root
MODULE.bazel
file; for example:
bazel_dep(name = "alcotest", version = "0.0.0")
Important
|
Version "0.0.0" is a dummy bzlmod version id; the dependency will resolve to whatever version is installed in OPAM. |
Warning
|
If you add bazel_dep directives before you install the
coswitch, you will run into a bootstrapping problem. Bazel will not be
able to run or build any target because it will not be able resolve
the bazel_deps . In particular it will not be able to run the bazel
run @coswitch//new command needed to create the coswitch that will
enable bazel_dep resolution. To avoid this you can install the
coswitch executable on your PATH so you can run it independent of
Bazel. See instructions below.
|
You can run @coswitch
from any directory equipped with the following
files: .bazelrc
, MODULE.bazel
, and WORKSPACE.bazel
. If you
install it locally, as described below, you can run it independent of Bazel.
## common --enable_bzlmod # enabled by default for Bazel 7+ (1)
try-import %workspace%/.config/coswitch_registry.bazelrc (2)
common --registry=https://raw.githubusercontent.com/obazl/registry/main/ (3)
common --registry=https://bcr.bazel.build (4)
try-import .config/user.bazelrc (5)
-
The
common
prefix on these lines directs Bazel to use these directives for all commands (build
,test
,run
,query
). -
Imported file must contain
--registry=…
for your coswitch; see below -
@coswitch
is not yet available in the Bazel Central Repository (BRC) -
URL for the BCR. Bazel automatically uses it if no
--registry
directive is used; otherwise it must be explicitly required, as here. -
Optional. Will be ignored if
.config/user.bazelrc
does not exist. Do not put this file under version control. This allows each user to customize builds by adding Bazel directives. Any file name can be used; .config/user.bazelrc is just an OBazl convention.
common --registry=file:///path/to/coswitch/registry (1)
-
Path created by coswitch; for example,
/home/<uid>/.opam/5.1.1/share/obazl
module(
name = "mymodule", (1)
version = "0.0.0",
compatibility_level = 0,
bazel_compatibility = [">=7.0.0"] (2)
)
bazel_dep(name = "coswitch", version = "x.y.z") (3)
-
A common convention is to use the same name for the module and the basename of the root directory, but this is not a requirement.
-
The least Bazel version that supports Bazel modules (?)
-
See the Releases page for the latest version identifier.
# (1)
-
With bzlmod enabled this file will be ignored but it must exist. This constraint will be removed in a future version of Bazel.
$ bazel run @coswitch//new INFO: Analyzed target @coswitch~2.3.0//new:new (7 packages lo aded, 66 targets configured). INFO: Found 1 target... (1) Target @coswitch~2.3.0//new:new up-to-date: .bazel/bin/external/coswitch~2.3.0/new/new INFO: Elapsed time: 0.080s, Critical Path: 0.00s INFO: 1 process: 1 internal. INFO: Build completed successfully, 1 total action INFO: Running command line: .bazel/bin/external/coswitch~2.3.0/new/new Switch name: 4.14.0 (2) Compiler version: 4.14.0 Switch prefix: $HOME/.opam/4.14.0 (3) rules_ocaml version: 2.1.0 (3) Coswitch: $HOME/.opam/4.14.0/share/obazl (3) Registry: $HOME/.opam/4.14.0/share/obazl (3)
-
INFO
messages are printed by Bazel -
By default coswitch is created for current switch; override with
-s
or--switch
-
Printed by
@coswitch//new
(with$HOME
expanded)
By default each coswitch is installed in the corresponding switch, in
$OPAM_SWITCH_PREFIX/share/obazl
. To instead install to
$XDG_DATA_HOME/obazl
, pass ` — -x` or ` — --xdg`.
You can ask @coswitch//new
to print more information by passing -v
one or more times; such arguments must be passed after --
; for example:
$ bazel run @coswitch//new -- -v
To silence messages from @coswitch//new
, pass -q
or --quiet
.
To silence messages from Bazel you can use these directives (documented at Commands and Options and Command-Line Reference), which may be passed on the command line (NB: before --
) or added to .config/user.bazelrc
:
--noshow_progress --noshow_loading_progress --show_result=0 --ui_event_filters=-<event>,-<event> (1)
-
where <event> is one of: fatal, error, warning, info, progress, debug, start, finish, subcommand, stdout, stderr, pass, fail, timeout, cancelled or depchecker
Once you have configured a coswitch, you must tell Bazel to use it by
passing a --registry=
directive pointing to the coswitch. For example,
--registry=file:///home/<uid>/.opam/5.1.0/share/obazl
You can pass this on the command line, but it will be more convenient
to put it in a bazelrc file. One strategy is to put it in
.config/coswitch_registry.bazelrc
, and then add this to .bazelrc
(as noted above):
try-import %workspace%/.config/coswitch_registry.bazelrc common --registry=https://raw.githubusercontent.com/obazl/registry/main/ common --registry=https://bcr.bazel.build
Important
|
Order matters. The coswitch --registry directive should
be first, and the directive for the BCR must come last.
|
If you work with multiple OPAM switches, you will need to create a
coswitch for each; then to change to use a different coswitch, edit
your --registry
directive.
Note
|
Use of coswitches in this manner is independent of the "current" OPAM switch. |
For better integration with OPAM and the shell, you can use a shell
script to wrap the bazel invocation. When you install coswitch, a
sample script is installed at
$XDG_DATA_HOME/obazl/templates/bazel_wrapper.sh
. Copy this file to
tools/bazel
(it must be an executable named bazel
). Now when you
run a bazel command, control will be passed to this script, which runs
opam switch show
to discover the current switch, and then constructs
the appropriate --registry
directive and adds it to the bazel
command.
Once @coswitch//new
has done its work, the packages in the switch
are available to Bazel build programs as standard Bazel labels. The
labels are constructed according to a simple schema: package p
becomes @p//lib/p
; package p.q.r
becomes @p//lib/q/r
.
Executables are labelled @p//bin/p
; they are also accesible as
@ocaml//bin:p
.
Some examples:
OPAM | Bazel |
---|---|
|
|
|
|
|
|
|
|
Most (all?) OPAM packages contain one META
file in their root
directories. The standard compiler distributions are a little
different. They contain a small number of "distrib-packages":
-
compiler-libs
-
dynlink
-
ocamldoc
-
runtime_events (>=5.0.0)
-
stdlib
-
str
-
threads
-
unix
These are packages (so they can be listed as dependencies), but they are included in the compiler distributions rather than as free-standing OPAM/findlib packages.
Special case: num
. Implemented as a free-standing pkg, but installs
its files to the standard lib dir (lib/ocaml).
Prior to version 5.0.0
the distributions contained no META
files
for these packages within the ocaml
package. Instead, the
distrib-packages were expressed as OPAM top-level "virtual" packages
with redirection to artifacts in lib/ocaml
. They would be installed by OPAM when the compiler was installed.
For example, the dynlink
package for 4.14.0
is represented by
<opam-root>/4.14.0/lib/dynlink
, which contains only a META
file
indicating that the dynlink
archive files are found in
<opam-root>/4.14.0/ocaml
Starting with 5.0.0
the distib-packages are expressed by eight
nested META
files:
lib/ocaml/compiler-libs/META lib/ocaml/dynlink/META lib/ocaml/ocamldoc/META lib/ocaml/runtime_events/META lib/ocaml/stdlib/META lib/ocaml/str/META lib/ocaml/threads/META lib/ocaml/unix/META
These packages are no longer represented by toplevel subdirectories
within the switch’s lib
subdirectory.
@coswitch//new
creates a Bazel package for each of these within the
@ocaml
module. For example, @ocaml//lib/dynlink
. For compatibility
it also creates a Bazel module for each; for example,
@dynlink//lib/dynlink
. The build targets in these packages are
aliased to those in the @ocaml
module:
@dynlink//lib/dynlink => @ocaml//lib/dynlink
@coswitch//new
has special logic for translating the compiler
distribution itself.
Bazel pkg |
Imports (by symlinks to <switch-prefix>) |
|
|
|
|
|
|
|
alias to |
|
alias to |
|
alias to |
|
|
|
|
|
|
|
C sdk headers |
|
|
|
|
|
|
|
|
|
OBazl-specific helper pkg |
|
OBazl-specific helper pkg |
|
OBazl-specific helper pkg |
|
OBazl-specific helper pkg |
$ bazel run @coswitch//install -c opt
This will install an optimized build of the coswitch executable to
$HOME/.local/bin/coswitch
; it will also install some template files
to $XDG_DATA_HOME/obazl/templates
.
You can install the executable to a different directory by passing ` — -b /path/to/bindir`.
Once you have installed coswitch
on your local system you can remove
its bazel_dep
from your MODULE.bazel file, and run $ coswitch
.
@coswitch
converts META
files in the OPAM switch to BUILD.bazel
files in the Bazel "coswitch". It also generates bzlmod registry records.
By default, both the coswitch and the registry are created in
$OPAM_SWITCH_PREFIX/share/obazl
.
If you pass -- --xdg
, they are installed to $XDG_DATA_HOME/obazl
:
$XDG_DATA_HOME/obazl/opam/<switch name> $XDG_DATA_HOME/obazl/registry/<switch name>
A coswitch is essentially a mirror of a switch. Symlinks are used to integrate the the two.
For example, the coswitch record for package yojson
of switch
fiveone
(as describe below in Creating an OPAM switch) looks like this:
~ $ tree .opam/fiveone/share/obazl/lib/yojson .opam/fiveone/share/obazl/lib/yojson ├── MODULE.bazel ├── WORKSPACE.bazel ├── bin │ ├── BUILD.bazel │ └── ydump -> $HOME/.opam/fiveone/bin/ydump └── lib └── yojson ├── BUILD.bazel ├── META -> $HOME/.opam/fiveone/lib/yojson/META ├── dune-package -> $HOME/.opam/fiveone/lib/yojson/dune-package ├── opam -> $HOME/.opam/fiveone/lib/yojson/opam ├── yojson.a -> $HOME/.opam/fiveone/lib/yojson/yojson.a ├── yojson.cma -> $HOME/.opam/fiveone/lib/yojson/yojson.cma ├── yojson.cmi -> $HOME/.opam/fiveone/lib/yojson/yojson.cmi ├── yojson.cmt -> $HOME/.opam/fiveone/lib/yojson/yojson.cmt ├── yojson.cmti -> $HOME/.opam/fiveone/lib/yojson/yojson.cmti ├── yojson.cmx -> $HOME/.opam/fiveone/lib/yojson/yojson.cmx ├── yojson.cmxa -> $HOME/.opam/fiveone/lib/yojson/yojson.cmxa ├── yojson.cmxs -> $HOME/.opam/fiveone/lib/yojson/yojson.cmxs ├── yojson.ml -> $HOME/.opam/fiveone/lib/yojson/yojson.ml └── yojson.mli -> $HOME/.opam/fiveone/lib/yojson/yojson.mli
If installed with ` — --xdg`:
~ $ tree .local/share/obazl/opam/fiveone/lib/yojson .local/share/obazl/opam/fiveone/lib/yojson ├── MODULE.bazel ├── WORKSPACE.bazel ├── bin │ ├── BUILD.bazel │ └── ydump -> $HOME/.opam/fiveone/bin/ydump └── lib └── yojson ├── BUILD.bazel ├── META -> $HOME/.opam/fiveone/lib/yojson/META ├── dune-package -> $HOME/.opam/fiveone/lib/yojson/dune-package ├── opam -> $HOME/.opam/fiveone/lib/yojson/opam ├── yojson.a -> $HOME/.opam/fiveone/lib/yojson/yojson.a ├── yojson.cma -> $HOME/.opam/fiveone/lib/yojson/yojson.cma ├── yojson.cmi -> $HOME/.opam/fiveone/lib/yojson/yojson.cmi ├── yojson.cmt -> $HOME/.opam/fiveone/lib/yojson/yojson.cmt ├── yojson.cmti -> $HOME/.opam/fiveone/lib/yojson/yojson.cmti ├── yojson.cmx -> $HOME/.opam/fiveone/lib/yojson/yojson.cmx ├── yojson.cmxa -> $HOME/.opam/fiveone/lib/yojson/yojson.cmxa ├── yojson.cmxs -> $HOME/.opam/fiveone/lib/yojson/yojson.cmxs ├── yojson.ml -> $HOME/.opam/fiveone/lib/yojson/yojson.ml └── yojson.mli -> $HOME/.opam/fiveone/lib/yojson/yojson.mli
For each Bazel module in the coswitch (i.e. package in the switch),
@coswitch//new
also creates a Bazel registry record. The Bazel registry
has this structure (documented at
Bazel registries):
$ tree -L 1 .opam/fiveone/share/obazl/ .opam/fiveone/share/obazl/ ├── bazel_registry.json ├── lib (1) └── modules (2)
-
Contains coswitch package entries as described above
-
Contains bzlmod registry records
With ` — --xdg`:
$ tree -L 1 .local/share/obazl/registry/fiveone .local/share/obazl/registry/fiveone ├── bazel_registry.json └── modules
The bazel_registry.json
file contains the path to pkg entries in the OPAM switch:
{ "mirrors": [], (1) "module_base_path": "lib" (2) }
-
Not used
-
Relative to cwd; serves as based dir for relative paths in registry records
For XDG installations:
{ "mirrors": [], (1) "module_base_path": "$HOME/.local/share/obazl/opam/fiveone/lib" (2) }
-
Not used
-
Registry records implicitly reference this base path (in this case, absolute path)
The modules
subdirectory contains one record per Bazel module. The record for yojson
:
$ tree .local/share/obazl/registry/fiveone/modules/yojson [fiveone] .local/share/obazl/registry/fiveone/modules/yojson ├── 0.0.0 (1) │ ├── MODULE.bazel │ └── source.json └── metadata.json
-
Coswitch entries always use version 0.0.0, which resolves to whatever version is installed in the OPAM switch. This obviates the need to keep
bazel_dep
entries inMODULE.bazel
files in sync with the OPAM switch.
The source.json
file contains the information Bazel needs to find
the "source" (i.e. OPAM package) for the Bazel module. In a
networked registry, this would be a URL and integrity checksum. But
this registry is for local use only, so instead the source.json
file
looks like this:
{
"type": "local_path", (1)
"path": "yojson" (2)
}
-
Tells Bazel to use the
module_base_path
field of the registry’sbazel_registry.json
file to construct the local path to module source. -
To be interpreted as the desired subdirectory of
module_base_path
.
Together, bazel_registry.json
and yojson/0.0.0/source.json
indicate that the sources for yojson
are located at $HOME/.local/share/obazl/opam/fiveone/lib/yojson
.
First update your OPAM installation: $ opam update
.
Then create a switch, giving it a name and a compiler version. Synax: opam switch create <name> <compiler id>
or just opam switch
create <compiler id>
. To list available compilers: $ opam switch list-available
Example:
$ opam switch create my511 5.1.1
This should install the switch and make it the "current" switch. Verify:
$ opam switch
Note
|
To make sure your shell is in sync with OPAM: $ eval $(opam env) .
|
Now install the packages you need:
opam install base bigarray-compat cppo csexp
If you need a lot of packages you can create a simple script:
#!/bin/sh
opam install a b c ... etc. ...
Verify installation:
$ opam list