package ocb-stubblr

  1. Overview
  2. Docs

OCamlbuild plugin for C stubs

OCamlbuild plugin for building C stubs. See the Intro, Utilities and Examples.

Intro

ocb-stubblr helps dealing with C libraries, especially ones that provide new primitives to OCaml programs (stubs).

Most of the plugin consists of new tags and rules. In order to activate them, init needs to be called from Ocamlbuild_plugin.dispatch:

let () = Ocamlbuild_plugin.dispatch Ocb_stubblr.init

The plugin helps with three aspects of building C stubs:

Using .clib files

The plugin adds header detection to .clib targets, so that the .c files referenced by a .clib are made to depend on any project-local files they #include.

It also adds the parameterized tag link_stubs. Tagging ocaml archives (<foo.cm{,x}a>: link_stubs(path/libbar)) makes them depend on the C libraries produced by the file path/libbar.clib. It also adds the appropriate -cclib/-dllib flags when linking the native/bytecode archives. This will record the linker flag -lbar.a in the produced archive, causing the final executables that use it, to also link with the stub library.

pkg-config

The plugin adds the parameterized tag pkg-config.

Tagging objects with pkg-config(package) will query pkg-config for the package, add its --cflags to the compilation of tagged C files, and add its --libs to the linkage of tagged C and OCaml files.

For example

<src/*.{c,cma,cmxa}>: pkg-config(sdl2)

will add the appropriate flags to compile the C sources with SDL2 includes, link them with libSDL2, and record the flags needed to link with SDL in the OCaml archives.

The full syntax of the tag is pkg-config(package[,relax][,static]).

relax will ignore the package if it's not found. Without it, the compilation will abort. static will use --static instead of --libs for the link tags.

Note pc files in the current Opam switch take precedence; see Pkg_config.

Multi-lib

Sometimes it can be desirable to compile a C library in several ways, e.g. with different compiler options, and install all of the versions.

The plugin allows compiling every .clib file path/libstub.clib as if the file X/<target>/path/libstub+<target>.clib was also present. It will replicate the needed .c and .h files in the same directory. For example, a valid new target is X/foobar/path/libstub+foobar.a. This new archive is built from the same files as the original one, but it doesn't inherit any of its directly applied tags. Instead, the files <X/foobar/**/*> can be marked with a separate set of tags, in order to build and link this archive with different options.

As a special case, there are two pre-defined targets: mirage-xen and mirage-freestanding. These are already marked with the compilation options needed to produce C stubs that work with these two MirageOS backends.

Note If your paths already contain +, OCamlbuild solver is likely to get confused. Assume that the meaning of + in paths has been hijacked by ocb-stubblr. The new semantics of + is accessible only through transcendental hermenautics.

Utilities

type ocb_hook = Ocamlbuild_plugin.hook -> unit
val init : ?incdirs:bool -> ?mllibs:path list -> ocb_hook

init ?incdirs ?paths initializes the plugin.

incdirs controls wheter include_include_dirs is called. Defaults to true. Use false to disable.

mllibs passed to ocaml_libs to detect .mllib files. Defaults to ["."]. Use [] to disable.

val ocaml_libs : ?mllibs:path list -> ocb_hook

ocaml_libs ~mllibs calls Ocamlbuild_plugin.ocaml_lib on every .mllib found in mllibs.

mllibs is a list of either files or directories. Directories in the list are searched recursively. mllibs defaults to ["."].

It's a shortcut to enable use_LIB tag for every LIB.mllib.

val include_include_dirs : ocb_hook

include_include_dirs will add -I dir when linking OCaml programs for every dir marked as include. Hence, if the program is compiled against a locally-built library that has extra -dllib flags, the mentioned stubs archives will be correctly resolved.

val ccopt_flags : ?tags:string list -> string -> ocb_hook

ccopt_flags tags options is an ocb_hook adding -ccopt options when compiling C sources tagged with ~tags.

tags defaults to [].

val cclib_flags : ?tags:string list -> string -> ocb_hook

cclib_flags tags options is an ocb_hook adding -cclib options when linking C objects tagged with ~tags.

tags defaults to [].

val after_rules : (unit -> unit) -> ocb_hook

after_rules f is function After_rules -> f () | _ -> ().

val dispatchv : ocb_hook list -> unit

dispatchv hooks is a shortcut for registering several ocb_hooks.

It is equivalent to Ocamlbuild_plugin.dispatch hookf where hookf is a function that applies each hook from hooks in order.

val (&) : ocb_hook -> ocb_hook -> ocb_hook

h1 & h2 is a hook combining h1 and h2.

module Pkg_config : sig ... end

Query pkg-config.

OS and machine detection

These utilities are included because the host OS and architecture are sometimes important when building C stubs.

type os = [
  1. | `Linux
  2. | `Hurd
  3. | `Darwin
  4. | `FreeBSD
  5. | `OpenBSD
  6. | `NetBSD
  7. | `DragonFly
  8. | `KFreeBSD
  9. | `Haiku
  10. | `HP_UX
  11. | `AIX
  12. | `Interix
  13. | `Minix
  14. | `QNX
  15. | `SunOS
  16. | `Cygwin of string
  17. | `Mingw of string
  18. | `Uwin of string
  19. | `UNKNOWN of string
]

A selection of favourite operating systems.

type machine = [
  1. | `x86_64
  2. | `x86
  3. | `ARMv6
  4. | `ARMv7
  5. | `UNKNOWN of string
]

A selection of machine architectures.

val os : unit -> os

os () is the normalized result of uname -s.

val machine : unit -> machine

machine () is the normalized result of uname -m.

Examples

Assume a project laid out like the following:

Project dir:

./myocamlbuild.ml
./_tags
./src/foo.ml
./src/stubs.c
./src/extra/defs.h
./src/libstubs.clib
./src/foo.mllib
./exe/demo.ml

The content of src/foo.mllib:

Foo

The content of src/libstubs.clib:

stubs.o

The content of _tags:

<src>: include

Basic integration

Initialize from myocamlbuild.ml:

let () = Ocamlbuild_plugin.dispatch Ocb_stubblr.init

The file src/extra/defs.h will be automatically used when compiling src/stubs.c, if the latter #includes it.

Adding the tag

<src/*.cm{,x}a>: link_stubs(src/libstubs)

will make foo.cmxa/foo.cma link against libstubs.a/dllstubs.so.

pkg-config

Adding the tag

<src/*.{c,cma,cmxa}>: pkg-config(sdl2, relax)

will add the appropriate compilation and linkage flags to the C sources and archives, and the OCaml archives. If sdl2 is not installed, it will be ignored.

In-tree executables

To build demo.native and/or demo.byte, _tags needs to contain

<exe/*>: use_foo

This will create the tag use_foo (see ocaml_libs), causing demo to link with the cm{,x}a archive. The archive in turn contains the link flags for the stubs library (link_stubs), and sdl2 (pkg-config). It will also add src to the include path when linking OCaml executables (see include_include_dirs).

Multi-lib

Invoking ocamlbuild X/t/src/libstubs+t.a will build libstubs+t.a.

If _tags contained

<src/*.c>: ccopt(-flub)
<X/t/**/*.c>: ccopt(-DT)

then libstubs+t.a would not be compiled with -flub. Instead, the pre-processor symbol T would be defined during compilation.

Mirage

If using Topkg, register the .clib file using Ocb_stubblr_topkg.mirage.

Pkg.describe ... @ fun c ->
...
Ok ([ Pkg.clib "path/to/libstubs.clib" ] @
      Ocb_stubblr_topkg.mirage "path/to/libstubs.clib")

or directly require individual targets:

Pkg.[clib "src/libstubs.clib";
   clib "X/mirage-xen/src/libstubs+mirage-xen.clib";
   clib "X/mirage-freestanding/libstubs+mirage-freestanding.clib"]

Otherwise, arrange for these libraries to be installed.

When using Mirage, the META file still needs to be extended with the appropriate link flags, to signal the mirage tool to redirect linkage for various targets.

Composition

let myhook = function
| After_rules -> ...
| ...
let () = Ocb_stubblr.(dispatchv [ init; myhook; ])
let () = dispatch Ocb_stubblr.(init & myhook)
OCaml

Innovation. Community. Security.