Don't Link all Standard Library Modules when Compiling OCaml - ocaml

I'm putting together an intro OCaml project for a CS class and part of it involves implementing list operations. I want them to be able to use Pervasives, but not List or any other standard library modules. Is there a way to set up ocamlbuild so it only links against Pervasives in the standard library?

You can use the -nostdlib option of the compilers but that will hide both Pervasives and List.
What you want is difficult to achieve since both compilation units are part of the same library archive namely stdlib.cma.
You could maybe try to compile your own copy of Pervasives and use the above flag.

I see two opportunities: either remove module directly from the OCaml standard library or hide them by overloading with a module with different (possibly empty) signature.
The first variant requires editing OCaml distribution Makefiles. With opam and is not that scary, actually, as you can patch OCaml quite easily and distribute each patched OCaml as a separate compiler. To remove module from the stdlib archive you will need to edit stdlib/Makefile.shared, stdlib/StdlibModules, and stdlib.mllib. After you've removed the unnecessary modules, you can do:
./configure
make world.opt
make install
Another option is to (ab)use the -open command line argument of ocamlc. When this option is specified with a name of a module, this module will be automatically opened in the compiled program. For example, you can write your own overlay over a standard library, that has the following interface (minimal.mli):
module List = sig end (* or whatever you want to expose *)
and then you can compile either with ocamlc -open minimal ..., or, with ocamlbuild: ocamlbuild -cflags -open,minimal ... (you can also use _tags file to pass the open flag, or write an ocamlbuild plugin).

Related

In Ocaml, how can I access the Lib module from the command line?

If I type ocaml at the command line and then run code involving e.g. the Lib.explode function, I get the error Error: Unbound module Lib. How can I fix this?
You don't give much to go on. I assume Lib is not the real name, but just an example. You also don't say whether it's a standard OCaml library, a library you have built yourself, or somebody else's library.
Assume it's a library you have built yourself. Then somewhere you have a file named lib.cmo (for a single module) or lib.cma (for an archive of multiple modules). To use such a library from inside the OCaml REPL (the so-called "toplevel"):
$ ocaml
# #load "lib.cmo";;
Or:
$ ocaml
# #load "lib.cma";;
Note that you want to type #load including the #. The first # on the line is the prompt from OCaml. This sometimes makes it confusing to read a transcription of an OCaml session.
After loading the library you can refer to names in the library as you usually would, i.e., as Lib.myfun. You don't refer to them just by the name myfun (as it is sometimes tempting to assume).
You may also wish to use ocamlmktop.
Consider for instance a very basic file test.ml:
let foo = 42
I compile that with the following and now have test.cmo.
ocamlc test.ml
Now I'll create a toplevel with that module available.
% ocamlmktop -o mytop test.cmo
% ./mytop
OCaml version 4.13.1
# Test.foo;;
- : int = 42
#

OCaml statically detect dependency on non-pervasives library in standard distribution

Certain modules that ship with OCaml like Unix and Bigarray have their own .cmx and .cmxa files in ocamlopt -where (which is ~/.opam/4.03.0/lib/ocaml on my system in my current opam switch).
Is there a way to determine without compiling which source files depend on which of these "special" libraries in the standard distribution? I'm intending to consume this output later in a Makefile.
The following program example.ml
open Unix;;
Unix.system "echo hi";;
Can be compiled using ocamlfind ocamlopt -package unix -linkpkg example.ml. I'm not sure how to compile it without going through the ocamlfind wrapper.
I'm wondering if there's a way to statically detect that the unbound-in-this-file module Unix corresponds to "something" in the standard distribution and report unix.cmxa as a dependency. ocamldep does not seem to report it as a dependency by default.
ocamldep -all example.ml just reports that the various object and interfaces files that can be produced using example.ml depend on example.ml. I was hoping for either an error message complaining that ocamldep doesn't understand the Unix module or some indication that it's required to build the objects.
$ ocamldep -all example.ml
example.cmo example.cmi : example.ml
example.cmx example.o example.cmi : example.ml
I understand that your question is:
For a given module name, say Unix, how can we find the library which provides it?
Unfortunately there is no such a tool (yet).
If we limit the search space to the libraries come with the OCaml compiler itself, I would do:
$ ocamlobjinfo $HOME/.opam/4.03.0/lib/ocaml/*.cma | grep '^\(File\|Unit name\)'
This will list all the modules defined in each archive. You may or may not find the module name in the result.
It is impossible in general, since the library you seek may not be standard or may not be installed locally. You can use API search engines like ocamloscope but they never cover all the OCaml libraries ever written of course.
Though modules might be packed into libraries with arbitrary names, the module interfaces still preserve a one to one mapping between top-level module names and compiled module interface file names. So if you have an error 'Unbound module Xxx`, the you can do
find ~/.opam -iname Xxx.cmi
If you didn't find any, then it means, that such library is not installed. And currently, there is no well-established way to find out which package provides this module, you can use Google, ask people on mailing lists or discussion forums, or try to use apt-file hoping that the library is in a standard distribution.
If the search returned exactly one folder, then you're lucky, you got the package. The package may contain object files of different genera (.cmx - for native code, .cmo - for the bytecode), as well as libraries (.cma - is a collection of .cmo, .cmxa - is a collection of .cmx, and .cmxs is a dynamic version of .cmxs). The flexibility of OCaml, that is both a boon and a bane, allows any of these files to be missing. Well-mannered libraries usually provide all these files, as well as have a naming convention that package name matches with the library name. But if you're using ocamlfind and the folder has the META file, then the name of the folder is the name of the package that you need to pass to ocamlfind in order to link the libraries from this package.
If you have more than one results, then you need to use common sense to determine which of the two libraries you need to use. Alternatively, you may try to use one and another and see which one compiles.

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

load|use|require a library object file within an Ocaml source file

The Ocaml manual contains an exercise (here) in which library object files are loaded in the toplevel loop (the ocaml interactive interpreter) in the following way:
#load "dynlink.cma";;
#load "camlp4o.cma";;
I'm trying to replicate the subsequent code in a compilable source file, and the code requires the above library object files. Can I load these files with a line of code within the source file and compile it with ocamlc? I've tried "#load", "load", "#use", "use", "#require", "require", and all these proceded by "#directory" and "directory". I know that you can include modules with "include ;;", but this shouldn't work either, because they're just library files, not modules. I've tried to find a way to do this in the manual, but to no avail.
Do I need to reference the files in the compilation command? If so, how do I do this?
Directives starting with a # character are used only in the toplevel and are not strictly part of the OCaml language. In a file that you want to compile, you don't use # directives. See the OCaml manual Chapter 9. The #load directives are for loading a library. When compiling a file, you have to tell the compiler to use the library (on the command line, not in the file). It's good to learn the compiler commands directly at first, but eventually you should use ocamlfind and oasis, which make compilation much easier.
I'm assuming your source is written using extensions implemented by camlp4o. To compile your source, you can say:
ocamlc -pp camlp4o -o myfile myfile.ml
I believe the complexities of the #load command are required only when you want to use the extensions in the toplevel (the interpreter).

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.