Using ocamlmktop with ocamlbuild - ocaml

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.

Related

Documentation generation for BuckleScript project

Is there any way to generate code documentation for BuckleScript or Reason? I've tried using ocamldoc, but I don't know how to include node package dependencies automatically.
There isn't an automatic resolution yet for node packages. You can manually specify each dependent package in the ocamldoc command, e.g.:
ocamldoc -html -d doc -I node_modules/bs-webapi/lib/ocaml -I node_modules/bs-fetch/lib/ocaml -I node_modules/bs-platform/lib/ocaml src/YourModule.re
The directory includes are fairly predictable, you just have to point at the lib/ocaml directories in each package, ocamldoc will find their compiled .cmi files and pull in the required type information from there.
This also means that you'll first need to have done bsb -make-world, to compile all those .cmis.
There’s a tool which, supposedly, automatically performs a lot of the orchestration of ocamldoc described by #Yawar, called BsDoc.
Note that I have not used this myself; but it it supposed to be the go-to for a lot of BuckleScript-specific projects (i.e. using bsb with npm-installed dependencies, not dune with opam-installed dependencies.)

Loading OCaml modules not in the current directory

I'm writing a large OCaml project. I wrote a file foo.ml, which works perfectly. In a subdirectory of foo.ml's directory, there is a file bar.ml.
bar.ml references code in foo.ml, so its opening line is:
open Foo
This gives me an error at compile time:
Unbound module Foo.
What can I do to fix this without changing the location of foo.ml?
The easy path is to use one of OCaml build system like ocamlbuild or oasis. Another option would be jbuilder but jbuilder is quite opiniated about file organization and does not allow for the kind of subdirectory structure that you are asking for.
The more explicit path comes with a warning: OCaml build process is complicated with many moving parts that can be hard to deal with.
After this customary warning, when looking for modules, OCaml compiler first looks for module in the current compilation environment, then looks for compiled interface ".cmi" files in the directories specified by the "-I" option flags (plus the current directory and the standard library directory).
Thus in order to compile your bar.ml file, you will need to add the parent directory in the list of included directories with the -I .. option.
After all this, you will discover that during the linking phase, all object files (i.e. .cmo or .cmx) need to be listed in a topological order compatible with the dependency graph of your project.
Consequently, let me repeat my advice: use a proper build system.

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

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).

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

Determine list of source files (*.[ch]) for a complex build with scons

Suppose you have a complex source tree for a C project, lots of directories with lots of files. The scons build supports multiple targets (i386, sparc, powerpc) and multiple variants (debug, release). There's an sconstruct at the root (referencing various sconscripts) that does the right thing for all of these, when called with arguments specifying target and variant, e.g. scons target=i386 variant=release.
Is there an easy way to determine which source files (*.c and *.h) each of these builds will use (they are all slightly different)? My theory is that scons needs to compute this file set anyway to know which files to compile and when to recompile. Can it provide this information?
What I do not want to do:
Log a verbose build and postprocess it (probably wouldn't tell *.h files anyway)
find . -name '*.[ch]' also prints unwanted files for unit testing and other cruft and is not target specific
Ideally I would like to do scons target=i386 variant=release printfileset and see the proper list of *.[ch] files. This list could then serve as input for further source file munging tools like doxygen.
There are a few questions all squashed together here:
You can prevent SCons from running the compiler using the --dry-run flag
You can get a dependency tree from SCons by using --debug=tree, or --tree=all flags, depending on which version you are running
Given a list of files, one per line, you can use grep to filter out only the things that are interesting for you.
When you put all of that together you end up with something like:
scons target=i386 variant=release printfileset -n --tree=all | egrep -i '^ .*\.(c|h|cpp|cxx|hpp|inl)$'