Platforms, Packaging, Progress
Modernising a small OCaml package
I recently decided to refresh and update my ocal package,1 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.48
: 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
So that’s it: a simple executable distribution taken from old-school Oasis and
OCamlBuild infrastructure to shiny new modern jbuilder and topkg. The new
scheme seems to me to be an improvement: faster build times, simpler (to my
eyes) metadata, autogeneration of more of the repeated metadata (.merlin
etc),
and a reasonably simple Makefile
that I actually think I understand.
Definitely progress :)
-
A somewhat over-featured replacement for the standard UNIX
cal
utility, because I got irritated by its American-centricity and my initial Python replacement was just too slow… ↩