Module case name confusion

882 views Asked by At

I made the mistake of updating software and now I can't run any OUnit tests.

I think I've managed to boil the problem down to a simple REPL session.

$ ocaml -I /opt/local/lib/ocaml/site-lib/oUnit
        OCaml version 4.01.0

# Ounit.assert_equal;;
Error: Wrong file naming: /opt/local/lib/ocaml/site-lib/oUnit/ounit.cmi
contains the compiled interface for
Ounit when OUnit was expected
# OUnit.assert_equal;;
Error: Reference to undefined global `OUnit'

Any ideas what I'm doing wrong?

I'm running this on a Mac laptop which has the default case-insensitive/case-preserving file-system, but dinking with the case of the include path doesn't help.


My larger problem manifests thus:

ocamlbuild \
  -libs \
  nums,str,unix,oUnit,graph \
  -cflags \
  -g,-w,+a-4,-warn-error,+a-4,-I,/opt/local/lib/ocaml/site-lib/oUnit,-I,/opt/local/lib/ocaml/site-lib/ocamlgraph \
  -lflags \
  -g,-I,/opt/local/lib/ocaml/site-lib/oUnit,-I,/opt/local/lib/ocaml/site-lib/ocamlgraph \
  ./AllTests.native

Error: No implementations provided for the following modules:
     OUnitCore referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit2),
       /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
     OUnitLoggerStd referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
     OUnitUtils referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit2),
       /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
     OUnitConf referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit2),
       /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
     OUnitAssert referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit2),
       /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
     OUnitBracket referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit2),
       /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
     OUnitTest referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit2),
       /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
     OUnitRunner referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
     OUnitChooser referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
     OUnitLogger referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit2),
       /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
     OUnitTestData referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit2)
Command exited with code 2.
Compilation unsuccessful after building 646 targets (645 cached) in 00:00:02.
2

There are 2 answers

1
camlspotter On BEST ANSWER

Your first error is probably because your OS does not distinguish cases in filenames but OCaml does:

$ ocamlobjinfo `ocamlfind query oUnit`/oUnit.cmi
File /mnt/home/jun/.opam/system/lib/oUnit/oUnit.cmi
Unit name: OUnit
Interfaces imported:
    cb146e345f0b34fc8ad4b5afe69d1f20    OUnit
    ...

You can see that the module is called "OUnit" not "Ounit".

The second error is clearly because the library is not loaded into the REPL yet. The REPL's knows the existence of the function, but has no code loaded. It is accessible if you load the library and those on which it depends:

ocaml -I `ocamlfind query oUnit` unix.cma oUnitAdvanced.cma oUnit.cma
        OCaml version 4.01.0

# OUnit.assert_equal;;
- : ?cmp:('a -> 'a -> bool) ->
    ?printer:('a -> string) ->
    ?pp_diff:(Format.formatter -> 'a * 'a -> unit) ->
    ?msg:string -> 'a -> 'a -> unit
= <fun>
0
Mike Samuel On

I solved the problem by changing my ocamlbuild invocation to use -use-ocamlfind which seems to work around all the path confusion.

I also uninstalled all OCaml modules and instead installed OPAM and then installed OUnit and others via OPAM, and re-logged in so that my environment is pristine.

The build command that works for me now is

ocamlbuild \
    -use-ocamlfind \
    -libs   graph \
    -pkgs   str,unix,ounit \
    -cflags -g,-w,+a-4,-warn-error,+a-4,-I,/opt/local/lib/ocaml/site-lib/ocamlgraph \
    -lflags -g,-I,/opt/local/lib/ocaml/site-lib/ocamlgraph \
    ./AllTests.native

which is generated by my updated build script

(* Usage: ocaml make.ml [<ModuleToBuild>.{byte,native}] *)

#load "unix.cma";;

let args = match Array.to_list Sys.argv with
  | []      -> failwith "no program on argv"
  | _::[]   -> ["AllTests.byte"]
  | _::argv -> argv

let library_paths = [
  (* TODO: Figure out why `opam install ocamlgraph` fails *)
  "-I"; "/opt/local/lib/ocaml/site-lib/ocamlgraph";
]

(* If the -p flag is first in the argument list, then replace it
   with a host of flags that enable profiling via ocamlprof. *)
let args, c_opt_flags, l_opt_flags =
  match args with
    | "-p"::rest ->
      (* Treat the targets as debug and profile targets. *)
      "-tags"::"debug,profile"
      (* Use profiling versions of the compilers which instrument
         various flow control constructs with entry counters. *)
      ::"-ocamlc"::"ocamlcp"
      ::"-ocamlopt"::"ocamloptp"
      ::rest,
      (* Instrument all available points. *)
      ["-P"; "a"],
      (* Link with a wrapper that dumps profiling data if program
         exits noramlly. *)
      ["-p"]
    | _ -> args, [], []

let standard_flags = [
  "-use-ocamlfind";
  (* TODO: Figure out why `opam install ocamlgraph` fails *)
  "-libs"; "graph";
  "-pkgs"; "str,unix,ounit";  (* TODO: graph *)
  "-cflags"; (String.concat "," (
    [
      "-g";
      "-w"; "+a-4";
      "-warn-error"; "+a-4";
    ]
    @ c_opt_flags
    @ library_paths));
  "-lflags"; (String.concat "," (
    [
      "-g";
    ]
    @ l_opt_flags
    @ library_paths));
]

let build_command = "ocamlbuild"

let argv = build_command::(standard_flags @ args)

let _ = begin
  Printf.printf "%s\n" (String.concat " \\\n\t" argv);
  flush stdout;
  Unix.execvp build_command (Array.of_list argv);
end;;