Platforms, Packaging, Progress

Modernising a small OCaml package

I recently decided to refresh and update my ocal package, primarily to port it to use the excellent notty before adding support for indicating week-of-year. At the same time, I took the opportunity to update the build infrastructure now that the OCaml world has some shiny new packaging and build tools to go with OPAM, namely topkg and jbuilder . So, starting from Dave Scott’s wiki entry about how to package Mirage libraries, here’s what I had to do…

Remove Oasis remnants

git rm _oasis setup.ml Makefile * _tags myocamlbuild.ml .merlin mv ocal.opam/opam o && git rm -rf ocal.opam && mv o ocal.opam && git add ocal.opam cat > | .gitignore << _EOF _build *.merlin *.install _EOF

Although we’re removing the ocal.opam/descr file, we’re not going to lose the content: we’re going to let topkg opam pkg use its default --readme option to extract the relevant info from the first marked up section of the README.md :

# ocal — An improved Unix `cal` utility %%VERSION%% A replacement for the standard Unix `cal` utility. Partly because I could, partly because I'd become too irritated with its command line interface.

We also remove but don’t lose the functionality of the .merlin and OPAM ocal.install files, as jbuilder will generate them for us.

Create src/jbuild file

cat > | src/jbuild << _EOF (jbuild_version 1) (executable ((public_name ocal) (package ocal) (name main) (libraries ( astring calendar cmdliner notty notty.unix )) (flags (:standard -w "A-44-48-52" -safe-string)) )) _EOF

This corresponds to the 0.2.0 release of ocal. Note that the name parameter refers to the module that contains the entrypoint for the executable, and that we turn on all warnings ( A ) except for three that we wish to ignore:

44 : Open statement shadows an already defined identifier.

: Open statement shadows an already defined identifier. 48 : Implicit elimination of optional arguments.

: Implicit elimination of optional arguments. 52 : (see 8.5.1) Fragile constant pattern.

After I did some tidying up of the code to deal with the newly imposed warnings, make and make install satisfactorily (and quickly!) used jbuilder to build and install the executable as ~/.opam/system/bin/ocal (thanks to the public_name stanza in the src/jbuild file, above). make uninstall then caused jbuilder to remove it, before I opam pinned it and then reinstall through opam to check that workflow worked as well:

opam remove ocal opam pin add -yn --dev-repo ocal . opam install ocal

Create the topkg skeletons

Having refreshed the basic build infrastructure, next it’s time to update the packaging workflow. For a simple library we could use the automatic jbuilder/topkg plugin per the wiki entry:

mkdir pkg cat > | pkg/pkg.ml << _EOF #!/usr/bin/env ocaml #use "topfind" #require "topkg-jbuilder.auto" _EOF

However, this isn’t a library so we don’t have documentation to build so we don’t bother with the odoc skeleton. As a result we also need to customise pkg/pkg.ml so as to stop topkg publish failing when it can’t build docs:

#!/ usr / bin / env ocaml # use "topfind" # require "topkg-jbuilder" open Topkg let publish = Pkg . publish ~ artefacts : [ `Distrib ] () let () = Topkg_jbuilder . describe ~ publish ()

Prepare a release

Finally, we follow the standard topkg workflow to prepare a release. First, add an entry to CHANGES.md with the correct formatting and commit the result, and then:

distrib : [ -x $$ ( opam config var root ) /plugins/opam-publish/repos/ocal ] || \ opam-publish repo add ocal mor1/ocal topkg tag topkg distrib

…which creates tokens for accessing the GitHub repo for this project (if they don’t already exist), creates a release tag based on entries in CHANGES.md , and then creates the release tarballs (without the edits to pkg/pkg.ml this would also build the docs, but we have none).

Publish a release

Finally, we publish the release to GitHub and issue a pull request to the OPAM repository to add the new release into OPAM after linting and tests have passed.

publish : topkg publish topkg opam pkg topkg opam submit

Given that this repo has only a single package, we could in fact simply issue

topkg tag && topkg bistro

Also, as an alternative to customising the pkg/pkg.ml as indicated above, we could simply remember to indicate the appropriate customisation on the command line:

topkg publish distrib

…but topkg bistro wouldn’t then work.

Conclusion