ocamlbuild-ctools is a plugin to ocamlbuild to support C/C++ compilation for general purpose development as an alternative to build tools like SCons.
It is not OCaml specific although the plugin also provide additional support for mixed OCaml / C libraries and programs.
The tool is interesting because it require very little configuration work to set up standard project types such as C programs and C libraries. Typically a single .cprog or .clib file listing .o files is all that it takes for all dependencies to work with support for debug and release builds.
In the simple hello.c case, no configuration is needed beyond copying the plugin files to the source root.
There is built-in support for MSVC and GCC tool chains. Other tools can be added.
Dependencies are automatically discovered. No configuration work is needed for dynamic dependencies such as .h files including a .c file generated from another supported build rule such as the Ragel scanner / state-machine tool. Part of this is derived from ocamlbuild, but the C dependency logic is new.
ocamlbuild itself is a powerful signature driven general purpose build engine. It is outstanding in its ability to handle dynamic dependencies, and this plugin relies heavily on this fact. It is also very efficient at adding non-trivial tool support through its plugin system through compiled ocaml source as this plugin.
Out of the box (as of OCaml 3.10.1) ocamlbuild only supports compiling ocaml with very limited support for C files.
Cross platform builds are supported, although testing has not been conducted on Windows. The current configuration is copied from other build scripts that have been known to work on Windows.
For OCaml users, this plugin also makes it simple to create mixed C/OCaml programs and libraries.
For non-OCaml users, you do not really need to know OCaml to use ocamlbuild and ocamlbuild-ctools, but you do need to edit a bit of configuration in simple declarative OCaml statements.
Multi-variant build targets are supported. For example building with and without linking to an external library, with and without debug, and then run test cases on all variants. It works by extending filenames like:
$ ocamlbuild hello.variant_debug.prog $ ocamlbuild hello.variant_debug,friendly.prog
'debug' and 'friendly' are user defined tags the way ocamlbuild normally uses tags. 'debug' and 'release' tags have special tool support, other tags can trigger C flags when configured in the myocamlbuild.ml file. Tags can also trigger inclusion or exclusion of files as discussed in the following.
Internally the ctools engine always use long names to track this differences. A few dist_xxx declarations in the myocamlbuild.ml configuration file can set up friendly named targets.
Note: this section is a quick write-up to give you an idea. It is untested and incomplete. See also the README file and last section of the ocamlbuild_config.ml file for more information. Documentation contributions are welcome!
You need OCaml 3.10.1+ installed. This also gives you the ocamlbuild tool.
On Windows you need the Bash shell as well, which is probably a good idea for cross platform development, regardless. The Windows platform has not been tested and feedback is welcome.
You also need the cppinclude (cppi) tool cppi. Just compile the single c file and put the cppi binary in executable path.
To use ocamlbuild and ocamlbuild-ctools in a new project, you have to do the following things:
1. copy the myocamlbuild_config.ml to the project root
2. copy and rename the myocamlbuild.ml.template to myocamlbuild.ml in the project root.
3. edit the myocamlbuild.ml file to match your include paths and other C flags.
Now assuming you have hello.c, you compile it like
$ ocamlbuild hello.native mv _build/hello.native hello
The result is located in the _build directory as ocamlbuild always does. ocamlbuild does not work well without file extensions, therefore we rename the file at last.
If you have several source files, you create a .prog file:
$ cat hello.cprog helpmsg.o hello.o $ ocamlbuild hello.prog
Note that our target is .prog because we create a generic linked binary, but our source is a .cprog file to address the C linker tool. We could have other tools creating .prog binaries and ocamlbuild always track build rules from target to source.
Also note that we use the .o extension although you want to compile .c files first, then link with the .o files. It is always .o , also on windows. ocamlbuild-ctools does an internal platform name translation so you only need one .cprog file.
You can have tag dependent file inclusion and exclusion:
$ cat hello.cprog hello.o [friendly,~debug] helpmsg.o
The above will only include a help message if the 'friendly' tag is set for the .prog target and if 'debug' is not set for the target.
You can do the same with C libraries. DLL's are are not fully supported. Mixed OCaml C-libraries are discussed in the README.
Changes in header files should automatically be detected, and it used to work even for deepely nested include files through dynamically generated targets. Unfortunately the logic has somehow regressed, possibly when dismissing some internal hacks in favor of the .stamp signature files provided as of Ocaml 3.10.1 (we need to evaluate a closure signature of all dependency signatures, and apparently we only calculate one level now). The cause is not well understood.
Even with the above limitation, the tool works fairly well. Just be aware of the issue.
If you delete a source file, ocamlbuild will not remove it from the _build directory, and you may get a false build. This is an inherent limitation in ocamlbuild.
You can clean or manually delete files in _build to handle both of the above cases.
Another issue is the greedy nature of the cppinclude tool. It may not always do exactly what you want, although it works well in praxis. The README file explains how you can add you own dependency files to the source tree to override this logic. The approach still works better than intelligent include file scanners like uccp or gcc's built in because these are slow and depends on data not yet available in case of dynamically generated files.
OCamlbuild can not compile with more plugin files than myocamlbuild_config.ml and myocamlbuild.ml. Hopefully future versions will allow linking with multiple plugins and to include third party libraries the plugins can use.
There is no relation between ocamlbuild-ctools and Symbiosis, except they both rely on ocamlbuild at their core.
Symbiosis is a meta build engine to drive the build of individual software components.
ocamlbuild with ocamlbuild-ctools is an ordinary build tool for building a single software component.
Symbiosis does not require OCaml after initial compilation, but ocamlbuild does (at least every time the myocamlbuild.ml configuration file is updated).
Symbiosis will trigger a component build of any source code component with a build tool it can get a grip on, including Make, OMake, SCons, and ocamlbuild with or witout ocamlbuild-ctools, or plain shell scripts.