Separate compilation of OCaml modules - ocaml

I have read this question and others,
but my compile problem is unsolved.
I am testing separate compilation with these files:
testmoda.ml
module Testmoda = struct
let greeter () = print_endline "greetings from module a"
end
testmodb.ml
module Testmodb = struct
let dogreet () = print_endline "Modul B:"; Testmoda.greeter ()
end
testmod.ml
let main () =
print_endline "Calling modules now...";
Testmoda.greeter ();
Testmodb.dogreet ();
print_endline "End."
;;
let _ = main ()
Now I generate the .mli file
ocamlc -c -i testmoda.ml >testmoda.mli
and the testmoda.cmi is there.
Next I create the .cmo file without errors:
ocamlc -c testmoda.ml
Fine, so do the same with testmodb.ml:
strobel#s131-amd:~/Ocaml/ml/testmod> ocamlc -c -i testmodb.ml >testmodb.mli
File "testmodb.ml", line 3, characters 45-61:
Error: Unbound value Testmoda.greeter
Another try:
strobel#s131-amd:~/Ocaml/ml/testmod> ocamlc -c testmoda.cmo testmodb.ml
File "testmodb.ml", line 3, characters 45-61:
Error: Unbound value Testmoda.greeter
Other combinations failed as well.
How do I compile testmodb.ml and testmod.ml? This should be easy - without ocamlbuild / omake /
oasis, I think.
Syntax errors in the files are excluded,
if I cat them together to one file (with the required space between) it compiles
and executes perfectly.

OCaml gives you a module for free at the top level of each source file. So your first module is actually named Testmoda.Testmoda, the function is named Testmoda.Testmoda.greeter, and so on. Things will work better if your files just contain the function definitions.
As a side comment, if you're going to use the interface generated by ocamlc -i, you really don't need mli files. The interface in the absence of an mli file is the same as the one generated by ocamlc -i. If you don't want the default interface, using ocamlc -i gives a good starting point for your mli file. But for a simple example like this, it just makes things look a lot more complicated than they really are (IMHO).
If you modify your files as I describe (remove extra module declarations), you can compile and run from scratch as follows:
$ ls
testmod.ml testmoda.ml testmodb.ml
$ cat testmoda.ml
let greeter () = print_endline "greetings from module a"
$ cat testmodb.ml
let dogreet () = print_endline "Modul B:"; Testmoda.greeter ()
$ ocamlc -o testmod testmoda.ml testmodb.ml testmod.ml
$ ./testmod
Calling modules now...
greetings from module a
Modul B:
greetings from module a
End.
If you have already compiled a file (with ocamlc -c file.ml) you can replace .ml with .cmo in the above command. This works even if all the filenames are .cmo files; in that case ocamlc just links them together for you.

Related

How to compile OCaml and C/C++ code that depend on each other

I'm having problems defining the signature of a C void function that accepts an uint64_t and a char*. I tried int64 -> string -> _.
I also don't know how to compile my C++ file (with C interface) together
events.ml
open Lwt.Infix
external call: int64 -> string -> _ = "function_pointer_caller"
let begin_event pointer =
Lwt_unix.sleep 5.0 >>= fun () ->
call pointer "message"
let () = Callback.register "begin_event" begin_event
interface.c:
#include <stdio.h>
#include <string.h>
#include <caml/mlvalues.h>
#include <caml/callback.h>
#include <caml/alloc.h>
#include <caml/bigarray.h>
extern void register_function_callback();
void print_from_event(char* message) {
printf("OCaml event: %s\n", message);
}
void function_pointer_caller(uint64_t pointer, char* message)
{
void (*f)(char *);
f = pointer;
}
void register_function_callback() {
static const value *begin_event_closure = NULL;
if (begin_event_closure == NULL)
{
begin_event_closure = caml_named_value("begin_event");
if (begin_event_closure == NULL)
{
printf("couldn't find OCaml function\n");
exit(1);
}
}
uint64_t pointer = (uint64_t) &function_pointer_caller;
caml_callback(*begin_event_closure, (int64_t) &pointer);
}
main.cc
#include <stdio.h>
#include <caml/callback.h>
extern "C" void register_function_callback();
int main(int argc, char **argv)
{
caml_startup(argv);
register_function_callback();
while (true)
{
}
return 0;
}
I think there's no way to compile a .cc together with a .ml, because a .cc does not necessairly have a C interface. Maybe it's possible to compile the .ml to a .so object and link it to the .cc with the C interface?
Anyways, I did change interface.cc to interface.c and added interface.c to the ocamlopt command:
ocamlfind ocamlopt -o s -linkpkg -package lwt.unix -thread event_emitter.ml interface.c
g++ -o event_emitter_program -I $(ocamlopt -where) \
s.o interface.o main.cc event_emitter.o $(ocamlopt -where)/libasmrun.a -ldl
The first command compiles ok, but g++ gives
event_emitter.o: In function `camlEvent_emitter__begin_event_90':
:(.text+0x3f): undefined reference to `camlLwt_unix__sleep_695'
:(.text+0x4c): undefined reference to `camlLwt__bind_1276'
collect2: error: ld returned 1 exit status
Notice that I plug the interface.o from the previous command (ocamlopt) and link in g++, because ocamlopt actually passes C files to a C compiler.
I don't know why it complains about Lwt_unix things, since I already compiled with them in ocamlopt.
The ocamlopt utility is doing a bit more than just linking the specified compilation units and libraries, it also embeds the architecture startup code, which is not included in the asmrun library. It is possible to obtain this code using the -output-obj, but I find it a little bit error-prone and hard to maintain as it is not really composable1.
So, it is better to rely on ocamlopt to build the final binary, which will consist of modules written in different languages. Each unit will be built with the appropriate to the language, in which it is written, tool. Let's build the events.ml compilation unit:
ocamlfind ocamlopt -c -package lwt.unix events.ml
Now, let's build the interface (we can use cc here, but using ocamlopt will save us a bit of hassle of providing the location of the includes)
ocamlfind ocamlopt -c interface.c
Now, let's build the C++ part (but first fix it and use caml_main instead of caml_startup since we want the native runtime).
g++ -c main.cc -o main.o -I `ocamlc -where`
Now, when we have all units we're ready for the final linking command:
ocamlfind ocamlopt events.cmx interface.o main.o -package lwt.unix -package lwt -thread -linkpkg -o emitter
Et voila, we can run our program with ./emitter.
1) using this option for more than one compilation unit may easily lead to symbol conflicts.
You should be more careful with “warning” messages and not rely on things that bind the building process to your configuration. Avoid usage of root console please!
[WARNING] Running as root is not recommended
In the root environment path to libraries can be complicated.
According to your pastebin I see that linker is unable to locate the lwt_unix_stubs library. And don't mix '.a' and '.so' libraries please (/root/.opam/4.10.0/lib/lwt/unix/liblwt_unix_stubs.a /root/.opam/4.10.0/lib/stublibs/dlllwt_unix_stubs.so)! '.a' is for static build while '.so' is for dynamic.
Example of build that works for me (Ubuntu Linux, ocaml 4.05.0):
ocamlfind ocamlopt -output-obj -dstartup -linkpkg -package lwt.unix -thread -o s.o event_emitter.ml
g++ -o event_emitter_program -I`ocamlfind c -where` -I $(ocamlopt -where) s.o interface.o main.cc -L `ocamlc -where` -L`ocamlfind query lwt.unix` -L`ocamlfind query threads` -lasmrun -llwt_unix_stubs -lunix -lbigarray -lcamlstr -lthreadsnat -lpthread -ldl
Usage of $(ocamlopt -where)/libasmrun.a is bad idea. It's more pedantic way to use -lasmrun and -L for path to libraries.
If you have problems with some undefined symbols, it's best to use readelf to find out which library is missing.

how to chose between two implementation code in OCaml

I have uploaded the very small exemple project on a github repository
I have a school project. The specifications say that we can enable or disable a component.
I came from the c world in which we can compile c to object and chose the object to link. I try this in OCaml.
I have two source file with the same function but their effect is different.
I have two folders called "on" with implementation and "off" folder with an empty body
For the test, I have a simple a.ml and a.mli file that only print hello world and b.mli b.ml that call the module a. (so I have on/a.ml and off/a.ml)
I compile the on version and the off with this command:
ocamlc -c -I on on/a.mli on/a.ml -o on/a.cmo
then I try to link the C-way
ocamlc on/a.cmo b.ml -o on_b.exe
but I get the error
File "b.ml", line 1, characters 9-15:
Error: Unbound module A
I have then read I should specify the folder to search in with -I.
ocamlc -I on -I off on/a.cmo b.ml -o on_b.exe
I was happy because of that work for the on version
but it will not work for the off version
ocamlc -I on -I off off/a.cmo b.ml -o off_b.exe
I get the error
Error: Files b.cmo and off/a.cmo
make inconsistent assumptions over interface A
I have inspected with ocamlobjinfo it seems to build B its searches for the first module called A
In this example, I have only A and B but in future, I will build with some version on and some off... but don't do it manually
A solution found but no really efficient is to clean all .cmo and .cmi files ...
thanks for your reading and your time
EDIT:
I have also test with -open but it seems to work only for standards modules.
As you can see, compiling your .mli files produces a .cmi file. This is the file that you indicate is there by using the -I dir option.
So, if I understand you correctly, you have a a.mli in both your on and your off directory.
Now, when you refer to the module A in your code, the compiler looks for it in the current directory, then in the linked directories in the order they are given. When you compile "on", the right .cmi is found (because -I on comes before -I off in your command line). In the case of "off", the "on" cmi is found, and the compiler sees that it doesn't correspond to the right cmo.
Your two compiling lines should be:
ocamlc -I on on/a.cmo b.ml -o on_b.exe
ocamlc -I off off/a.cmo b.ml -o off_b.exe

How do you use gcc's ar utility with ocaml's compiler?

I can create a static library with ocaml object files using
ar rc ./lib/libprog.a ./_build/*.cmo
And I can display the contents of the libprog.a using
ar t ./lib/libprog.a
but I can't find any documentation that explains how to use this static library with ocaml's compilers.
Could someone please post an example(or provide a link) which demonstrates how to use a libprog.a file with ocaml's compilers. Please no examples with ocaml build utilities because I want to see how its done at the command line using ocaml's compilers. Thanks.
OCaml wants to use its own archive format.
$ cat a.ml
let x = 14
$ cat b.ml
let y = 17
$ cat main.ml
let () = Printf.printf "%d\n" (A.x + B.y)
$ ocamlc -c a.ml
$ ocamlc -c b.ml
$ ocamlc -a -o oclibab.cma a.cmo b.cmo
Now there is an OCaml archive (.cma file) named oclibab.cma that contains the modules A and B:
$ ocamlobjinfo oclibab.cma
File oclibab.cma
Force custom: no
Extra C object files:
Extra C options:
Extra dynamically-loaded libraries:
Unit name: A
Interfaces imported:
9b04ecdc97e5102c1d342892ef7ad9a2 Pervasives
79ae8c0eb753af6b441fe05456c7970b CamlinternalFormatBasics
b162d96cf09fcde9d245f96359c9178a A
Required globals:
Uses unsafe features: no
Force link: no
Unit name: B
Interfaces imported:
9b04ecdc97e5102c1d342892ef7ad9a2 Pervasives
79ae8c0eb753af6b441fe05456c7970b CamlinternalFormatBasics
bc583fd68fc2b0a4e44e3e640983c8dd B
Required globals:
Uses unsafe features: no
Force link: no
Note that this archive format contains digital signatures for the interfaces. This allows stronger checking at link time than is generally available through the stock toolchain of the OS (like ar).
$ ocamlc -o main oclibab.cma main.ml
$ ./main
31

"Error: unbound module" in OCaml

Here's a simple example of using the library Cohttp:
open Lwt
open Cohttp
open Cohttp_lwt_unix
let body =
Client.get (Uri.of_string "http://www.reddit.com/") >>= fun (resp, body) ->
let code = resp |> Response.status |> Code.code_of_status in
Printf.printf "Response code: %d\n" code;
Printf.printf "Headers: %s\n" (resp |> Response.headers |> Header.to_string);
body |> Cohttp_lwt.Body.to_string >|= fun body ->
Printf.printf "Body of length: %d\n" (String.length body);
body
let () =
let body = Lwt_main.run body in
print_endline ("Received body\n" ^ body)
I'm trying to compile it
ocaml my_test1.ml
Error:
Error: Unbound module Lwt
How to actually include/require the module Lwt into my app?
update
Also:
$ ocamlbuild
bash: ocamlbuild: command not found
But:
$ opam install ocamlbuild
[NOTE] Package ocamlbuild is already installed (current version is
0.12.0).
And
$ opam install ocamlfind
[NOTE] Package ocamlfind is already installed (current version is
1.7.3-1).
And
$ ocamlfind
bash: ocamlfind: command not found
Where are ocamlfind and ocamlbuild located?
update2
$ ocamlfind ocamlc -package lwt -c my_test1.ml
File "my_test1.ml", line 2, characters 5-11:
Error: Unbound module Cohttp
You have several options depending on your needs.
1) If you want to create a full project for your binary I recommend looking at jbuilder. Here is a very nice guide that explains the environment/project configuration step-by-step: OCaml for the impatient.
2) Another option is to compile the binary directly as you were trying to do:
ocamlbuild -pkg lwt -pkg cohttp-lwt-unix my_test1.native
Note that you need to have a file named my_test1.ml to generate the requested my_test1.native.
3) And finally for quick scripts I find it handy to be able to ask the OCaml interpreter to load the dependencies directly in the source file. Just add the following to the beginning of your file:
#use "topfind";;
#require "lwt";;
#require "cohttp-lwt-unix";;
And then run ocaml my_test1.ml.
Hope this helps! :)
Also looking at the command not found errors you are getting I can suggest to make sure your environment is correctly configured. The Real World OCaml book has a wiki page for that: https://github.com/realworldocaml/book/wiki/Installation-Instructions
Try to compile it like this ...
ocamlfind ocamlopt -o my_test \
-linkpkg \
-package lwt,cohttp,cohttp-lwt-unix \
-thread
my_test.ml

Ocamlbuild doesn't take in account the included folders

I am trying to use ocamlbuild to build my project. It needs to take into account an external library (also compiled with ocamlbuild) that contains in the folder myfolder/ the following files (myfolder is a symlink to the good library that I create in the file myocamlbuild.ml):
$ ls _build/myfolder/
PhTools.cmi PhTools.ml PhTools.mli
PhTools.cmo PhTools.ml.depends PhTools.mli.depends
To compile my library I run the following command:
ocamlbuild -use-ocamlfind -plugin-tags "package(eliom.ocamlbuild),package(containers),package(fileutils)" -Is "myfolder" -mods "myfolder/PhTools.cmo" _server/./TestHelloServices.cmo
When I run it I have the error:
[...]
ocamlfind ocamlc -I '' -I _server -I _type -i -thread -package eliom.server -package eliom.syntax.predef -package eliom.syntax.type -syntax camlp4o _type/TestHelloServices.ml > _type/TestHelloServices.inferred.mli
+ ocamlfind ocamlc -I '' -I _server -I _type -i -thread -package eliom.server -package eliom.syntax.predef -package eliom.syntax.type -syntax camlp4o _type/TestHelloServices.ml > _type/TestHelloServices.inferred.mli
File "TestHelloServices.eliom", line 6, characters 10-17:
Error: Unbound module PhTools
Command exited with code 2.
As you can see the option -I myproject hasn't been given to the command ocamlfind ocamlc -i .... You can note that if I add by hand ocamlfind ocamlc -i -I 'myproject' this step works. I tried lot's of differents things to solve my problem : using -cflags '-I,myproject' -lflags '-I,myproject', putting in _tags the line:
myproject: include
but I always have this error.
I don't know if it's a relevant information but I use a slightly modified version of the ocsigen myocamlbuild.ml file:
let client_dir = "_client"
let server_dir = "_server"
let type_dir = "_type"
module M = Ocamlbuild_eliom.Make(struct
let client_dir = client_dir
let server_dir = server_dir
let type_dir = type_dir
end)
open Ocamlbuild_plugin;;
let () =
dispatch begin function
| Before_options ->
M.dispatcher Before_options;
(* Link root project *)
(try
Unix.symlink "../../../../../myfolder/" "_build/myfolder"
with Unix.Unix_error (Unix.EEXIST, _, _)-> ());
| hook -> M.dispatcher hook
end;
Thank you in advance,
TobiasBora.
I guess ocamlbuild will not follow "external" (out of project tree) links.
Maybe hardlink will work, but better yet install the library as the normal ocamlfind package.