How to compile a jocaml file with ocamlbuild and include a package? - ocaml

How can I compile a jocaml source file which needs the cryptokit package (successfully compiled with the companion ocaml) with the ocamlbuild tool?
When I execute the command ocamlbuild -pkg cryptokit -use-jocaml a.native I get this error:
Warning: tag "package" does not expect a parameter, but is used with parameter "cryptokit"¬
+ jocamlopt -I /prefix/lib/ocaml -I /prefix/lib/ocaml/site-lib/cryptokit -I /prefix/lib/ocaml/site-lib/num /prefix/lib/ocaml/unix.cmxa /prefix/lib/ocaml/nums.cmxa /prefix/lib/ocaml/site-lib/cryptokit/cryptokit.cmxa a.cmx -o a.native¬
File "_none_", line 1:¬
Error: Files /prefix/lib/ocaml/unix.cmxa¬
and /prefix/lib/ocaml/unix.cmxa¬
both define a module named Unix¬
Command exited with code 2.¬
Compilation unsuccessful after building 4 targets (3 cached) in 00:00:00.
Essentially, the ocaml Unix module clashes with himself.
This error only pops when I include Cryptokit (with -pkg cryptokit) probably because Cryptokit requires Unix. a.ml can in fact be empty and still reproduce the error.
I tried to add the -use-ocamlfind flag but as it also uses ocamlfind to get the compiler, it selects the ocaml compiler instead of the jocaml one.
By executing sequentially the same commands as ocamlbuild (displayed by -verbose 1), I got that when I execute the last one without /.../unix.cmxa then there is no more clash, but the wrong Unix module is loaded: it's the one from ocaml and not from jocaml, so it it completely crashes when I use any jocaml feature in a.ml:
jocamlopt -I /prefix/lib/ocaml -I /prefix/lib/ocaml/site-lib/cryptokit -I /prefix/lib/ocaml/site-lib/num /prefix/lib/ocaml/nums.cmxa /prefix/lib/ocaml/site-lib/cryptokit/cryptokit.cmxa a.cmx -o a.native
However, when I also remove the -I /prefix/lib/ocaml part, then it compiles successfully:
jocamlopt -I /prefix/lib/ocaml/site-lib/cryptokit -I /prefix/lib/ocaml/site-lib/num /prefix/lib/ocaml/nums.cmxa /prefix/lib/ocaml/site-lib/cryptokit/cryptokit.cmxa a.cmx -o a.native
To summarize, I got it to work by executing manually a modification of the last command, but I would like to get ocamlbuild working.
I think this error has to do with the fact that Cryptokit requires the Unix module: as I compiled it with ocaml and not jocaml, at the linking stage it tries to link with the ocaml stdlib one (which needs to be included) and not the jocaml stdlib one (which is implicitly included as part of the stdlib).

I had no idea there were active users of the ocamlbuild+JOcaml combination! By curiosity, would you say a bit more about what you are using JOCaml+cryptokit for?
I don't know much about Cryptokit or JOCaml, but it looks like your main problem is not related to ocamlbuild. If I understand correctly, (1) Cryptokit needs Unix and (2) JOCaml needs to use its own variant of Unix. If this is correct, compiling Cryptokit against ocaml's Unix and expecting it to work when linked with a JOCaml program that itself requires JOCaml's Unix is bound to create a lot of trouble. If this work in your case, it must be because either the part of Cryptokit you use doesn't actually require Unix, or the JOCaml program you are testing with does not actually require JOCaml's Unix. In the long run, it would probably be best to compile Cryptokit with JOCaml directly (I don't know how comfortable you are with the OCaml ecosystem in general, but I would personally try to build an OPAM switch where ocaml{c,opt} are aliases for jocaml{c,opt} and build programs from that).
Regarding the ocamlbuild specific part, it's hard to give any accurate advice without a tarball to be able to reproduce your setup and experiment with it. But I would try one of the two following options:
You can use -use-ocamlfind and teach ocamlfind to use jocaml instead of ocaml by using the OCAMLFIND_COMMANDS environment variable (see man ocamlfind)
You can avoid -use-ocamlfind entirely and instead call ocamlfind as a command-line tool to get the location of the cryptokit library (ocamlfind query cryptokit). You would then not use -pkg cryptokit but pass the path yourself (with -lflags and -cflags or by modifying your myocamlbuild.ml configuration file).

Elaborating on the -use-ocamlfind option as suggested by gasche, I got it to work with the addition of a small nasty hack: removing "unix" from the requires field of the META file of the cryptokit package. It works because jocaml links everything with threads and unix by default (a real solution would have been to disable this behavior, but it seems a lot harder). So the working compilation command is:
ocamlbuild -use-ocamlfind -use-jocaml -pkg cryptokit a.ml
I think it is possible to generalize this to any package that uses either unix or threads when compiling with jocaml. A subsidiary question is whether it is possible to do this dynamically with a _tags or myocamlbuild.ml file (note: comment if this remark needs to be moved).

Related

Using ocamlmktop with ocamlbuild

I have a project that builds successfully using ocamlbuild. However, I would also like an easy way to interact with the project's individual functions from different modules via the toplevel but my attempts at using ocamlmktop haven't worked out as I'd like. I've found that unless I manually put the .cmi files in the active directory, I get an "Unbound module" error. The command I'm currently using to build is:
ocamlfind ocamlmktop -I _build -o my_ocaml -linkpkg -package str module1.cmo module2.cmo
Is there a better, less hacky way to get the toplevel to work in this project structure without moving cmi files out of the _build directory?
Edit: I've figured out that I can get it to load the types and modules if I run the toplevel as
./my_ocaml -I _build
But this still seems hacky. Is there a way to bake the search path or cmi files in perhaps?
Edit 2: I think the solution to my problem may actually be not to compile a custom toplevel at all given this restriction about interface files. I have instead added load directives to my .ocamlinit to use the modules. If anybody has better ideas to solve this, I'd greatly appreciate it.
You can build a toplevel by listing the module names you want in a my_ocaml.mltop file:
Module1
Module2
subdir/Module3
Then building the target my_ocaml.top will call ocamlmktop in the expected way, and you can run the resulting my_ocaml.top toplevel.
This does not change the way that you need to add _build to the include path for the type-checker to be able to find the .cmi files. You can do this when you invoke the toplevel by passing the command-line arguments -I _build, or from the toplevel with #dir "_build";; -- the last command can also be put in your .ocamlinit if you prefer.

Creating an OCaml library

I am trying to create a library that I can use in other OCaml projects, and I'm totally lost.
I'm currently using ocamlbuild which is great for spitting out executables, but I don't know how to get a library out of it.
I've discovered the -a option in ocamlopt and ocamlc but I'm not really sure how to use it. The documentation I've found (for example, here), seems to assume some preexisting knowledge. I don't even know what a .a file is. After I run that, which of the outputted files do I need to build a project that depends on this library? Do I need the mli files so that the application knows the signatures of the library code, or is that included in the output somehow? Also, it would be nice to be able to package all the files together, something similar to a .jar file for Java.
In any case, I would love for ocamlbuild to do all of this for me, since if I have to invoke ocamlopt -a I will have to either manually specify dependencies or hack a script around ocamldep -- something that ocamlbuild was supposed to fix. However, I don't know how to tell it to build a library.
I'm willing to use oasis or OPAM or something if it's necessary, but I would like to learn how to do this using just the basic tools first.
OCamlbuild has some built-in functionality for building libraries, so you can get started with just ocamlbuild foo.cma foo.cmxa (assuming foo.ml is your entry point). This will invoke ocamlopt -a and ocamlc -a for you, handling all the dependency plumbing and leaving the generated files inside _build.
That should be enough to let you compile a library and link it from another program. Since this is just a test you can simply point at the aforementioned _build with -I when compiling the program that uses the library. For real use a library should be packaged - when you get to that point you'll want to look into ocamlfind, oasis, etc.
Have a look at the ocaml.org tutorial on compiling OCaml projects. Additionally the official manual for the bytecode and native code compilers contains useful detail on producing and using the various types of files.
The documentation for ocamlbuild archives seems to cover this pretty well.
In any case, here's one way to do ocaml libraries. Let's say you have a directory called foo containing your .ml, .mli, and .mllib files. Let's say it contained bar.ml, bar.mli, baz.ml, and baz.mli. To distribute all this as one library, you'd also have a foo.mllib in that directory, whose contents are
Bar
Baz
Then to compile, do
$ ocamlbuild -use-ocamlfind foo.cma foo.cmxa
Here is an example.
Then to use your library foo, let's say you had a sibling directory called main, and main contains main.ml, _tags, myocamlbuild.ml.
myocamlbuild.ml should have the following contents:
open Ocamlbuild_plugin
open Command
let () =
dispatch (
function
| After_rules ->
ocaml_lib
~extern:true
~dir:"/path/to/foo/_build"
"foo"
| _ -> ()
)
_tags should have the following contents:
<main.{ml,native,byte}>: use_foo
Compile main.ml with
$ ocamlbuild -use-ocamlfind main.byte main.native
run with
$ ./main.byte
$ ./main.native
More information here as well: https://ocaml.org/learn/tutorials/ocamlbuild/Using_an_external_library.html

Difference between module and package Ocaml

I'm basically trying to follow this stackoverflow answer located in this post:
What is the best module for HttpRequest in OCaml
and I'm running in to problems. When I am trying to run a single file with just
open Lwt ;;
I am getting and error saying it is an unbound module. I have run the following opam instruction:
opam install lwt
and it did install the correct package.
So I think the problem is the difference between a module and a package, which I don't really understand. I was looking at this question as a possible answer, but I wasn't sure if it was what I needed.
Unbound modules in OCaml
Thanks for the input guys, I'm new to Ocaml and I'm trying to learn the ins and outs of building something.
To use a "package", you must tell the compiler about it explicitly. Unbound module in OCaml usually means one of two things: your made a typo of the module name, or you failed to set a proper module search path. What compiler options do you use?
If you use ocamlfind, the compilation should look like:
ocamlfind ocamlc -package lwt -c mymodule.ml
this instructs the compiler to try to find modules in lwt package installation directory, in addition to the default ones.
if you do not use ocamlfind.... well, use ocamlfind.
The command for compiling our program will be:
ocamlfind ocamlopt -o progprog -linkpkg \
-package lablGL,sdl,sdl.sdlimage,sdl.sdlmixer,sdl.sdlttf \
module1.ml module2.ml
As seen on: https://ocaml.org/learn/tutorials/compiling_ocaml_projects.html

Ocamlbuild override the default options

My installation of OCaml does not recognize #!, therefore camlp4o cannot be ran standalone. It must be invoked as "ocamlrun camlp4o".
I try to add a flag in the plugin. But the new flag is simply added to the existing flag.
The result is that ocamlbuild will invoke the compiler with
"-pp camlp4o ocamlrun camlp4o".
A working workaround is to introduce a new tag 'my_camlp4o'. However, the documentation of ocamlbuild did mention the capability of 'overriding' defaults. It seems ocamlc, ocamlopt can be overridden. But can we override camlp4o? Can it be done in plugins?
There is support for changing defaults in Ocamlbuild, through the -ocamlc <command>, -ocamldep <command> etc. command-line options. Unfortunately, camlp4 is currently not part of the set of customizable commands. Could you please use the issue tracker to request this feature?
(In the meantime I recommend a workaround, eg. you replace the ocamlrun executable in your PATH by an executable script that does the right thing.)
Edit
Another workaround is to do all your compilation through ocamlfind, which has support for overriding the camlp4 command (see the documentation), and can be used as a basis for all ocamlbuild command with the -use-ocamlfind option. Unfortunately, ocamlfind itself only supports preprocessing at compile-time (when invoking the actual compiler), not stand-alone source-to-source processing, so that may not cover your own use case.

using OCaml Batteries Included as a vanilla cma

I am a bit frustrated and confused by the OCaml Batteries Included concept and the way most tutorials I could find proceed. Before I get to use "productivity" tools like GODI or replace invocations of ocamlc with ocamlfind batteries/ocamlc (which is, at this point, too magical for me) I was hoping to be able to simply use OCaml Batteries Included core set of libraries like any other library. To that end I downloaded the latest source from git (head hash: 9f94ecb) and did a make all. I noticed that I got three .cma libraries at ./_build/src/ together with 102 .cmi files in the same directory. So I assumed that compiling with the -I switch pointing to that directory and linking with one of the three .cma libraries found there would be enough without needing to "install" the Batteries or use the platform tools. To test that, I set out to produce an executable for the following simple program I found somewhere:
(* file euler001.ml *)
open BatEnum
open BatPervasives
let main () =
(1--999)
|> BatEnum.filter (fun i -> i mod 3 = 0 || i mod 5 == 0)
|> BatEnum.reduce (+)
|> BatInt.print stdout
let _ = main ()
I was able to compile it with:
ocamlc -c -I ../batteries-included/_build/src/ euler001.ml
but when I tried to link with:
ocamlc -o euler001 unix.cma nums.cma ../batteries-included/_build/src/batteries.cma euler001.cmo
I got:
File "_none_", line 1, characters 0-1:
Error: Error while linking ../batteries-included/_build/src/batteries.cma(BatBigarray):
The external function `caml_ba_reshape' is not available
The nums.cma and unix.cma I added at the command line because the linker complained about missing references to undefined global Big_int and (when that was added) to Unix. But after these two modules were added on the linker invocation I received the last message (on the missing external function 'caml_ba_reshape') which proved blocking for me. So I would like to ask:
how does one proceed in this particular case?
how does one proceed in the general case (i.e. when the linker complains about a missing external function)
is it viable to use Batteries Included in this fashion? Before I rely on platform tools I want to have the assurance that I can use the underlying artifacts (cma and cmi/mli files) with the standard OCaml compiler and linker if I run into problems.
caml_ba_reshape is, as you could guess from the name but I agree it's not obvious, a primitive of the Bigarray module. You should add bigarray.cma in your compilation command, before batteries.cma which depends on it.
There is a reason why it is advised to use ocamlfind, which is precisely used to abstract over those dependencies. I don't think you are supposed to use ocamlfind batteries/ocamlc, but rather ocamlfind ocamlc -package batteries. If you insist on using the compiler without such support, then indeed you have to compile manually -- I understand your frustration, but I hope you also understand that it is intrisic to any sufficiently sophisticated OCaml library, and that it comes only from your self-imposed constraints.
how does one proceed in the general case (i.e. when the linker complains about a missing external function
You have to know or guess where the primitive comes from. Looking at the META file provided by the library, which is used to inform ocamlfind of the dependencies, may help you. You can use the tool ocamlobjinfo to know which primitive a .cma provides, if you want to check your assumption. (Or better, use ocamlfind to spit the correct compile command, see below.)
is it viable to use Batteries Included in this fashion?
Compiling "by hand" is reasonable if you insist. Working only in the source repository, without installing the library, is not. It's easy to keep doing what you do after an install, just replace your -I ... by the chosen install path.
Before I rely on platform tools I want to have the assurance that I can use the underlying artifacts (cma and cmi/mli files) with the standard OCaml compiler and linker if I run into problems.
ocamlfind is not (only) a platform tool. It is the way to use third-party ocaml libraries, period. It should be a standard on any ocaml-using platform. That it does not come with INRIA's distribution is an historical detail.
You can ask ocamlfind to show you its invocation of the bare compilers:
% ocamlfind ocamlc -linkpkg -package batteries t.ml -o test -verbose
Effective set of compiler predicates:
pkg_unix,pkg_num.core,pkg_num,pkg_bigarray,pkg_str,pkg_batteries,autolink,byte
+ ocamlc.opt -o test -verbose -I /usr/local/lib/ocaml/3.12.1/batteries /usr/lib/ocaml/unix.cma /usr/lib/ocaml/nums.cma /usr/lib/ocaml/bigarray.cma /usr/lib/ocaml/str.cma /usr/local/lib/ocaml/3.12.1/batteries/batteries.cma t.ml
I don't want to throw stones at you. The landscape of OCaml tools, beside the minimal nutshell of what's provided by the source distribution, is quite sparse and lack a coherent point of entry. With time I've grown used to those tools and it's quite natural to use them, but I understand there is some cost of entry that we should try to lower.
PS: any advice on how to improve batteries documentation is warmly welcome. Patches to add things to the documentation or fix it are even better. batteries-devel#lists.forge.ocamlcore.org is the place to go.