Caml-get is a tool to distribute and get Objective-Caml code, in a way similar to the apt-get utility.
We all have various functions, types, or even whole modules that we use in many different programs or libraries, but that somehow we don't put in a separate library. I see two reasons for this:
Moreover, even if we took the time to isolate groups of consistent elements or put together elements doing the same kind of treatments, we would not, because it would make the programs or libraries we distribute depend on these small libraries. These dependencies can make the installation of the final software a real pain, especially when it is distributed in source only. So, in the hope that more people try and use the final software, we keep the "little functions" inside, in various Misc and Stuff modules. Worse, we sometimes find a bug in one of these functions (or we improve them), and must think about fixing them in all the places we copy-pasted the code.
So the idea behind Caml-get is to make copy-paste easier, and keep track of it, to automatically change it when the original source code is modified. As an extension, it can be used to distribute this code so that other developers can use what would never have been put in a distributed library anyway.
It is possible with a language like OCaml, because writing polymorphic and/or general usage code is very easy and comes naturally.
Caml-get comes with a Cameleon2 plug-in to perform operations on the repositories, and browse available elements.
An element can be an OCaml value, a type, a module, or an exception. An implementation code and an interface code are associated to each element.
Each element has a name, which can be different from the real name in the OCaml code, in order to organize distributed elements in a different way from the files they come from.
There are two different activites: distributing code, and using distributed code.
The distributed code is put in a Caml-get archive, on a web server (by now, supported protocols are "HTTP://" and "file://"), (usually) by the author of the code. This archive contains the functions, types, modules and exceptions, along with their name, version number, comment (à la ocamldoc), and of course interface and implementation code. To create an archive, see distributing code. The archive can be seen as the server side.
Using the distributed code can be seen as the client side. The repository is the file containing the available elements and sources. By default it is ~/.camlget_repository. This repository will be filled by retrieving the content of Caml-get archives. Then, some Caml-get commands can be used to extract code of elements from the repository and put it in your own source code files, or to upgrade the code of elements (in your code) with a newer version found in the repository. See using distributed code.
Creating a Caml-get archive is done using the ocamldoc custom generator odoc_ar.ml coming with Caml-get. This is a regular ocamldoc generator, but instead of generating documentation, this generator generates an archive.
ocamldoc -g odoc_ar.cma -keep-code -o myarchive.cga misc.ml misc.mli stuff.ml stuff.mliThis command creates the myarchive.cga Caml-get archive, using the given .ml and .mli files. The -keep-code option is mandatory so the code is kept by ocamldoc and the generator can put it in the archive.
Only elements with @cgname and @version tags are put in the generated archive. The element name is the one in argument of the @cgname tag, for example:
(** This function returns the content of a file in the form of one string. @cgname Files.input_file_as_string @version 1.0 *) val input_file_as_string : string -> stringNow this element will be refered as "Files.input_file_as_string" to extract it or print its code.
If you want to constraint the type of an archived value, you can use the @cgtype, like in:
(* ... @cgtype int -> string ... *)Note that if a module is exported as a Caml-get element (i.e. it has @cgname and @version tags), then none of its elements are exported, since an exported module should be used as a whole.
caml-get list -a myarchive.cgaThis command lists the elements of the Caml-get archive myarchive.cga.
caml-get update http://...Add the given url as a new source, or update the data associated to the source if it is already present in the repository.
caml-get updateUpdate all sources of the repository.
caml-get listList the elements present in the repository.
caml-get list *String* *List*List the elements of the repository whose name matches one of the given expressions.
caml-get print String.keep_alpha_nums >> misc.ml caml-get print -f misc.ml String.keep_alpha_numsThe two commands are equivalent. They append the implementation of the element Files.input_file_as_string to the file misc.ml.
caml-get print -i String.keep_alpha_nums >> misc.mli caml-get print -i -f misc.mli String.keep_alpha_numsThe two commands are equivalent. They append the interface code of the element Files.input_file_as_string to the file misc.mli.
caml-get upgrade misc.ml misc.mli foo.ml foo.mliFor each "Caml-get element" of the given files, upgrade the element if there is a greater version in the repository.
caml-get remove http://...Remove the given url from the list of sources in the repository.
Assertions are a good way to specify the behaviour of a function or a module. Such assertions could also be used as test cases for the function or module.
To do so, we define a new @cgtest tag for OCamldoc comments. Each "code" elements of the text in the tag will be used as an assertion and must be a valid OCaml expression of type bool. The "code" elements in OCamldoc texts are indicated between [ and ] or <[ and ]>.
For example, let's define the following function, used to compare version numbers, represented by lists of integers:
let (<<) = let rec iter = function (, ) -> false | (, _) -> true | (_,) -> false | (h1::q1, h2::q2) -> (h1 < h2) or (h1 = h2 && (iter (q1,q2))) in fun v1 v2 -> iter (v1,v2)
In the OCamldoc comment, we write:
(** [v1 << v2] returns true if version [v1] is strictly inferior to version [v2]. @cgtest The following assertions hold: - [[1;2] << [1;3]] - [not ([1;2] << [1;2])] - [let v = [ 1; 2 ; 3] and w = [ 1; 2] in w << v ] *)
In the comment, we indicate three assertions, i.e. three OCaml expressions of type bool which should be evaluated to true:
Then the following commands use the odoc_cgtest OCamldoc custom generator to gather these assertions and generate code in a file check.ml. After compiling this file, we can run the check.x program:
ocamldoc -g odoc_cgtest.cmo -o check.ml myfile.ml ocamlc -o check.x myfile.cmo check.ml ./check.x All test passed
If we add a bad assertion, for example [1;2]<<[1;2], the same procedure will fail, indicating that the assertion number 3 (starting from 0) for value Myfile.(<<) is false:
The following tests failed: Myfile.(<<) 
To make the defined assertions appear in the documentation, we can use the odoc_htmlcg OCamldoc custom HTML generator also included in the Caml-get distribution.
Even though Caml-get works, a lot of features are still missing:
Caml-get is distributed under the GPL license.
|0.8 (2012-04-11)||Use findlib to compile and install (packages "camlget" and "camlget.gtk").|
|0.7 (2006-05-10)||New architecture, with a Camlget library to manage repositories and perform basic operations (update, upgrade, ...) and a Camlget_gui module containing stuff to perform these operations in a graphical interface (based on LablGtk2). The command line tool uses the library , and asks the user before upgrading elements with the ability to display differences. A new graphical tool and the cameleon plugin uses both the library and the gui module. Caml-get now needs Cameleon >= 1.9.13.|
|0.6 (2005-09-23)||Some minor fixes.|
|0.5 (2005-06-16)||Some minor fixes.|