TurtleMan
TurtleMan

Reputation: 193

How to compile multiple files in OCaml?

I am currently teaching myself ocaml for my programming language class and I am having trouble compiling multiple files in ocaml.

I have defined a function in my get_file_buffer.ml file

Source code of get_file_buffer.ml

(* 
   Creating a function that will read all the chars 
   in a file passed in from the command argument.
   And store the results in a char list. 
*)

let read_file char_List =
    let char_in = open_in Sys.argv.(1) in   (* Creating a file pointer/in_channel *)
  try
    while true do
      let c = input_char char_in in         (* Getting char from the file *)
            char_List := c :: !char_List    (* Storing the char in the list *)
    done
  with End_of_file ->
        char_List := List.rev !char_List;   (* End of file was reaching, reversing char list *)
        close_in char_in;                   (* Closing the file pointer/in_channel *)

    (* Need to figure out how to catch if the file was not openned. *)
;; 

I am trying to call the function in my main.ml

Source code of main.ml

(* Storing the result of read_file to buffer which buffer is a char list reference *)
let buffer = ref [] in
      Get_file_buffer.read_file(buffer);

      print_string "\nThe length of the buffer is: ";
      print_int (List.length !buffer); (* Printing length of the list *)
      print_string ("\n\n");
      List.iter print_char !buffer;    (* Iterating through the list and print each element *)

In order to compile the program I am using a MakeFile

Makefile content

.PHONY: all
all: test

#Rule that tests the program
test: read_test
    @./start example.dat

#Rules that creates executable
read_test: main.cmx get_file_buffer.cmx
    @ocamlc -o start get_file_buffer.cmx mail.cmx

#Rule that creates main object file
main.cmx: main.ml
    @ocamlc -c main.ml

#Rule that creates get_file_buffer object file
get_file_buffer.cmx: get_file_buffer.ml
    @ocamlc -c get_file_buffer.ml

When I run the test rule of my Makefile I get the error: Error: Unbound module Get_file_buffer.

I have been trying to use these question as references: Compiling multiple Ocaml files and Calling functions in other files in OCaml.

Yet I have not been able to get the program to compile correctly. How would I correctly compile the above code to make the program run correctly?

Upvotes: 1

Views: 1342

Answers (3)

Bikal Lem
Bikal Lem

Reputation: 2423

Rather than build *.ml files one by one. You have a couple of better options which are both productive and effective.

Use ocamlbuild with Makefile like so.

Rename main.ml to start.ml and use the following Makefile.

$ cat Makefile

.PHONY: all test

all: start test

test: start
    @./start.native get_file_buffer.ml

start:
    ocamlbuild start.native

$ make ....

Use dune(formerly jbuilder) which is far the most coherent build tool for ocaml these days.

a. Create a jbuild file in the same directory as the *.ml files.

$cat jbuild

(jbuild_version 1)

(executable
 ((name start)))

$ jbuilder build start.exe

$ jbuilder exec -- ./start.exe get_file_buffer.ml

If you prefer you can use make to drive dune/jbuilder by creating a Makefile.

$cat Makefile

.PHONY: all test

all: start test

test: start
    jbuilder exec -- ./start.exe get_file_buffer.ml

start:
    jbuilder build start.exe

$ make

Upvotes: 2

octachron
octachron

Reputation: 18912

Writing a correct Makefile for OCaml is complicated: OCaml compilers tend to produce multiple files which is not something that Makefile handles gracefully, moreover the exact dependency graph might depend on the compiler flags (e.g. -opaque or -no-alias-deps) and the version (bytecode, native without flambda, native with flambda) of the compiler. That's why the easiest solution by far is to use a build system like jbuilder/dune(http://dune.readthedocs.io/en/stable/), or ocamlbuild (https://github.com/ocaml/ocamlbuild/blob/master/manual/manual.adoc).

p.s. : in your case, you are indeed missing the dependency of main.cmx to get_file_buffer.{cmi,cmx} .

Upvotes: 4

kne
kne

Reputation: 1418

I believe the problem is that main.cmx should depend on get_file_buffer.cmx. Otherwise make might try to compile main.cmx first, in which case of course the module Get_file_buffer cannot be found because it does not yet exist.

More precisely, the compilation of main.cmx also depends on gen_file_buffer.o. But as that file is created at the same time as gen_file_buffer.cmx, you should be fine. (And to my limited knowledge it is not possible with make to specify that a single rule creates more than one file at the same time.)

Upvotes: 0

Related Questions