Distributing OCaml libraries: common problems for packager and how upstream author can help solving it
Having already done some Debian packages of OCaml libraries, I wish to share my experience on what are the most common problems. The intent is to help people releasing OCaml libraries to have a good interaction with (Debian) packager. I think most of the tips should also apply to other distributions as well. I consider the use of findlib mandatory for this task as described in the Debian OCaml packaging policy.
Problems listed here are common, and I have encountered them for my libraries... In fact, most of the time when I finished building a personnal OCaml library I package it for my own and to test that everything is fine. Being a packager and an upstream author is really a different job.
"Upstream author" is used for people developing and releasing OCaml libraries and "packager" for people doing packages for a distribution.
Here is the list of the most common problems and their solutions:
- Missing the "static link clause" in COPYING/LICENSE
Libraries are linked statically in OCaml. It is an advantage and a problem. But upstream author should remember that the LGPL license doesn't support static link directly. If you link an executable with the library statically, the executable is contaminated by the license. This error is probably the most problematic, because it requires to go through the whole project fixing license header for explaining that there is an exception to the LGPL.
There is no way for a packager to fix this problem. Packager are not allowed to change the license. Library having this problem can be still packaged but complicate the legal aspect of building application using this library.
Good example of this can be found in OCaml LICENSE itself :
As a special exception to the GNU Library General Public License, you may link, statically or dynamically, a "work that uses the Library" with a publicly distributed version of the Library to produce an executable file containing portions of the Library, and distribute that executable file under terms of your choice, without any of the additional requirements listed in clause 6 of the GNU Library General Public License. By "a publicly distributed version of the Library", we mean either the unmodified Library as distributed by INRIA, or a modified version of the Library that is distributed under the conditions defined in clause 2 of the GNU Library General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU Library General Public License.
Followed by the text of the LGPL.
Each file header looks like:
This file is distributed under the terms of the GNU Library General Public License, with the special exception on linking described in file ../LICENSE.
- Forget that some architecture doesn't have an ocamlopt compiler
By far this is the most common error I have seen. Upstream author consider that you are able to build a native and byte version of their library. Unfortunately this is not the case for most architectures. This error is pretty simple to fix by separating byte and opt targets in Makefile. The ultimate test is to rename /usr/bin/ocamlopt and /usr/bin/ocamlopt.opt to something different and try to rebuild the library from scratch.
- Missing or incomplete META file
META file is the base of everything in findlib. This is very important to provide good META file because it helps other people to easily build thing over OCaml library. Each library should provide its own META file. Until now, Debian packager was used to write this missing META file and contribute it to upstream author. I think that now findlib has become a standard in OCaml developement, so upstream should take into consideration this file.
A good test for META file is to install it and:
~$ ocaml Objective Caml version 3.10.1 # #use "topfind";; - : unit = () Findlib has been successfully loaded. Additional directives: #require "package";; to load a package #list;; to list the available packages #camlp4o;; to load camlp4 (standard syntax) #camlp4r;; to load camlp4 (revised syntax) #predicates "p,q,...";; to set these predicates Topfind.reset();; to force that packages will be reloaded #thread;; to enable threads - : unit = () # #require "ZZZ";;
If you are able to load your library in ocaml toplevel, this is a good point.
- Doesn't use ocamlfind to install library
ocamlfind allow to install library. Using it is the best way to install your ocaml library. Moreovoer, you should use the most simple way to install.
For pure OCaml library this is straightforward and allow Debian packager to override destination directory by using environment variable OCAMLFIND_DESTDIR.
install: ocamlfind install toto META toto.mli toto.cmi toto.cmxa toto.cma toto.cmx
For non-pure library, you could add a OCAMLFINDFLAGS variable to allow Debian packager add "-ldconf ignore":
install: ocamlfind install $(OCAMLFINDFLAGS) toto META toto.mli toto.cmi toto.cmxa toto.cma toto.cmx
- Doesn't distribute .mli files
".mli" files is the human-readable interface to the library. Even if most OCaml programmers use the ocamldoc HTML API of the library, it is best to distribute these files. In particular, Debian packager use it to automatically generate up-to-date HTML API documentation directly.
- Doesn't distribute .cmx files
".cmx" files contain more informations than ".cmxa". Distributing these files allow application based on the library to be more optimized.
- Forget about other OS files
This one comes from my "Windows experiment". Most of the time upstream author doesn't forget about "*.a" but doesn't add "*.lib" (yes is this windows counterpart of ".a" file).
- Use bad wildcard for file to distribute
This one is not very OCaml specific. In Makefile and sh you have the same wildcard "*.mli". But if the wildcard is not expanded into sh, it just stay as is. It will make the executable called looked for a file "*.mli" literally. Mixing Makefile wildcard and sh is the best option here:
install: ocamlfind install $(OCAMLFINDFLAGS) toto META $(wildcard *.mli *.cmi toto.cmxa toto.cma *.cmx)
This way you test and expand to only existing files.
- Use a custom build system
This is more or less a problem. In fact, if everything is fine, there is no problem and you should just forget about this point. If the custom build system is complex and if there is an error in it: it becomes a big problem.
I must confess that I have myself done this kind of mistake a lot of time. But looking back, I think that most of the time I should have used directly OCamlMakefile. I think relying on ocamlfind for library installation, simple Makefile, OCamlMakefile, OMake or ocamlbuild, is just enough.
- Missing BTS
This point is a problem for upstream author and communication with packagers (from Debian and other distributions). A bug tracking system is a way to publish, comment, work on and find solution of bugs. If you have a single problem in a library having the bug public helps people to show you what is wrong.
Of course, Debian (and Fedora, FreeBSD...) has its own bug tracking system and upstream author can use it directly. This is less work for upstream but tends to make the bug "distribution specific". Having an upstream author BTS helps to track the bug across distributions.
If upstream author doesn't like maintaining their own BTS, he/she should consider hosting part of the project on a forge like OCamlCore.org. Even if they don't host the source code, this is still a good idea.
That's all. If you have other common problems, feel free to comment, i will add it to the list.