|
Warning
|
OBSOLETE. Use tools_opam instead. |
Bazel-OPAM integration.
Detailed documentation below.
First: configure an OPAM switch. By default, coswitch will use the current switch.
## common --enable_bzlmod # enabled by default for Bazel 7+
try-import %workspace%/.config/coswitch_registry.bazelrc
common --registry=https://raw.githubusercontent.com/obazl/registry/main/
common --registry=https://bcr.bazel.build
try-import .config/user.bazelrcmodule(
name = "mymodule",
version = "0.0.0",
compatibility_level = 0,
bazel_compatibility = [">=7.0.0"]
)
bazel_dep(name = "coswitch", version = "2.3.0")$ bazel run @coswitch//new
Next step: create or configure a project. See The OBazl Book for guidance.
@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
commonprefix 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 -
@coswitchis not yet available in the Bazel Central Repository (BRC) -
URL for the BCR. Bazel automatically uses it if no
--registrydirective is used; otherwise it must be explicitly required, as here. -
Optional. Will be ignored if
.config/user.bazelrcdoes 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.
|
Note
|
For recent versions of Bazel, the WORKSPACE.bazel file is not needed. |
# (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)
-
INFOmessages are printed by Bazel -
By default coswitch is created for current switch; override with
-sor--switch -
Printed by
@coswitch//new(with$HOMEexpanded)
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.
Misconfigured repositories may result in a message like the following:
ERROR: Error computing the main repository mapping: in module dependency chain <root> -> [email protected]: module not found in registries: [email protected]@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_depentries inMODULE.bazelfiles 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_pathfield of the registry’sbazel_registry.jsonfile 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