2012-04-20 11 views
8

Estoy tratando de compilar un programa utilizando fuentes mixtas C y Ocaml, con el principal de la aplicación en C llamando a algunas partes del código OCaml. Muy bien, no hay problema aquí, parece ser una operación común, fully documented, fácil de hacer con las herramientas estándar de Ocaml.Incrustar OCaml en C: error de vinculación

Permítanme explicarles un poco, este tipo de compilación se divide en 4 pasos: compilación de Caml para objetos Caml, luego compilación de los objetos Caml a C, luego compilación de los archivos C y finalmente compilación de todos los objetos C para obtener el ejecutable.

La teoría es, el compilador Ocaml va a incrustar el tiempo de ejecución de Caml, GC, y todas sus cosas de forma automática, y sólo tenemos que indicar si utilizamos cualquiera que sea el ocaml código de bytes (referencia -lcamlrun) o el binario nativo (referenciando -lasmrun) .

Por lo tanto, parece ser bastante simple, hagámoslo. Los pasos 1, 2 y 3 fueron como se esperaba, ¡bien! Solo el 4 ° paso es problemático. Basta con echar un vistazo:

cc -o /home/thomas/Documents/projects/ocaml/GogoGame/bin/GogoPlayer.exe \ 
     -L/usr/lib/ocaml -lcamlrun \ 
     /home/thomas/Documents/projects/ocaml/GogoGame/src/init_camlcode.o \ 
      /home/thomas/Documents/projects/ocaml/GogoGame/src/init.o \ 
      /home/thomas/Documents/projects/ocaml/GogoGame/src/interface.o \ 
      /home/thomas/Documents/projects/ocaml/GogoGame/src/caml_func.oo 
/home/thomas/Documents/projects/ocaml/GogoGame/src/interface.o: In function `main': 
interface.c:(.text+0x0): multiple definition of `main' 
/usr/lib/ocaml/libcamlrun.a(main.o):(.text+0x0): first defined here 
/usr/lib/ocaml/libcamlrun.a(floats.o): In function `caml_exp_float': 
(.text+0x488): undefined reference to `exp' 
/usr/lib/ocaml/libcamlrun.a(floats.o): In function `caml_fmod_float': 
(.text+0x4f9): undefined reference to `fmod' 
/usr/lib/ocaml/libcamlrun.a(floats.o): In function `caml_log_float': 
(…) 
/usr/lib/ocaml/libcamlrun.a(unix.o): In function `caml_dlopen': 
(.text+0x2ed): undefined reference to `dlopen' 
/usr/lib/ocaml/libcamlrun.a(unix.o): In function `caml_dlclose': 
(.text+0x300): undefined reference to `dlclose' 
/usr/lib/ocaml/libcamlrun.a(unix.o): In function `caml_dlsym': 
(.text+0x31b): undefined reference to `dlsym' 
/usr/lib/ocaml/libcamlrun.a(unix.o): In function `caml_dlerror': 
(.text+0x342): undefined reference to `dlerror' 
/home/thomas/Documents/projects/ocaml/GogoGame/src/init_camlcode.o:(.data.rel+0xc): undefined reference to `caml_array_get_addr' 
/home/thomas/Documents/projects/ocaml/GogoGame/src/init_camlcode.o:(.data.rel+0x10): undefined reference to `caml_array_get_float' 
(...) 
/home/thomas/Documents/projects/ocaml/GogoGame/src/init_camlcode.o:(.data.rel+0x31c): undefined reference to `caml_lazy_make_forward' 
/home/thomas/Documents/projects/ocaml/GogoGame/src/init_camlcode.o:(.data.rel+0x320): undefined reference to `caml_get_public_method' 
/home/thomas/Documents/projects/ocaml/GogoGame/src/init_camlcode.o:(.data.rel+0x3ac): undefined reference to `caml_terminfo_setup' 
/home/thomas/Documents/projects/ocaml/GogoGame/src/init_camlcode.o:(.data.rel+0x3b0): undefined reference to `caml_terminfo_backup' 
/home/thomas/Documents/projects/ocaml/GogoGame/src/init_camlcode.o:(.data.rel+0x3b4): undefined reference to `caml_terminfo_standout' 
/home/thomas/Documents/projects/ocaml/GogoGame/src/init_camlcode.o:(.data.rel+0x3b8): undefined reference to `caml_terminfo_resume' 
/home/thomas/Documents/projects/ocaml/GogoGame/src/init.o: In function `camlInit__f_1030': 
(.text+0xc): undefined reference to `camlPervasives' 
/home/thomas/Documents/projects/ocaml/GogoGame/src/init.o: In function `camlInit__f_1030': 
(.text+0x11): undefined reference to `camlPervasives__output_string_1191' 
/home/thomas/Documents/projects/ocaml/GogoGame/src/init.o: In function `camlInit__f_1030': 
(.text+0x19): undefined reference to `camlPervasives__string_of_int_1130' 
/home/thomas/Documents/projects/ocaml/GogoGame/src/init.o: In function `camlInit__f_1030': 
(.text+0x20): undefined reference to `camlPervasives' 
/home/thomas/Documents/projects/ocaml/GogoGame/src/init.o: In function `camlInit__f_1030': 
(.text+0x25): undefined reference to `camlPervasives__output_string_1191' 
/home/thomas/Documents/projects/ocaml/GogoGame/src/init.o: In function `camlInit__format_result_1034': 
(.text+0x9c): undefined reference to `camlPrintf__sprintf_1414' 
/home/thomas/Documents/projects/ocaml/GogoGame/src/init.o: In function `camlInit__entry': 
(.text+0xe1): undefined reference to `caml_c_call' 
/home/thomas/Documents/projects/ocaml/GogoGame/src/init.o: In function `camlInit__entry': 
(.text+0xfb): undefined reference to `caml_c_call' 
/home/thomas/Documents/projects/ocaml/GogoGame/src/init.o: In function `camlInit__entry': 
(.text+0x115): undefined reference to `caml_c_call' 
/home/thomas/Documents/projects/ocaml/GogoGame/src/init.o: In function `camlInit__f_1030': 
(.text+0x32): undefined reference to `camlPervasives__print_newline_1276' 
collect2: ld returned 1 exit status 
make: *** [GogoPlayer] Error 1 

en mi humilde opinión, parece que hay dos errores:

  • múltiples definiciones de main
  • El enlazador no encuentra el módulo Pervasive

Realmente no tengo idea de cómo solucionarlo, tal vez tengo que vincular otro archivo. ¿Alguien tiene una idea?

Como le pregunté, puse el código que da estos errores. Será bastante simple porque hay muy poco código, la mayoría se dio en un ejemplo en la documentación.

init.ml

let f x = print_string "f is applied to "; print_int x; print_newline() 
let rec fib n = if n < 2 then 1 else fib(n-1) + fib(n-2) 
let format_result n = Printf.sprintf "Result is: %d\n" 

let _ = 
    Callback.register "Arbitrary Name" f; 
    Callback.register "fib" fib; 
    Callback.register "format_result" format_result 

caml_func.c

#include <stdio.h> 
#include <string.h> 
#include <caml/mlvalues.h> 
#include <caml/callback.h> 

void call_caml_f(int x) 
{ 
    static value * closure_f = NULL; 
    if (closure_f == NULL) /* First time around, look up by name */ 
     closure_f = caml_named_value("Arbitrary Name"); 

    caml_callback(*closure_f, Val_int(x)); 
} 

int fib(int n) 
{ 
    static value * fib_closure = NULL; 
    if (fib_closure == NULL) fib_closure = caml_named_value("fib"); 
    return Int_val(caml_callback(*fib_closure, Val_int(n))); 
} 

char * format_result(int n) 
{ 
    static value * format_result_closure = NULL; 
    if (format_result_closure == NULL) 
     format_result_closure = caml_named_value("format_result"); 
    return strdup(String_val(caml_callback(*format_result_closure, Val_int(n)))); 
    /* We copy the C string returned by String_val to the C heap 
    so that it remains valid after garbage collection. */ 
} 

interface.c

#include <stdio.h> 
#include "caml_func.c" 

#define BYTECODE 

int main(int argc, char **argv) 
{ 
#ifdef BYTECODE 
    caml_startup(argv); 
#else 
    caml_main(argv); 
#endif 

    /* Make sure that stdout is not block buffered. */ 
    setbuf(stdout, NULL); 

    /* Process GTP commands. */ 
    //gtp_main_loop(commands, stdin, NULL); 
    // CAML code here ? 

    return 0; 
} 

Y esto es todo. Obviamente, me salté todas las cosas sin sentido ya que este simple ejemplo debería funcionar y no es así. Debe ser mi Makefile, que sigue.

Por cierto, es bastante feo. Si tiene propuestas para este tipo de aplicación (Caml dentro de C), o sugerencias de refactorización, las tomaré todas.

Makefile.ocaml

.PHONY: all clean mrproper 

# RULES and EXEC are magically set in Makefile.magic 
all: depend $(RULES) $(EXE) 
    @echo [DONE] 

mli: $(CAML_ONLY:.ml=.mli) 
ml-byte: $(CAML_ONLY:.ml=.cmo) 
ml-called-byte: $(CAML_CALLED_BY_C:.ml=.$(OBJ)) 
ml-nativ: $(CAML_ONLY:.ml=.cmx) 
ml-called-nativ: $(CAML_CALLED_BY_C:.ml=.$(OBJ)) 
c-wrapper: $(C_WRAPPERS:.c=.oo) 
c-only: $(C_ONLY:.c=.o) 

$(EXE): 
    $(CC) -o $(BIN)/$(EXE).exe \ 
     $(FLAGS) \ 
     -L$(OCAMLLIB) $(LINKED) -l$(RUNLIB) \ 
     $(wildcard $(SRC)/*.$(OBJ)) $(wildcard $(SRC)/*.oo) # */ 

%.o: %.c 
    $(CC) $(FLAGS_C) -c $< -o $(SRC)/$(*F).o 
%.mli: %.ml 
    $(OCAMLC) $(FLAGS_ML) -i $< > $(SRC)/$(*F).mli 
%.cmi: %.mli 
    $(OCAMLC) $(FLAGS_ML) -c $< -o $(SRC)/$(*F).cmi 
%.cmo: %.ml 
    $(CAMLC) $(FLAGS_ML) -c $< -o $(SRC)/$(*F).cmo 
%.cmx: %.ml 
    $(CAMLOPT) $(FLAGSOPT) -c $< -o $(SRC)/$(*F).cmx 
# native 
%.o: %.ml 
    $(cd $(SRC)) 
    $(OCAMLC) -output-obj -o $(*F)_camlcode.o \ 
     $(FLAGS_MLC) \ 
     $< 
# bytecode 
%.ob: %.ml 
    $(cd $(SRC)) 
    $(OCAMLOPT) -output-obj -o $(*F)_camlcode.ob \ 
     $(FLAGS_MLC) \ 
     $< 
%.oo: %.c 
    $(CC) $(FLAGS_WRAP) -c $< -o $(SRC)/$(*F).oo 


clean_mli: 
    rm -f $(SRC)/*.mli # */ 
clean: 
    rm -f $(BIN)/*.{a,o,oo,cmi,cmo,cmx} # */ 
    rm -f $(SRC)/*.{a,o,oo,cmi,cmo,cmx} # */ 

mrproper: clean, clean_mli 
    rm -f $(BIN)/$(EXE) 

depend: 
    $(OCAMLDEP) $(INCLUDES) $(SRC)/*.ml $(SRC)/*.mli > .depend # */ 

include .depend 
+0

creo que usted no desea vincular con stdlib.a (como debe ser cargado por defecto por -lasmrun y -lcamlrun) – Thomas

+0

Sí, tienes razón, era un registro de una compilación de prueba. Eliminado –

+0

Es bastante difícil de depurar sin ver su código. ¿Tienes un pequeño ejemplo que reproduce el error? – Thomas

Respuesta

6

Su comando de enlace es incorrecta de dos maneras:

  1. Es necesario vincular con -ldl para dlopen, etc.
  2. usted debe poner bibliotecas después objetos que los referencian (es decir, sus argumentos -lcamlrun están en th el lugar equivocado en la línea del enlace). El orden de los argumentos en la línea de enlace matters.