2012-04-20 20 views
8

Próbuję skompilować program używając mieszanych źródeł C i Ocaml, z główną aplikacją w C wywołującą niektóre fragmenty kodu OCaml. W porządku, nie ma problemu, Wydaje się, że jest to typowa operacja, łatwa w użyciu ze standardowymi narzędziami Ocaml.Osadzanie OCaml w C: linkowanie błędu

Pozwolę sobie wyjaśnić, że ten rodzaj kompilacji jest podzielony na 4 etapy: Caml kompiluje do obiektów Caml, następnie kompiluje obiekty Caml do C, następnie kompiluje pliki C, a na końcu kompiluje wszystkie obiekty C razem i robi plik wykonywalny.

W teorii jest to, że kompilator Ocaml osadzi runtime Caml, GC i wszystkie swoje rzeczy automatycznie, a my po prostu trzeba wskazać, czy korzystamy z pełnym poszanowaniem kodu bajtowego ocaml (przedstawieniu -lcamlrun) lub rodzimy binarny (przedstawieniu -lasmrun) .

Wydaje się więc dość proste, zróbmy to. Kroki 1, 2 i 3 przebiegły zgodnie z oczekiwaniami, dobrze! Tylko czwarty krok jest problematyczny. Wystarczy spojrzeć:

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 

IMHO, wydaje się, że jest dwa błędy:

  • Wielokrotne definicje main
  • Linker nie znajdzie modułu Pervasive

Naprawdę nie mam pojęcia, jak to naprawić, może muszę połączyć inny plik. Czy ktoś ma pomysł?

Zgodnie z zapytaniem umieszczam kod, który podaje te błędy. Będzie to dość proste, ponieważ jest bardzo mało kodu, większość została podana w przykładzie w dokumentacji.

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

I to wszystko. Oczywiście pominąłem wszystkie nic nie znaczące rzeczy, jak ten prosty przykład powinien działać i nie działa. To musi być mój Makefile, który następuje.

Przy okazji, to jest dość brzydkie. Jeśli masz propozycje dla tego rodzaju aplikacji (Caml wewnątrz C) lub sugestie refaktoryzacji, wezmę je wszystkie.

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

Myślę, że nie chcesz łączyć ze stdlib.a (jak powinno być ładowane domyślnie przez -lasmrun i -lcamlrun) – Thomas

+0

Tak, masz rację, to był dziennik testowej wersji. Usunięto. –

+0

Dosyć trudno jest debugować bez wyświetlania kodu. Czy masz mały przykład, który odtwarza błąd? – Thomas

Odpowiedz

6

Twój komenda link jest niepoprawny na dwa sposoby:

  1. trzeba połączyć z -ldl dla dlopen itp
  2. Musisz umieścić bibliotekom po obiekty, które się do nich odwołują (czyli twoje argumenty -lcamlrun są w th niewłaściwe miejsce w linii łącza). Kolejność argumentów na linii łącza matters.