Quickstart

Overview

drom has two purposes:

  • It’s a tool to easily create OCaml projects: drom new PROJECT will create a complete OCaml project, with all the required files for opam and dune, plus additional files for project management on Github, documentation (Sphinx and Github Pages) and CI (Github Actions).

  • It’s a tool to build and install the project, combining calls to opam and dune to create a local switch, install dependencies, build the project and its documentation

drom uses subcommands:

$ drom
DROM(1)                           Drom Manual                          DROM(1)



NAME
       drom - Create and manage an OCaml project

SYNOPSIS
       drom COMMAND ...

COMMANDS
       build
           Build a project

       build-deps
           Install build dependencies only

       clean
           Clean the project from build files

       dev-deps
           Install dev dependencies (odoc, ocamlformat, merlin, etc.)

       doc Generate library API documentation using odoc in the docs/doc
           directory

       fmt Format sources with ocamlformat

       help
           display help about drom and drom commands

       install
           Build & install the project in the project opam switch

       package
           Create or update a package within a project

       project
           Create or update a project

       publish
           Generate a set of packages from all found drom.toml files

       run
           Execute the project

       sphinx
           Generate general documentation using sphinx

       test
           Run tests

       tree
           Display dependencies in the project as a tree

       uninstall
           Uninstall the project from the project opam switch

       update
           Update packages in the project opam switch

COMMON OPTIONS
       --help[=FMT] (default=auto)
           Show this help in format FMT. The value FMT must be one of `auto',
           `pager', `groff' or `plain'. With `auto', the format is `pager` or
           `plain' whenever the TERM env var is `dumb' or undefined.

       --version
           Show version information.

Creating a Project

Let’s suppose we want to create a new project hello_world. We can use drom to create almost everything we need for that!

Let’s create the project:

$ drom new hello_world
Creating project "hello_world" with skeleton "program", license "LGPL2"
  and sources in src/hello_world:
Creating directory hello_world
Calling git init
Initialized empty Git repository in /home/lefessan/tmp/hello_world/.git/
Calling git remote add origin git@github.com:ocamlpro/hello_world
Calling git commit --allow-empty -m Initial commit
[master (root-commit) 732c93c] Initial commit
Creating file dune-project
Creating file src/hello_world/index.mld
Creating file hello_world.opam
Creating file src/hello_world_lib/version.ml
Creating file src/hello_world_lib/index.mld
Creating file hello_world_lib.opam
Creating file sphinx/_static/css/fixes.css
Creating file test/output-tests/test2.ml
Creating file test/output-tests/test2.expected
Creating file test/output-tests/test1.expected
Creating file test/output-tests/dune
Creating file test/inline-tests/test.ml
Creating file test/inline-tests/dune
Creating file test/expect-tests/test.ml
Creating file test/expect-tests/dune
Creating file .github/workflows/workflow.yml
Creating file .github/workflows/doc-deploy.yml
Creating file docs/sphinx/index.html
Creating file docs/doc/index.html
Creating file sphinx/license.rst
Creating file sphinx/install.rst
Creating file sphinx/index.rst
Creating file sphinx/conf.py
Creating file sphinx/about.rst
Creating file docs/style.css
Creating file docs/index.html
Creating file docs/favicon.png
Creating file docs/README.txt
Creating file dune
Creating file .ocp-indent
Creating file .ocamlformat-ignore
Creating file .ocamlformat
Creating file .gitignore
Creating file README.md
Creating file Makefile
Creating file LICENSE.md
Creating file CHANGES.md
Creating file src/hello_world/main.ml
Creating file src/hello_world/dune
Creating file src/hello_world_lib/main.ml
Creating file src/hello_world_lib/dune
Forced Update of file drom.toml
Forced Update of file src/hello_world_lib/package.toml
Forced Update of file src/hello_world/package.toml
Calling git add .drom test/output-tests/test2.ml test/output-tests/test2.expected test/output-tests/test1.expected test/output-tests/dune test/inline-tests/test.ml test/inline-tests/dune test/expect-tests/test.ml test/expect-tests/dune src/hello_world_lib/version.ml src/hello_world_lib/package.toml src/hello_world_lib/main.ml src/hello_world_lib/index.mld src/hello_world_lib/dune src/hello_world/package.toml src/hello_world/main.ml src/hello_world/index.mld src/hello_world/dune sphinx/license.rst sphinx/install.rst sphinx/index.rst sphinx/conf.py sphinx/about.rst sphinx/_static/css/fixes.css hello_world_lib.opam hello_world.opam dune-project dune drom.toml docs/style.css docs/sphinx/index.html docs/index.html docs/favicon.png docs/doc/index.html docs/README.txt README.md Makefile LICENSE.md CHANGES.md .ocp-indent .ocamlformat-ignore .ocamlformat .gitignore .github/workflows/workflow.yml .github/workflows/doc-deploy.yml

As you can see, drom created a directory hello_world with the following files:

  • drom.toml for project management by drom, and two files package.toml for each sub-package in their sources.

  • Source files for the project, composed of an hello_world_lib library in src/hello_world_lib/ and a driver executable in src/hello_world/main.ml

  • git source version management files: .gitignore and .git/ for the git revision control tool

  • Documentation files: docs/index.html and docs/style.css for the project homepage

  • sphinx/ directory for the Sphinx documentation formatter

  • README.md, CHANGES.md and LICENSE.md project files

  • Specific files for opam and dune: dune-project, hello_world.opam and src/hello_world/dune for example

  • .github/ for Github Actions CI

  • .ocamlformat for the ocamlformat code formatting tool and .ocp-index for source lookups

  • Examples of test files in the test/ directory

At this point, you may decide that drom has done enough for you, and you can go back to using opam and dune to work on your project.

The drom.toml file has a particular importance, it can be used by drom to update all the generated files with this information. It contains information on the project, such as its name, license, description, dependencies, etc.

Everytime you modify drom.toml, you should call drom project again to update the project:

$ cd hello_world
$ emacs drom.toml
$ drom project
drom: Entering directory '/tmp/hello_world'
Updating file dune-project
Updating file hello_world.opam
Updating file hello_world_lib.opam
Updating file src/hello_world/dune
Updating file src/hello_world_lib/dune
Calling git add .drom src/hello_world_lib/dune src/hello_world/dune hello_world_lib.opam hello_world.opam dune-project .

Here, we added a dependency in the drom.toml file:

...
[dependencies]
ez_file = ""
...

And we see that drom updated the files for dune and opam.

drom project also takes a few command line options that can be used to modify the drom.toml file:

  • --skeleton SKELETON can be used to change the skeleton used to manage files. drom knows about 3 differents skeletons by default: library (a simple library), program (a driver calling a library, the default skeleton used when nothing is specified) and virtual (no default package). An up-to-date list of project skeletons can be found in the generated file _drom/known-skeletons.txt.

  • --upgrade can be used to upgrade the drom.toml file when you are using a more recent version of drom

  • --binary and --javascript can be used to switch between generating binaries and generating Javascript using js_of_ocaml by default.

Notice that, just after creating the project, you should be able to build it and run it with no error!

Building a Project

drom can be used to build a project. In this case, it will use opam to manage the environment (dependencies) and dune to build the project. You don’t need to know these tools for basic usage of drom.

Because drom makes extensive use of local opam switches, it is a good idea to use it from opam-bin to benefit from binary caching of packages, to speedup creation of local switches.

Building locally

By default, drom will try to build the project in its directory:

$ cd hello_world
$ drom build -y
Calling opam switch create -y . --empty
Calling opam install -y ocaml.4.10.0
The following actions will be performed:
  ∗ install base-bigarray       base
  ∗ install base-threads        base
  ∗ install base-unix           base
  ∗ install ocaml-base-compiler 4.10.0                     [required by ocaml]
  ∗ install ocaml-config        1                          [required by ocaml]
  ∗ install ocaml               4.10.0
===== ∗ 6 =====

<><> Gathering sources ><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
[ocaml-base-compiler.4.10.0] found in cache

<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><>
∗ installed base-bigarray.base
∗ installed base-threads.base
∗ installed base-unix.base
∗ installed ocaml-base-compiler.4.10.0
∗ installed ocaml-config.1
∗ installed ocaml.4.10.0
Done.
# Run eval $(opam env) to update the current shell environment
Calling opam switch set-base ocaml
Calling opam install -y --deps-only ./_drom/new.opam
The following actions will be performed:
∗ install dune 2.7.0

<><> Gathering sources ><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
[dune.2.7.0] found in cache

<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><>
∗ installed dune.2.7.0
Done.
# Run eval $(opam env) to update the current shell environment
Calling opam exec -- dune build
Done: 40/46 (jobs: 1)
Build OK

During this build, drom performed the following operations:

  • It loads the project definition file drom.toml

  • It creates a local opam switch (directory _opam) where it installs the version of OCaml specified in the edition field of the project definition

  • It installs all the dependencies of the package. In our simple example, it is only the dune build tool.

  • Once the environment is ok, it builds the project using dune.

Since building the environment can take some time, it is important to know that it is only done the first time. It will also be upgraded only if the dependencies are changed.

We can now run the program:

$ drom run
In opam switch /tmp/hello_world/_opam
Calling opam exec -- dune build
Done: 0/0 (jobs: 0)
Calling opam exec -- dune exec -p hello_world -- hello_world
Hello world!

It’s a bit verbose, but the last line Hello world! was printed by our project!

Building with a global opam switch

drom can use global switches also. For example, if you want to install the project in that switch:

$ drom build --switch 4.10.0
Error: You must remove the local switch `_opam` before using option --switch

Since we previously built the project locally, we have a local _opam switch. drom will not remove this switch automatically, because it is often long to rebuild. So, you will have to do it yourself (or backup it if you are not using opam-bin):

$ rm -rf _opam
$ drom build --switch 4.10.0
Calling opam switch link 4.10.0
Directory /tmp/hello_world set to use switch 4.10.0.
Just remove /tmp/hello_world/_opam to unlink.
In opam switch 4.10.0
Calling opam install --deps-only ./_drom/new.opam
Nothing to do.
# Run eval $(opam env) to update the current shell environment
Calling opam exec -- dune build
Build OK

drom performed exactly the same steps as for a local build. In our case, the opam switch 4.10.0 already existed on our computer, and the dependencies were already installed, so it only built the project.

However, if the switch specified by --switch does not exist globally, drom will call opam to create it.

Installing the Project

Now that we have tested that our project correctly builds in a local switch and in a global switch, we can ask drom to install it in the switch:

$ drom install
Directory /tmp/hello_world set to use switch 4.07.0.
Just remove /tmp/hello_world/_opam to unlink.
In opam switch 4.07.0
Calling opam install --deps-only ./_drom/new.opam
Nothing to do.
Calling opam exec -- dune build
Calling opam uninstall -y hello_world
The following actions will be performed:
  ⊘ remove hello_world 0.1.0

<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><>
  ⊘ removed   hello_world.0.1.0
Done.
Calling opam pin -y --no-action -k path .
Package hello_world does not exist, create as a NEW package? [Y/n] y
[hello_world.~dev] synchronised from file:///tmp/hello_world
hello_world is now pinned to file:///tmp/hello_world (version 0.1.0)
Calling opam install -y hello_world

<><> Synchronising pinned packages ><><><><><><><><><><><><><><><><><><><><><><>
[hello_world.0.1.0] no changes from file:///tmp/hello_world

The following actions will be performed:
  ∗ install hello_world 0.1.0*

<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><>
∗ installed hello_world.0.1.0
Done.
Calling opam unpin -n hello_world
Ok, hello_world is no longer pinned to file:///tmp/hello_world (version 0.1.0)
Installation OK

As we can see in this example, drom performed the following steps:

  • Building the project, as in the previous sections

  • Removing all the packages of the project that may already be installed

  • Pinning all the packages for opam

  • Installing the pinned packages (rebuilding them in opam)

  • Unpinning all the packages

Building Documentation

drom generates a web-site for your project with 2 parts that you need to generate separately: a documentation of the library API automatically generated by odoc and a general documentation that you can modify, generated by the sphinx-doc tool, with the Read-the-doc theme . You will need to install them, it’s usually something like:

pip install sphinx
pip install sphinx_rtd_theme

The documentation is generated in the _drom/docs/ directory. If you are on Github, drom generates a Github action that will automatically merge this directory into the gh-pages branch after every merge/push in the master branch, so that you can easily use Github Pages to host your project documentation.

The main webpage is created from docs/index.html, and the Sphinx files used to generate the documentation are in sphinx/. You can edit these files before generating the documentation.

To generate the documentation, you must call two commands:

  • drom odoc, each time you want to generate the documentation of the API

  • drom sphinx, each time you want to compile the Sphinx files

The command drom doc can be used to generate everything. You can use these commands with an extra argument --view to open a local browser on the documentation.

Since generating the API documentation requires to use odoc, drom will automatically install the Development dependencies of your project. They are usually tools like merlin, odoc or ocamlformat that only developers will need.

Still, you can trigger directly their installation using:

$ drom dev-deps
In opam switch 4.10.0
Calling opam install odoc ocamlformat
The following actions will be performed:
  ∗ install odoc        1.5.1
  ∗ install ocamlformat 0.15.0
===== ∗ 2 =====

<><> Gathering sources ><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
[ocamlformat.0.15.0] found in cache
[odoc.1.5.1] found in cache

<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><>
∗ installed odoc.1.5.1
∗ installed ocamlformat.0.15.0
Done.

In our case, only odoc and ocamlformat have been detected as missing, so they are installed.

Let’s now generate the API documentation:

$ drom odoc
In opam switch 4.10.0
Calling opam exec -- dune build
Calling opam exec -- dune build @doc
Calling rsync -auv --delete _build/default/_doc/_html/. _drom/docs/doc
sending incremental file list
./
highlight.pack.js
index.html
odoc.css
hello_world/
hello_world/index.html

sent 26,886 bytes  received 103 bytes  53,978.00 bytes/sec
total size is 26,507  speedup is 0.98

The API documentation has been generated and copied into _drom/docs/doc.

Let’s now generate the Sphinx documentation:

$ drom sphinx
Calling sphinx-build sphinx _drom/docs/sphinx
Running Sphinx v1.8.5
building [mo]: targets for 0 po files that are out of date
building [html]: targets for 4 source files that are out of date
updating environment: 4 added, 0 changed, 0 removed

/tmp/hello_world/sphinx/index.rst:8: WARNING: Title underline too short.

Welcome to hello_world doc
=================
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
writing output... [100%] license
generating indices... genindex
writing additional pages... search
copying static files... done
copying extra files... done
dumping search index in English (code: en) ... done
dumping object inventory... done
build succeeded, 1 warning.

The HTML pages are in _drom/docs/sphinx.

We can now check how it looks like:

$ xdg-open ./_drom/docs/sphinx/index.html