Version française
Home     About     Download     Resources     Contact us    
Browse thread
Shared libraries with ocamlopt callable from C (without main())?
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: Matthieu Dubuget <matthieu.dubuget@g...>
Subject: Re: [Caml-list] Shared libraries with ocamlopt callable from C (without main())?
Ben Jakb a écrit :
> I try to create a static library called "libadd5wrapperlib.a" ( I know
> kinda WTF ). So here is my simple example:

I did not succeed (yet ;-) ) in making a static library…
I have an example with a shared one, though.

> 
> (Warning: You'll see a lot of bad code below, sorry for that, I work
> hard to improve)
> 
> =====  $ cat ./add5.ml =====
>     (* the code doesnt add anything yet, it just gives 5 back *)
>     let add_five () =
>       let i = ref 0 in
>         i := 5;
>         !i;;
>     Callback.register "add five" add_five;;

I wrote :

	Callback.register "add five" (fun () -> 5)


> 
> 
> =====  $ cat ./add5wrapperlib.c (Wraps the Ocaml code ) =====
>     #include <stdio.h>
>     #include <caml/mlvalues.h>
>     #include <caml/callback.h>
#include <assert.h>

#define CAMLCBK_INIT( callback, cbk_name) \
        static value *callback = NULL; \
        if (callback == NULL) callback = \
              caml_named_value(cbk_name);\
          assert(callback);

static int init_done = 0;

void init_lib(void){
  char *vide[1];
  vide[0] = NULL;
  if (!init_done){
	caml_startup(vide);
	init_done = 1;
  }
}

int add5wrapper(){
  CAMLCBK_INIT(cbk, "add_five");
  return (Int_val ( caml_callback(*cbk, Val_unit)));
}


> =====  $ cat ./include/libadd5wrapper.h (header file) =====
extern void init_lib(void);
extern int add5wrapper(void);
> 
> =====  $ cat ./main.c =====
>     #include <stdio.h>
>     #include "libadd5wrapper.h"
>     int main (int argc,char **argv){
init_lib();
>        printf("Gimme - %d \n", add5wrapper());
>        return 0;
> }
> 
> Now I try to BUILD the whole thing:
> ----------------------------------------------

ocamlopt -c add5.ml
ocamlopt -c add5wrapperlib.c
ocamlopt -o add5lib.native.so -ccopt -shared add5.cmx add5wrapperlib.o

ocamlc.opt -ccopt -Iinclude -c main.c
gcc -o maintest.native main.o -l:add5lib.native.so -L. -Wl,-rpath=.

In fact, i used ocamlbuild.


--------------myocamlbuild.ml -------------------------------------
open Ocamlbuild_plugin
open Command
open Outcome

(* properties as (string * string) list read from "ocamlc -config" *)
let my_ocamlc_config =
  let rec sc s h =
    Scanf.sscanf s "%s@: %s@\n%n"
      begin fun k v n->
		let h' = (k, v) :: h in
		let len = String.length s in	
		  if
			len - n <= 0
		  then
			h'
		  else
			sc (String.sub s n (len - n)) h'
      end in
    sc (Ocamlbuild_pack.My_unix.run_and_read "ocamlc -config") []

let ext_o = List.assoc "ext_obj" my_ocamlc_config
let ext_so = List.assoc "ext_dll" my_ocamlc_config
let syst = List.assoc "system" my_ocamlc_config

let split s ch =
  let x = ref [] in
  let rec go s =
    let pos = String.index s ch in
      x := (String.before s pos)::!x;
      go (String.after s (pos + 1))
  in
	try
      go s
	with Not_found -> !x

exception Found of string

let pwd () =
  let env = Array.to_list (Unix.environment ()) in
  let rec search = function
	  [] -> ""
	| h :: tl ->
		try
		  Scanf.sscanf h "PWD=%s" (fun x -> x)
		with
			_ -> search tl in
	search env

let split_nl s = split s '\n'

let before_space s =
  try
    String.before s (String.index s ' ')
  with Not_found -> s
	
let uncap_module_path p =
  (Pathname.dirname p) / (String.uncapitalize (Pathname.basename p))

let _ = dispatch begin function
  | After_rules ->
	  if syst = "linux_elf" then
		flag ["link"; "cmldll"]  (S[A"-ccopt";A"-shared"])	
	  else if syst = "mingw" then begin
		flag ["link";"cmldll"] (S[A"-output-obj"])
	  end;

	  flag ["cmldll";"link";"byte"] (S[A"-custom"]);
	  	
	  rule "Mixed C-Ocaml native DLL: cmldll & o* & cmx* -> native DLL
(.dll | .so)"
		~dep:"%.cmldll"
		~prod:("%.native" ^ ext_so)
		begin
		  fun env build ->
			let output = env ("%.native" ^ ext_so)
			and input = env "%.cmldll" in
			let dir = Pathname.dirname input in

			(* TODO: use functions of Pathname module? *)
			let ext_o_files, moduls_files =
			  string_list_of_file input |>
				  List.partition (fun fic -> Filename.check_suffix fic ".o") in
			let objs = ext_o_files |>
				List.map Filename.chop_extension |>
					List.map (fun fic -> fic ^ ext_o) in
			let cmxs = moduls_files |>
				List.map (fun modul ->
							(uncap_module_path modul) -.- "cmx") in
			let deps = cmxs @ objs in

			  List.iter ignore_good
				(build (List.map (fun x -> [dir/x]) deps));

			  Cmd (S [!Options.ocamlopt;
					  A"-o"; Px output;
					  T (tags_of_pathname output++"ocaml"++"native"++"cmldll"++"link");
					  atomize_paths deps
					 ])
		end;

      (* Allows to have .h copied in the _build directory *)
      dep ["file:main.c"] ["include/libadd5wrapper.h"];

	  flag ["include_libadd"] (S[A"-ccopt";A"-Iinclude"]);
	  tag_file "main.c" ["include_libadd"];

	  rule "Compile maintest.native"
		~deps:["main.o"; "add5lib.native.so"]
		~prod:"maintest.native"
		begin fun _ _ ->
		  let target = "maintest.native" in
		  let spc = "-Wl,-rpath=" ^ ((pwd ()) / (!Options.build_dir))  in
			Cmd(
			  S[
				A"gcc";
				A"-o";A target;
				A"main.o";
				A"-l:add5lib.native.so";
				A"-L."; A spc;
			  ])
		end;


  | _ -> ()
end

--------------end of myocamlbuild.ml -----------------------

and the helper file add5lib.mlcdll:

-------------add5lib.mlcdll----------------------
Add5
add5wrapperlib.o
-------------end of add5lib.mlcdll------------------

do
ocamlbuild maintest.native
in order to build.

This myocamlbuild.ml file is a "work in progress".
You can use it as you want if it can be of any help for you.

Salutations