best practices как писать либу с сишными биндингами чтобы она с минимумом проблем собиралась на всех инкарнациях камля.

Scope: ocaml утилиты, сишный код, утилиты для сборки/конфигурирования/установки NB: сейчас тут вперемешку проблемы, решения, жалобы и нытьё

Этапы сборки биндингов

Ocamlbuild

myocamlbuild.ml:

open Ocamlbuild_plugin
open Command
open Printf


(**
  [link_stubs name ?cclib stubs] embeds options into ocaml library [name] so that
  further linking with main program will find C [stubs] library
  @param name ocaml library name
  @param stubs C stubs module name
  @param cclib extra -cclib options to pass to linker
*)
let link_stubs name ?(cclib=[]) stubs =
  let stubs_lib = sprintf "lib%s.%s" stubs !Options.ext_lib in
  let stubs_dll = sprintf "dll%s.%s" stubs !Options.ext_dll in
  let cclib = List.flatten & List.map (fun lib -> ["-cclib"; lib]) & ("-l"^stubs) :: cclib in
  List.iter (fun ext ->
    let file = sprintf "file:%s.%s" name ext in
    (* embed -l option into ocaml library so that C linker can find C stubs *)
    flag ["link"; "ocaml"; "library"; file] & atomize cclib;
    dep  ["link"; "ocaml"; "library"; file] [stubs_lib];
    if ext = "cma" then
    begin
      (* embed -l option into ocaml library so that ocamlrun can find dll stubs *)
      flag ["link"; "ocaml"; "library"; "byte"; file] & atomize ["-dllib"; "-l" ^ stubs;];
      dep  ["link"; "ocaml"; "library"; "byte"; file] [stubs_dll]
    end
  ) ["cma"; "cmxa"]


let ocaml_lib_stubs name ?(dir="") ?(cclib=[]) stubs =
  link_stubs name ~cclib stubs;
  (* locally compiled programs (toplevel and tests) will use locally compiled library *)
  ocaml_lib name;
  (* find library locally (ocamlrun and C linker respectively) *)
  flag ["link"; "ocaml"; "byte"; "use_"^name] & atomize ["-dllpath"; !Options.build_dir / dir];
  flag ["link"; "ocaml"; "use_"^name] & atomize ["-I"; (if dir = "" then "." else dir);]


;;


dispatch begin function
| After_rules ->
  ocaml_lib_stubs "mylib" ~cclib:["-lotherlib"] "mylib_stubs";
  dep ["c"; "compile"; "file:mylib_stubs.c"] ["mylib_config.h"];
| _ -> ()
end

libmylib\_stubs.clib:

mylib_extra.o
mylib_stubs.o

Функции link\_stubs и ocaml\_lib\_stubs достаточно обобщённые и портабельные (проверено - правильно работают на linux и ocaml/msvc). В файле .clib перечисляются сишные модули из которых состоит сишная сторона стабов, расширение всегда .o (да, это будет работать и с ocaml/msvc, хотя cl и создаёт объектные файлы с расширением .obj). Обратите внимание на имя файла - на такое соглашение (lib[stubs].clib) завязаны встроенные правила ocamlbuild и это учитывается параметром stubs функции link\_stubs). Если сишный код зависит от внешних библиотек то эту зависимость надо указать явно :

В примере зависимость mylib\_stubs.c от (локального) mylib\_config.h приведёт к тому что последний будет скопирован предварительно в \_build и его сможет найти сишный компилятор.


2011-03-26 13:09