caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* Shared libraries with ocamlopt callable from C (without main())?
@ 2009-01-08 22:45 Ben Aurel
  2009-01-09  8:21 ` [Caml-list] " Matthieu Dubuget
  0 siblings, 1 reply; 10+ messages in thread
From: Ben Aurel @ 2009-01-08 22:45 UTC (permalink / raw)
  To: caml-list

hi
I'm thinking about the possibility to use OCaml as an extension
language. If I wanted to call a OCaml function from langX I had to use
a C wrapper.

    langX -> ( C API of langX | C Wrapper | C API of OCaml ) -> OCaml

Maybe I make it more complicated than it is. But could somebody tell
me if ( and maybe how ) this can be done. I made some attempts, but
there is always a main() function involved. But this C wrapper would
be a shared library.

Thanks in advance
ben


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Caml-list] Shared libraries with ocamlopt callable from C (without main())?
  2009-01-08 22:45 Shared libraries with ocamlopt callable from C (without main())? Ben Aurel
@ 2009-01-09  8:21 ` Matthieu Dubuget
  2009-01-09  8:44   ` [Caml-list] Shared libraries with ocamlopt callable from C(without main())? RABIH.ELCHAAR
  2009-01-10  1:22   ` [Caml-list] Shared libraries with ocamlopt callable from C (without main())? Ben Jakb
  0 siblings, 2 replies; 10+ messages in thread
From: Matthieu Dubuget @ 2009-01-09  8:21 UTC (permalink / raw)
  To: caml-list

Ben Aurel a écrit :
> I'm thinking about the possibility to use OCaml as an extension
> language. If I wanted to call a OCaml function from langX I had to use
> a C wrapper.
>
>     langX -> ( C API of langX | C Wrapper | C API of OCaml ) -> OCaml
>   

Yes

> But could somebody tell me if ( and maybe how ) this can be done. I made some attempts, but
> there is always a main() function involved. But this C wrapper would be a shared library.
>   

Exactly. You can deliver your OCaml code as shared library, and then
call it from
any language that can call a shared library.

Could you point out what problem you faced?

Salutations

Matt



^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: [Caml-list] Shared libraries with ocamlopt callable from C(without main())?
  2009-01-09  8:21 ` [Caml-list] " Matthieu Dubuget
@ 2009-01-09  8:44   ` RABIH.ELCHAAR
  2009-01-10  1:22   ` [Caml-list] Shared libraries with ocamlopt callable from C (without main())? Ben Jakb
  1 sibling, 0 replies; 10+ messages in thread
From: RABIH.ELCHAAR @ 2009-01-09  8:44 UTC (permalink / raw)
  To: ben.aurel; +Cc: caml-list

The simplest way of achieving what you want might be the following:
1) Write the ocaml code you want to call.
2) register your caml functions via Callback.register
3) In your C code, define the callback values for each ocaml function, via a macro like
#define ACTION(actionname)               \
static value *actionname##_closure = NULL;
and fore each c function that wraps the caml call, call ACTION("camlfunctionname") before your c wrapping code.
4) define an initialization helper (to get the pointers to ocaml functions), something like
#define INITACTION(actionname)                             \
    actionname##_closure = caml_named_value(#actionname);
5) In your DLLMain function, you can have something like
BOOL APIENTRY DllMain(HANDLE module, DWORD reason, void *reserved)
{
  char * argv[2];
  switch (reason) {
  case DLL_PROCESS_ATTACH:
    argv[0] = "--";
    argv[1] = NULL;
    caml_startup(argv);
    INITACTION(analyze)
    INITACTION(updateBlob)
    .....
    break;
  }
  return TRUE;
} 

This handles the C functions:(C Wrapper | C API of OCaml ) -> OCaml defining a C dll with the needed entries.

You can use afterwards your langX -> ( C API of langX -> call above dll).
If your langX can call directly C functions without the need of type conversions, this can do.
If not, this approach might give you two dlls (langX -> C dll) and (C -> ocaml) dll.

If you want only one dll, I think this is only a linking issue, and can be solved by inputing the C->ocaml , compiled ocaml and langX->C together to the linker.

Hope this helps,

Sincerely,
Rabih Chaar

-----Message d'origine-----
De : caml-list-bounces@yquem.inria.fr [mailto:caml-list-bounces@yquem.inria.fr] De la part de Matthieu Dubuget
Envoyé : vendredi 9 janvier 2009 09:22
À : caml-list@inria.fr
Objet : Re: [Caml-list] Shared libraries with ocamlopt callable from C(without main())?

Ben Aurel a écrit :
> I'm thinking about the possibility to use OCaml as an extension
> language. If I wanted to call a OCaml function from langX I had to use
> a C wrapper.
>
>     langX -> ( C API of langX | C Wrapper | C API of OCaml ) -> OCaml
>   

Yes

> But could somebody tell me if ( and maybe how ) this can be done. I made some attempts, but
> there is always a main() function involved. But this C wrapper would be a shared library.
>   

Exactly. You can deliver your OCaml code as shared library, and then
call it from
any language that can call a shared library.

Could you point out what problem you faced?

Salutations

Matt


_______________________________________________
Caml-list mailing list. Subscription management:
http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
Archives: http://caml.inria.fr
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
Bug reports: http://caml.inria.fr/bin/caml-bugs
This message and any attachments (the "message") are confidential, intended solely for the addressee(s), and may contain legally privileged information. 
Any unauthorised use or dissemination is prohibited. 
E-mails are susceptible to alteration. 
Neither Societe Generale Asset Management nor any of its subsidiaries or affiliates shall be liable for the message if altered, changed or falsified. 
  
Find out more about Societe Generale Asset Management's proposal on www.sgam.com
  
                                ******** 
  
Ce message et toutes les pieces jointes (ci-apres le "message") sont confidentiels et susceptibles de contenir des informations couvertes par le secret professionnel.
Ce message est etabli a l'intention exclusive de ses destinataires.
Toute utilisation ou diffusion non autorisee est interdite. 
Tout message electronique est susceptible d'alteration. Societe Generale Asset Management et ses filiales declinent toute responsabilite au titre de ce message s'il a ete altere, deforme ou falsifie. 

Decouvrez l'offre et les services de Societe Generale Asset Management sur le site www.sgam.fr


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Caml-list] Shared libraries with ocamlopt callable from C (without main())?
  2009-01-09  8:21 ` [Caml-list] " Matthieu Dubuget
  2009-01-09  8:44   ` [Caml-list] Shared libraries with ocamlopt callable from C(without main())? RABIH.ELCHAAR
@ 2009-01-10  1:22   ` Ben Jakb
  2009-01-10 11:50     ` Matthieu Dubuget
  2009-01-11 16:27     ` [Caml-list] " Xavier Leroy
  1 sibling, 2 replies; 10+ messages in thread
From: Ben Jakb @ 2009-01-10  1:22 UTC (permalink / raw)
  To: matthieu.dubuget, caml-list

> Could you point out what problem you faced?

I try to create a static library called "libadd5wrapperlib.a" ( I know
kinda WTF ). So here is my simple example:

(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;;


=====  $ cat ./add5wrapperlib.c (Wraps the Ocaml code ) =====
    #include <stdio.h>
    #include <caml/mlvalues.h>
    #include <caml/callback.h>
    int add5wrapper(char **argv) {
        int add_5;
        caml_startup(argv);
        add_5=Int_val(callback(*caml_named_value("add five"),Val_unit));
        return add_5;
}

=====  $ cat ./include/libadd5wrapper.h (header file) =====
    extern int add5 (char **argv);

=====  $ cat ./main.c =====
    #include <stdio.h>
    #include "libadd5wrapper.h"
    int main (int argc,char **argv){
       printf("Gimme - %d \n", add5wrapper());
       return 0;
}

Now I try to BUILD the whole thing:
----------------------------------------------
# 1.) Build the OCaml code
ocamlopt -output-obj add5.ml -o add5-prog.o

# 2.) Create the static library
gcc -c add5wrapperlib.c
ar rs lib/libadd5wrapperlib.a add5wrapperlib.o

# 3.) Create the main object file
gcc -c main.c -Iinclude -I"`ocamlc -where`"

# 4.) Build the whole thing:
gcc --static -Iinclude -Llib -o mainprog.opt main.o add5wrapperlib.o
-L"`ocamlc -where`" -ladd5wrapperlib -lm -ldl -lasmrun

Number 4 is where it breaks. I get a ton of errors and this is where
it ends for me. Because # 4 is also the point where I definitely have
no idea what I'm doing.
Thanks for helping me.

Errors of #4:
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(startup.o): In function
`caml_main':
startup.c:(.text+0x209): undefined reference to `caml_data_segments'
startup.c:(.text+0x21d): undefined reference to `caml_code_segments'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(floats.o): In function
`caml_ceil_float':
floats.c:(.text+0x225): undefined reference to `ceil'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(floats.o): In function
`caml_atan2_float':
floats.c:(.text+0x24b): undefined reference to `atan2'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(floats.o): In function
`caml_atan_float':
floats.c:(.text+0x268): undefined reference to `atan'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(floats.o): In function
`caml_acos_float':
floats.c:(.text+0x285): undefined reference to `acos'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(floats.o): In function
`caml_asin_float':
floats.c:(.text+0x2a2): undefined reference to `asin'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(floats.o): In function
`caml_tanh_float':
floats.c:(.text+0x2bf): undefined reference to `tanh'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(floats.o): In function
`caml_tan_float':
floats.c:(.text+0x2dc): undefined reference to `tan'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(floats.o): In function
`caml_cosh_float':
floats.c:(.text+0x2f9): undefined reference to `cosh'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(floats.o): In function
`caml_cos_float':
floats.c:(.text+0x316): undefined reference to `cos'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(floats.o): In function
`caml_sinh_float':
floats.c:(.text+0x333): undefined reference to `sinh'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(floats.o): In function
`caml_sin_float':
floats.c:(.text+0x350): undefined reference to `sin'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(floats.o): In function
`caml_power_float':
floats.c:(.text+0x376): undefined reference to `pow'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(floats.o): In function
`caml_sqrt_float':
floats.c:(.text+0x3a6): undefined reference to `sqrt'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(floats.o): In function
`caml_log10_float':
floats.c:(.text+0x487): undefined reference to `log10'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(floats.o): In function
`caml_log_float':
floats.c:(.text+0x4a4): undefined reference to `log'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(floats.o): In function
`caml_fmod_float':
floats.c:(.text+0x599): undefined reference to `fmod'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(floats.o): In function
`caml_floor_float':
floats.c:(.text+0x5b6): undefined reference to `floor'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(floats.o): In function
`caml_exp_float':
floats.c:(.text+0x5d3): undefined reference to `exp'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(unix.o): In function
`caml_dlerror':
unix.c:(.text+0x1db): undefined reference to `dlerror'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(unix.o): In function `caml_dlsym':
unix.c:(.text+0x1f5): undefined reference to `dlsym'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(unix.o): In function
`caml_dlclose':
unix.c:(.text+0x208): undefined reference to `dlclose'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(unix.o): In function
`caml_dlopen':
unix.c:(.text+0x223): undefined reference to `dlopen'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(i386.o): In function
`caml_start_program':
(.text+0x129): undefined reference to `caml_program'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(i386.o): In function
`caml_callback2_exn':
(.text+0x221): undefined reference to `caml_apply2'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(i386.o): In function
`caml_callback3_exn':
(.text+0x241): undefined reference to `caml_apply3'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(fail.o): In function
`caml_array_bound_error':
fail.c:(.text+0x75): undefined reference to `caml_exn_Invalid_argument'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(fail.o): In function
`caml_raise_stack_overflow':
fail.c:(.text+0x98): undefined reference to `caml_bucket_Stack_overflow'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(fail.o): In function
`caml_raise_out_of_memory':
fail.c:(.text+0xaa): undefined reference to `caml_bucket_Out_of_memory'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(fail.o): In function
`caml_raise_sys_error':
fail.c:(.text+0x144): undefined reference to `caml_exn_Sys_error'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(fail.o): In function
`caml_invalid_argument':
fail.c:(.text+0x17d): undefined reference to `caml_exn_Invalid_argument'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(fail.o): In function
`caml_failwith':
fail.c:(.text+0x196): undefined reference to `caml_exn_Failure'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(fail.o): In function
`caml_raise_sys_blocked_io':
fail.c:(.text+0x21a): undefined reference to `caml_exn_Sys_blocked_io'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(fail.o): In function
`caml_raise_not_found':
fail.c:(.text+0x22c): undefined reference to `caml_exn_Not_found'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(fail.o): In function
`caml_raise_zero_divide':
fail.c:(.text+0x23e): undefined reference to `caml_exn_Division_by_zero'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(fail.o): In function
`caml_raise_end_of_file':
fail.c:(.text+0x250): undefined reference to `caml_exn_End_of_file'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(roots.o): In function
`caml_init_frame_descriptors':
roots.c:(.text+0x118): undefined reference to `caml_frametable'
roots.c:(.text+0x136): undefined reference to `caml_frametable'
roots.c:(.text+0x18f): undefined reference to `caml_frametable'
roots.c:(.text+0x207): undefined reference to `caml_frametable'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(roots.o): In function
`caml_do_roots':
roots.c:(.text+0x25c): undefined reference to `caml_globals'
roots.c:(.text+0x293): undefined reference to `caml_globals'
/usr/local/godi/lib/ocaml/std-lib/libasmrun.a(roots.o): In function
`caml_oldify_local_roots':
roots.c:(.text+0x364): undefined reference to `caml_globals'
roots.c:(.text+0x36b): undefined reference to `caml_globals'
collect2: ld returned 1 exit status















2009/1/9 Matthieu Dubuget <matthieu.dubuget@gmail.com>:
> Ben Aurel a écrit :
>> I'm thinking about the possibility to use OCaml as an extension
>> language. If I wanted to call a OCaml function from langX I had to use
>> a C wrapper.
>>
>>     langX -> ( C API of langX | C Wrapper | C API of OCaml ) -> OCaml
>>
>
> Yes
>
>> But could somebody tell me if ( and maybe how ) this can be done. I made some attempts, but
>> there is always a main() function involved. But this C wrapper would be a shared library.
>>
>
> Exactly. You can deliver your OCaml code as shared library, and then
> call it from
> any language that can call a shared library.
>
> Could you point out what problem you faced?
>
> Salutations
>
> Matt
>
>
> _______________________________________________
> Caml-list mailing list. Subscription management:
> http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
> Archives: http://caml.inria.fr
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
>


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Caml-list] Shared libraries with ocamlopt callable from C (without main())?
  2009-01-10  1:22   ` [Caml-list] Shared libraries with ocamlopt callable from C (without main())? Ben Jakb
@ 2009-01-10 11:50     ` Matthieu Dubuget
  2009-01-10 15:28       ` Ben Jakb
       [not found]       ` <200901101911.41594.monnier.florent@gmail.com>
  2009-01-11 16:27     ` [Caml-list] " Xavier Leroy
  1 sibling, 2 replies; 10+ messages in thread
From: Matthieu Dubuget @ 2009-01-10 11:50 UTC (permalink / raw)
  To: caml-list; +Cc: Ben Jakb

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


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Caml-list] Shared libraries with ocamlopt callable from C (without main())?
  2009-01-10 11:50     ` Matthieu Dubuget
@ 2009-01-10 15:28       ` Ben Jakb
  2009-01-10 15:36         ` Matthieu Dubuget
       [not found]       ` <200901101911.41594.monnier.florent@gmail.com>
  1 sibling, 1 reply; 10+ messages in thread
From: Ben Jakb @ 2009-01-10 15:28 UTC (permalink / raw)
  To: matthieu.dubuget, caml-list

matthieu,
thanks for the interesting answer.
Unfortunately I can't build 'mycamlbuild.ml', and it is a bit too
advanced for me to understand.

Therefore I'll first go with your standard build commands. So - after
modifing the sources according to your suggestions I run:

BUILD
    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=.

EXECUTION
    cp add5lib.native.so  lib/
    ./maintest.native

ERROR
"maintest.native: add5wrapperlib.c:24: add5wrapper: Assertion `cbk' failed.
Aborted"

This is the referenced line:

    $ sed -n 24p add5wrapperlib.c
CAMLCBK_INIT(cbk, "add_five");


Any ideas what' wrong here?





2009/1/10 Matthieu Dubuget <matthieu.dubuget@gmail.com>:
> 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
>


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Caml-list] Shared libraries with ocamlopt callable from C (without main())?
  2009-01-10 15:28       ` Ben Jakb
@ 2009-01-10 15:36         ` Matthieu Dubuget
  2009-01-10 18:08           ` Ben Jakb
  0 siblings, 1 reply; 10+ messages in thread
From: Matthieu Dubuget @ 2009-01-10 15:36 UTC (permalink / raw)
  To: caml-list; +Cc: Ben Jakb

Ben Jakb a écrit :
> matthieu,
> thanks for the interesting answer.
> Unfortunately I can't build 'mycamlbuild.ml', and it is a bit too
> advanced for me to understand.

If you have a > 3.10 version of ocaml, you don't need to compile
yourself myocamlbuild.ml.

This is done automatically by ocamlbuild.

> 
> Therefore I'll first go with your standard build commands. So - after
> modifing the sources according to your suggestions I run:
> 
> BUILD
>     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=.
> 
> EXECUTION
>     cp add5lib.native.so  lib/
>     ./maintest.native
> 
> ERROR
> "maintest.native: add5wrapperlib.c:24: add5wrapper: Assertion `cbk' failed.
> Aborted"
> 
> This is the referenced line:
> 
>     $ sed -n 24p add5wrapperlib.c
> CAMLCBK_INIT(cbk, "add_five");
> 
> 
> Any ideas what' wrong here?

Yes ;-)

This is exactly why i use assert there.

I introduced a bug: the string in the C file is "add_five", while the
registered one in the ml file is "add five".


Hope this helps


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Caml-list] Shared libraries with ocamlopt callable from C (without main())?
  2009-01-10 15:36         ` Matthieu Dubuget
@ 2009-01-10 18:08           ` Ben Jakb
  0 siblings, 0 replies; 10+ messages in thread
From: Ben Jakb @ 2009-01-10 18:08 UTC (permalink / raw)
  To: matthieu.dubuget, caml-list

> I introduced a bug: the string in the C file is "add_five", while the
> registered one in the ml file is "add five".

Touché. I suppose I looked to long to this code
And yes it works now, thanks ...

2009/1/10 Matthieu Dubuget <matthieu.dubuget@gmail.com>:
> Ben Jakb a écrit :
>> matthieu,
>> thanks for the interesting answer.
>> Unfortunately I can't build 'mycamlbuild.ml', and it is a bit too
>> advanced for me to understand.
>
> If you have a > 3.10 version of ocaml, you don't need to compile
> yourself myocamlbuild.ml.
>
> This is done automatically by ocamlbuild.
>
>>
>> Therefore I'll first go with your standard build commands. So - after
>> modifing the sources according to your suggestions I run:
>>
>> BUILD
>>     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=.
>>
>> EXECUTION
>>     cp add5lib.native.so  lib/
>>     ./maintest.native
>>
>> ERROR
>> "maintest.native: add5wrapperlib.c:24: add5wrapper: Assertion `cbk' failed.
>> Aborted"
>>
>> This is the referenced line:
>>
>>     $ sed -n 24p add5wrapperlib.c
>> CAMLCBK_INIT(cbk, "add_five");
>>
>>
>> Any ideas what' wrong here?
>
> Yes ;-)
>
> This is exactly why i use assert there.
>
> I introduced a bug: the string in the C file is "add_five", while the
> registered one in the ml file is "add five".
>
>
> Hope this helps
>


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Shared libraries with ocamlopt callable from C (without main())?
       [not found]       ` <200901101911.41594.monnier.florent@gmail.com>
@ 2009-01-10 18:18         ` Ben Jakb
  0 siblings, 0 replies; 10+ messages in thread
From: Ben Jakb @ 2009-01-10 18:18 UTC (permalink / raw)
  To: Florent Monnier, caml-list

> Can I leech this example to include it in my small ocaml/C wrapping tutorial ?
> http://www.linux-nantes.org/%7Efmonnier/OCaml/ocaml-wrapping-c.php

this would be great (imho). Your articles where the starting point for
my attempts.


2009/1/10 Florent Monnier <monnier.florent@gmail.com>:
> Hi,
> Can I leech this example to include it in my small ocaml/C wrapping tutorial ?
> http://www.linux-nantes.org/%7Efmonnier/OCaml/ocaml-wrapping-c.php
>
> If so, do you want that your name appears or not or you don't mind ?
>
> Cheers
> Florent
> --
> Matthieu Dubuget a écrit :
>> 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
>>
>> _______________________________________________
>> Caml-list mailing list. Subscription management:
>> http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
>> Archives: http://caml.inria.fr
>> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
>> Bug reports: http://caml.inria.fr/bin/caml-bugs
>


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Caml-list] Shared libraries with ocamlopt callable from C (without main())?
  2009-01-10  1:22   ` [Caml-list] Shared libraries with ocamlopt callable from C (without main())? Ben Jakb
  2009-01-10 11:50     ` Matthieu Dubuget
@ 2009-01-11 16:27     ` Xavier Leroy
  1 sibling, 0 replies; 10+ messages in thread
From: Xavier Leroy @ 2009-01-11 16:27 UTC (permalink / raw)
  To: Ben Jakb; +Cc: matthieu.dubuget, caml-list

> =====  $ cat ./main.c =====
>     #include <stdio.h>
>     #include "libadd5wrapper.h"
>     int main (int argc,char **argv){
>        printf("Gimme - %d \n", add5wrapper());

Should be add5wrapper(argv) -- as gcc's warnings told you.

>        return 0;
> }
> 
> Now I try to BUILD the whole thing:
> [...]
> Number 4 is where it breaks. I get a ton of errors and this is where
> it ends for me. Because # 4 is also the point where I definitely have
> no idea what I'm doing.

Two things:

- You're not linking in add5-prog.o as far as I can see

- In static mode, the Unix linker is very picky about the relative
  order of -lxxx arguments on the command-line.  For more information,
  see the Info pages for GNU ld.  You probably don't need -static anyway.

The following works:

ocamlopt -output-obj add5.ml -o add5-prog.o
gcc -I`ocamlc -where` -c add5wrapperlib.c
gcc -c main.c
gcc -o mainprog.opt main.o add5wrapperlib.o add5-prog.o \
       -L`ocamlc -where` -lasmrun -ldl -lm

Add "-static" to the last line if you know you really need it.

Hope this puts you back on tracks.

- Xavier Leroy


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2009-01-11 16:28 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-01-08 22:45 Shared libraries with ocamlopt callable from C (without main())? Ben Aurel
2009-01-09  8:21 ` [Caml-list] " Matthieu Dubuget
2009-01-09  8:44   ` [Caml-list] Shared libraries with ocamlopt callable from C(without main())? RABIH.ELCHAAR
2009-01-10  1:22   ` [Caml-list] Shared libraries with ocamlopt callable from C (without main())? Ben Jakb
2009-01-10 11:50     ` Matthieu Dubuget
2009-01-10 15:28       ` Ben Jakb
2009-01-10 15:36         ` Matthieu Dubuget
2009-01-10 18:08           ` Ben Jakb
     [not found]       ` <200901101911.41594.monnier.florent@gmail.com>
2009-01-10 18:18         ` Ben Jakb
2009-01-11 16:27     ` [Caml-list] " Xavier Leroy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).