OCaml C interface undefined reference to "caml_local_roots"

Hi !

I’m trying to compile a very simple C++ project for OCaml. Here is what I did so far:

#include <iostream>
#include <string>

#include "osm.hpp"

extern "C" {
    #include <caml/mlvalues.h>
    #include <caml/memory.h>
    #include <caml/alloc.h>
    #include <caml/custom.h>
}

extern "C"
CAMLprim value osm_from_file(value file)
{
    CAMLparam1 (file);
    std::string fileName = String_val(file);

    std::cout << fileName << std::endl;

    CAMLreturn (Val_unit);
}

extern "C"
CAMLprim value osm_read_data(value osmObj)
{
    CAMLparam1 (osmObj);

    std::cout << "Testing" << std::endl;

    CAMLreturn (Val_unit);
}

I’m compiling it using CMake for the “osm” little class I’m writing and Dune to link it with my OCaml project.

Here is what I get while building my project with Dune.

$>> dune build
    ocamlopt project/bin/main.exe (exit 2)
(cd _build/default && /home/user/.opam/4.12.0/bin/ocamlopt.opt -w @1..3@5..28@30..39@43@46..47@49..57@61..62-40 -strict-sequence -strict-formats -short-paths -keep-locs -g -o project/bin/main.exe project/lib/project.cmxa project/bin/osm/cosm.cmxa -I project/bin/osm project/bin/.main.eobjs/native/dune__exe.cmx project/bin/.main.eobjs/native/dune__exe__Map.cmx project/bin/.main.eobjs/native/dune__exe__Main.cmx -linkall -cclib -lstdc++)
/usr/bin/ld: project/bin/osm/libbinding.a(binding.cpp.o): in function `osm_from_file':
binding.cpp:(.text+0x55e): undefined reference to `caml_local_roots'
/usr/bin/ld: binding.cpp:(.text+0x56c): undefined reference to `caml_local_roots'
/usr/bin/ld: binding.cpp:(.text+0x57b): undefined reference to `caml_local_roots'
/usr/bin/ld: binding.cpp:(.text+0x61f): undefined reference to `caml_local_roots'
/usr/bin/ld: project/bin/osm/libbinding.a(binding.cpp.o): in function `osm_read_data':
binding.cpp:(.text+0x6ba): undefined reference to `caml_local_roots'
/usr/bin/ld: project/bin/osm/libbinding.a(binding.cpp.o):binding.cpp:(.text+0x6c5): more undefined references to `caml_local_roots' follow
collect2: error: ld returned 1 exit status
File "caml_startup", line 1:
Error: Error during linking (exit code 1)

caml_local_roots should be defined in “caml/memory.h” but it looks like there is a problem while linking this file with my project.

Here is my Dune file to compile the “osm” library.

(library
    (name cosm)
    (c_library_flags :standard -lstdc++ -lbz2 -lexpat -lz -lpthread)
    (foreign_archives binding)
)

(rule
    (deps (source_tree osm))
    (targets libbinding.a dllbinding.so)
    (action
        (no-infer
            (progn
                (chdir osm (run make))
                (copy osm/libbindingsh.so dllbinding.so)
                (copy osm/libbinding.a libbinding.a)
))))

Does anyone has an idea on how can I solve this problem ?

It looks like you need to link with libasmrun.a (the runtime system).

Typically this should be taken care for you if you build your stubs using the (foreign_stubs ...) field, see General concepts — dune documentation.

Cheers,
Nicolás

1 Like

Hi !

Thanks for your reply. I’m using the foreign_archives stanza because I need CMake in order to compile my library (it’s using external C++ dependencies).

Even when adding the -lasmrun flag, I still got the same error.

Do you have an idea how can I “reproduce” the same behavior of foreign_stubs with the foreign_library ? Or maybe foreign_stubs is usable with an external build system like CMake but I saw nothing on the doc ?

If I understand correctly you have a C++ library, osm, that you build with CMake, and also some C bindings that are currently being built together with the C++ library, so that libbinding.a contains both the C++ library and the bindings. The idea would be to continue to use foreign_archive to build the osm library, but move the bindings to the OCaml library using foreign_stubs, something like:

(library
 (name cosm)
 (c_library_flags ...)
 (foreign_stubs (language c) (names binding))
 (foreign_archives osm))

(rule
 (deps (source_tree osm))
 (target libosm.a dllosm.so)
 (action (progn ...)))

Cheers,
Nicolás

1 Like

It looks like the bindings.cpp file is still inside the osm directory and CMake is trying to “build” it. You should take it out of that directory and move it to the parent directory (where the cosm OCaml library lives). The build of the osmium library has nothing to do with that of the bindings, so there is no reason to keep them in the same directory.

Cheers,
Nicolás

1 Like

This is not a problem compiling osm.cpp but binding.cpp. You need to pass the required include directories in the (foreign_stubs ...) using (include_dirs ...), eg

(foreign_stubs (language cxx) (include_dirs osm) (names binding))

If you do this you will need to change your #include "osm/mapping/osp.hpp" to #include "mapping/osp.hpp" inside binding.cpp because dune will pass -I osm when compiling it.

Cheers,
Nicolás

2 Likes

It worked, thanks a lot @nojb for your help and the time you took.

Have an excellent day !