diff --git a/native_toplevel/opttopdirs.ml b/native_toplevel/opttopdirs.ml
index 67a04508bce..e8f094fa491 100644
--- a/native_toplevel/opttopdirs.ml
+++ b/native_toplevel/opttopdirs.ml
@@ -34,7 +34,7 @@ let _ = Hashtbl.add directive_table "quit" (Directive_none dir_quit)
let dir_directory s =
let d = expand_directory Config.standard_library s in
- let dir = Load_path.Dir.create d in
+ let dir = Load_path.Dir.create ~hidden:false d in
Load_path.append_dir dir;
toplevel_env :=
Stdlib.String.Set.fold
@@ -62,7 +62,7 @@ let _ =
let _ = Hashtbl.add directive_table "show_dirs"
(Directive_none
(fun () ->
- List.iter print_endline (Load_path.get_paths ())
+ List.iter print_endline (Load_path.get_path_list ())
))
(* To change the current directory *)
diff --git a/native_toplevel/opttoploop.ml b/native_toplevel/opttoploop.ml
index 322f656bb98..f8052dbfc2d 100644
--- a/native_toplevel/opttoploop.ml
+++ b/native_toplevel/opttoploop.ml
@@ -720,17 +720,22 @@ let set_paths () =
but keep the directories that user code linked in with ocamlmktop
may have added to load_path. *)
let expand = Misc.expand_directory Config.standard_library in
- let current_load_path = Load_path.get_paths () in
- let load_path = List.concat [
+ let Load_path.{ visible; hidden } = Load_path.get_paths () in
+ let visible = List.concat [
[ "" ];
List.map expand (List.rev !Compenv.first_include_dirs);
List.map expand (List.rev !Clflags.include_dirs);
List.map expand (List.rev !Compenv.last_include_dirs);
- current_load_path;
+ visible;
[expand "+camlp4"];
]
in
- Load_path.init load_path ~auto_include:Compmisc.auto_include
+ let hidden = List.concat [
+ List.map expand (List.rev !Clflags.hidden_include_dirs);
+ hidden
+ ]
+ in
+ Load_path.init ~auto_include:Compmisc.auto_include ~visible ~hidden
let initialize_toplevel_env () =
toplevel_env := Compmisc.initial_env();
diff --git a/ocaml/.depend b/ocaml/.depend
index 720edbcb329..34869cf6884 100644
--- a/ocaml/.depend
+++ b/ocaml/.depend
@@ -1280,6 +1280,7 @@ typing/persistent_env.cmi : \
typing/subst.cmi \
utils/misc.cmi \
parsing/location.cmi \
+ utils/load_path.cmi \
utils/lazy_backtrack.cmi \
utils/import_info.cmi \
utils/consistbl.cmi \
@@ -4543,6 +4544,7 @@ file_formats/cmt_format.cmi : \
typing/typedtree.cmi \
typing/shape.cmi \
parsing/location.cmi \
+ utils/load_path.cmi \
utils/import_info.cmi \
typing/env.cmi \
utils/compilation_unit.cmi \
diff --git a/ocaml/bytecomp/bytelink.ml b/ocaml/bytecomp/bytelink.ml
index 09c7a7ebe3e..4e5e0e0c948 100644
--- a/ocaml/bytecomp/bytelink.ml
+++ b/ocaml/bytecomp/bytelink.ml
@@ -376,7 +376,7 @@ let link_bytecode ?final_name tolink exec_name standalone =
if check_dlls then begin
(* Initialize the DLL machinery *)
Dll.init_compile !Clflags.no_std_include;
- Dll.add_path (Load_path.get_paths ());
+ Dll.add_path (Load_path.get_path_list ());
try Dll.open_dlls Dll.For_checking sharedobjs
with Failure reason -> raise(Error(Cannot_open_dll reason))
end;
diff --git a/ocaml/debugger/command_line.ml b/ocaml/debugger/command_line.ml
index f311295bbc1..43f4a95f402 100644
--- a/ocaml/debugger/command_line.ml
+++ b/ocaml/debugger/command_line.ml
@@ -262,7 +262,8 @@ let instr_dir ppf lexbuf =
let new_directory = argument_list_eol argument lexbuf in
if new_directory = [] then begin
if yes_or_no "Reinitialize directory list" then begin
- Load_path.init ~auto_include:Compmisc.auto_include !default_load_path;
+ Load_path.init ~auto_include:Compmisc.auto_include
+ ~visible:!default_load_path ~hidden:[];
Envaux.reset_cache ~preserve_persistent_env:false;
Hashtbl.clear Debugger_config.load_path_for;
flush_buffer_list ()
@@ -278,7 +279,8 @@ let instr_dir ppf lexbuf =
List.iter (function x -> add_path (expand_path x)) new_directory'
end;
let print_dirs ppf l = List.iter (function x -> fprintf ppf "@ %s" x) l in
- fprintf ppf "@[<2>Directories: %a@]@." print_dirs (Load_path.get_paths ());
+ fprintf ppf "@[<2>Directories: %a@]@." print_dirs
+ (Load_path.get_path_list ());
Hashtbl.iter
(fun mdl dirs ->
fprintf ppf "@[<2>Source directories for %s: %a@]@." mdl print_dirs
diff --git a/ocaml/debugger/loadprinter.ml b/ocaml/debugger/loadprinter.ml
index 558cb2c116d..9768c0d0de3 100644
--- a/ocaml/debugger/loadprinter.ml
+++ b/ocaml/debugger/loadprinter.ml
@@ -40,8 +40,8 @@ let rec loadfiles ppf name =
Dynlink.loadfile filename;
let d = Filename.dirname name in
if d <> Filename.current_dir_name then begin
- if not (List.mem d (Load_path.get_paths ())) then
- Load_path.add_dir d;
+ if not (List.mem d (Load_path.get_path_list ())) then
+ Load_path.add_dir ~hidden:false d;
end;
fprintf ppf "File %s loaded@."
(if d <> Filename.current_dir_name then
diff --git a/ocaml/debugger/main.ml b/ocaml/debugger/main.ml
index b504ebeee61..006e8fd5bb7 100644
--- a/ocaml/debugger/main.ml
+++ b/ocaml/debugger/main.ml
@@ -224,7 +224,8 @@ let main () =
end;
if !Parameters.version
then printf "\tOCaml Debugger version %s@.@." Config.version;
- Load_path.init ~auto_include:Compmisc.auto_include !default_load_path;
+ Load_path.init ~auto_include:Compmisc.auto_include
+ ~visible:!default_load_path ~hidden:[];
Clflags.recursive_types := true; (* Allow recursive types. *)
toplevel_loop (); (* Toplevel. *)
kill_program ();
diff --git a/ocaml/debugger/parameters.ml b/ocaml/debugger/parameters.ml
index e7b84c8e295..42fc89cd68d 100644
--- a/ocaml/debugger/parameters.ml
+++ b/ocaml/debugger/parameters.ml
@@ -31,7 +31,7 @@ let time = ref true
let version = ref true
let add_path dir =
- Load_path.add_dir dir;
+ Load_path.add_dir ~hidden:false dir;
Envaux.reset_cache ~preserve_persistent_env:false
let add_path_for mdl dir =
diff --git a/ocaml/debugger/program_management.ml b/ocaml/debugger/program_management.ml
index b4a2f2d1ec1..8f6d62bf8e4 100644
--- a/ocaml/debugger/program_management.ml
+++ b/ocaml/debugger/program_management.ml
@@ -128,8 +128,9 @@ let initialize_loading () =
end;
Symbols.clear_symbols ();
Symbols.read_symbols Debugcom.main_frag !program_name;
- let dirs = Load_path.get_paths () @ !Symbols.program_source_dirs in
- Load_path.init ~auto_include:Compmisc.auto_include dirs;
+ let Load_path.{visible; hidden} = Load_path.get_paths () in
+ let visible = visible @ !Symbols.program_source_dirs in
+ Load_path.init ~auto_include:Compmisc.auto_include ~visible ~hidden;
Envaux.reset_cache ~preserve_persistent_env:false;
if !debug_loading then
prerr_endline "Opening a socket...";
diff --git a/ocaml/debugger/source.ml b/ocaml/debugger/source.ml
index b1f9b2ea767..d2045cf61b6 100644
--- a/ocaml/debugger/source.ml
+++ b/ocaml/debugger/source.ml
@@ -40,7 +40,7 @@ let source_of_module pos mdle =
else
acc)
Debugger_config.load_path_for
- (Load_path.get_paths ()) in
+ (Load_path.get_path_list ()) in
let fname = pos.Lexing.pos_fname in
if fname = "" then
let innermost_module =
diff --git a/ocaml/driver/compmisc.ml b/ocaml/driver/compmisc.ml
index d1af4ab0832..c0e2a0c57e3 100644
--- a/ocaml/driver/compmisc.ml
+++ b/ocaml/driver/compmisc.ml
@@ -23,28 +23,36 @@ let auto_include find_in_dir fn =
(* Initialize the search path.
[dir] (default: the current directory)
is always searched first unless -nocwd is specified,
- then the directories specified with the -I option (in command-line order),
- then the standard library directory (unless the -nostdlib option is given).
+ then the directories specified with the -I option (in command line order),
+ then the standard library directory (unless the -nostdlib option is given),
+ then the directories specified with the -H option (in command line order).
*)
let init_path ?(auto_include=auto_include) ?(dir="") () =
- let dirs =
+ let visible =
if !Clflags.use_threads then "+threads" :: !Clflags.include_dirs
else
!Clflags.include_dirs
in
- let dirs =
- !Compenv.last_include_dirs @ dirs @ Config.flexdll_dirs @
- !Compenv.first_include_dirs
+ let visible =
+ List.concat
+ [!Compenv.last_include_dirs;
+ visible;
+ Config.flexdll_dirs;
+ !Compenv.first_include_dirs]
in
- let exp_dirs =
- List.map (Misc.expand_directory Config.standard_library) dirs
+ let visible =
+ List.map (Misc.expand_directory Config.standard_library) visible
in
- let dirs =
+ let visible =
(if !Clflags.no_cwd then [] else [dir])
- @ List.rev_append exp_dirs (Clflags.std_include_dir ())
+ @ List.rev_append visible (Clflags.std_include_dir ())
in
- Load_path.init ~auto_include dirs;
+ let hidden =
+ List.rev_map (Misc.expand_directory Config.standard_library)
+ !Clflags.hidden_include_dirs
+ in
+ Load_path.init ~auto_include ~visible ~hidden;
Env.reset_cache ~preserve_persistent_env:false
(* Return the initial environment in which compilation proceeds. *)
diff --git a/ocaml/driver/main_args.ml b/ocaml/driver/main_args.ml
index efbf682af6f..d545df86b83 100644
--- a/ocaml/driver/main_args.ml
+++ b/ocaml/driver/main_args.ml
@@ -153,6 +153,11 @@ let mk_i f =
let mk_I f =
"-I", Arg.String f, "
Add to the list of include directories"
+let mk_H f =
+ "-H", Arg.String f,
+ " Add to the list of \"hidden\" include directories\n\
+ \ (Like -I, but the program can not directly reference these dependencies)"
+
let mk_impl f =
"-impl", Arg.String f, " Compile as a .ml file"
@@ -849,6 +854,7 @@ module type Common_options = sig
val _no_absname : unit -> unit
val _alert : string -> unit
val _I : string -> unit
+ val _H : string -> unit
val _labels : unit -> unit
val _alias_deps : unit -> unit
val _no_alias_deps : unit -> unit
@@ -1136,6 +1142,7 @@ struct
mk_stop_after ~native:false F._stop_after;
mk_i F._i;
mk_I F._I;
+ mk_H F._H;
mk_impl F._impl;
mk_intf F._intf;
mk_intf_suffix F._intf_suffix;
@@ -1238,6 +1245,7 @@ struct
mk_no_absname F._no_absname;
mk_alert F._alert;
mk_I F._I;
+ mk_H F._H;
mk_init F._init;
mk_labels F._labels;
mk_alias_deps F._alias_deps;
@@ -1346,6 +1354,7 @@ struct
mk_no_probes F._no_probes;
mk_i F._i;
mk_I F._I;
+ mk_H F._H;
mk_impl F._impl;
mk_inline F._inline;
mk_inline_toplevel F._inline_toplevel;
@@ -1489,6 +1498,7 @@ module Make_opttop_options (F : Opttop_options) = struct
mk_alert F._alert;
mk_compact F._compact;
mk_I F._I;
+ mk_H F._H;
mk_init F._init;
mk_inline F._inline;
mk_inline_toplevel F._inline_toplevel;
@@ -1602,6 +1612,7 @@ struct
mk_no_absname F._no_absname;
mk_alert F._alert;
mk_I F._I;
+ mk_H F._H;
mk_impl F._impl;
mk_intf F._intf;
mk_intf_suffix F._intf_suffix;
@@ -1745,7 +1756,8 @@ module Default = struct
module Core = struct
include Common
- let _I dir = include_dirs := (dir :: (!include_dirs))
+ let _I dir = include_dirs := dir :: (!include_dirs)
+ let _H dir = hidden_include_dirs := dir :: (!hidden_include_dirs)
let _color = Misc.set_or_ignore color_reader.parse color
let _dlambda = set dump_lambda
let _dparsetree = set dump_parsetree
@@ -1994,6 +2006,11 @@ module Default = struct
(* placeholder:
Odoc_global.include_dirs := (s :: (!Odoc_global.include_dirs))
*) ()
+ let _H(_:string) =
+ (* placeholder:
+ Odoc_global.hidden_include_dirs :=
+ (s :: (!Odoc_global.hidden_include_dirs))
+ *) ()
let _impl (_:string) =
(* placeholder:
Odoc_global.files := ((!Odoc_global.files) @ [Odoc_global.Impl_file s])
diff --git a/ocaml/driver/main_args.mli b/ocaml/driver/main_args.mli
index ce43a464353..c6b66bfe9e7 100644
--- a/ocaml/driver/main_args.mli
+++ b/ocaml/driver/main_args.mli
@@ -21,6 +21,7 @@ module type Common_options = sig
val _no_absname : unit -> unit
val _alert : string -> unit
val _I : string -> unit
+ val _H : string -> unit
val _labels : unit -> unit
val _alias_deps : unit -> unit
val _no_alias_deps : unit -> unit
diff --git a/ocaml/driver/makedepend.ml b/ocaml/driver/makedepend.ml
index b0bc4b56205..e0076083bce 100644
--- a/ocaml/driver/makedepend.ml
+++ b/ocaml/driver/makedepend.ml
@@ -408,7 +408,8 @@ let process_file_as process_fun def source_file =
load_path := [];
let cwd = if !nocwd then [] else [Filename.current_dir_name] in
List.iter add_to_load_path (
- (!Compenv.last_include_dirs @
+ (!Clflags.hidden_include_dirs @
+ !Compenv.last_include_dirs @
!Clflags.include_dirs @
!Compenv.first_include_dirs @
cwd
@@ -609,6 +610,8 @@ let run_main argv =
" Dump the delayed dependency map for each map file";
"-I", Arg.String (add_to_list Clflags.include_dirs),
" Add to the list of include directories";
+ "-H", Arg.String (add_to_list Clflags.hidden_include_dirs),
+ " Add to the list of include directories";
"-nocwd", Arg.Set nocwd,
" Do not add current working directory to \
the list of include directories";
diff --git a/ocaml/file_formats/cmt_format.ml b/ocaml/file_formats/cmt_format.ml
index 1a82c5131a6..9e8381bb395 100644
--- a/ocaml/file_formats/cmt_format.ml
+++ b/ocaml/file_formats/cmt_format.ml
@@ -54,7 +54,7 @@ type cmt_infos = {
cmt_args : string array;
cmt_sourcefile : string option;
cmt_builddir : string;
- cmt_loadpath : string list;
+ cmt_loadpath : Load_path.paths;
cmt_source_digest : Digest.t option;
cmt_initial_env : Env.t;
cmt_imports : Import_info.t array;
diff --git a/ocaml/file_formats/cmt_format.mli b/ocaml/file_formats/cmt_format.mli
index e1b233fa684..a2b6fd6c464 100644
--- a/ocaml/file_formats/cmt_format.mli
+++ b/ocaml/file_formats/cmt_format.mli
@@ -57,7 +57,7 @@ type cmt_infos = {
cmt_args : string array;
cmt_sourcefile : string option;
cmt_builddir : string;
- cmt_loadpath : string list;
+ cmt_loadpath : Load_path.paths;
cmt_source_digest : string option;
cmt_initial_env : Env.t;
cmt_imports : Import_info.t array;
diff --git a/ocaml/manual/src/cmds/ocamldep.etex b/ocaml/manual/src/cmds/ocamldep.etex
index 93d6741d02b..1213cb9b1b2 100644
--- a/ocaml/manual/src/cmds/ocamldep.etex
+++ b/ocaml/manual/src/cmds/ocamldep.etex
@@ -65,6 +65,12 @@ and no dependencies are generated. For programs that span multiple
directories, it is recommended to pass "ocamldep" the same "-I" options
that are passed to the compiler.
+\item["-H" \var{directory}]
+Behaves identically to "-I", except that the "-H" directories are searched
+last. This flag is included to make it easier to invoke "ocamldep" with
+the same options as the compiler, where "-H" is used for transitive
+dependencies that the program should not directly mention.
+
\item["-nocwd"]
Do not add current working directory to the list of include directories.
diff --git a/ocaml/manual/src/cmds/ocamldoc.etex b/ocaml/manual/src/cmds/ocamldoc.etex
index e2488f6f0a3..51022fd571c 100644
--- a/ocaml/manual/src/cmds/ocamldoc.etex
+++ b/ocaml/manual/src/cmds/ocamldoc.etex
@@ -207,6 +207,10 @@ They have the same meaning as for the "ocamlc" and "ocamlopt" commands.
Add \var{directory} to the list of directories search for compiled
interface files (".cmi" files).
+\item["-H" \var{directory}]
+Like "-I", but the "-H" directories are searched last and the program may
+not directly refer to the modules added to the search path this way.
+
\item["-nolabels"]
Ignore non-optional labels in types.
diff --git a/ocaml/manual/src/cmds/unified-options.etex b/ocaml/manual/src/cmds/unified-options.etex
index b855ba4a2bf..e74f291da86 100644
--- a/ocaml/manual/src/cmds/unified-options.etex
+++ b/ocaml/manual/src/cmds/unified-options.etex
@@ -300,6 +300,16 @@ the toplevel is running with the "#directory" directive
(section~\ref{s:toplevel-directives}).
}%top
+\notop{%
+\item["-H" \var{directory}]
+Behaves identically to "-I", except that (a) programs may not directly refer to
+modules added to the search path this way, and (b) these directories are
+searched after any "-I" directories. This makes it possible to provide the
+compiler with compiled interface and object code files for the current program's
+transitive dependencies (the dependencies of its dependencies) without allowing
+them to silently become direct dependencies.
+}%notop
+
\top{%
\item["-init" \var{file}]
Load the given file instead of the default initialization file.
diff --git a/ocaml/ocamldoc/odoc_args.ml b/ocaml/ocamldoc/odoc_args.ml
index cad9ae4a13e..8d61505931d 100644
--- a/ocaml/ocamldoc/odoc_args.ml
+++ b/ocaml/ocamldoc/odoc_args.ml
@@ -187,6 +187,7 @@ let anonymous f =
module Options = Main_args.Make_ocamldoc_options(struct
include Main_args.Default.Odoc_args
let _I s = Odoc_global.include_dirs := s :: !Odoc_global.include_dirs
+ let _H s = Odoc_global.hidden_include_dirs := s :: !Odoc_global.hidden_include_dirs
let _impl s = Odoc_global.files := !Odoc_global.files @ [Odoc_global.Impl_file s]
let _intf s = Odoc_global.files := !Odoc_global.files @ [Odoc_global.Intf_file s]
end)
diff --git a/ocaml/ocamldoc/odoc_global.ml b/ocaml/ocamldoc/odoc_global.ml
index 3bb1a67cd4a..221745438a0 100644
--- a/ocaml/ocamldoc/odoc_global.ml
+++ b/ocaml/ocamldoc/odoc_global.ml
@@ -24,6 +24,7 @@ type source_file =
| Text_file of string
let include_dirs = Clflags.include_dirs
+let hidden_include_dirs = Clflags.hidden_include_dirs
let errors = ref 0
diff --git a/ocaml/ocamldoc/odoc_global.mli b/ocaml/ocamldoc/odoc_global.mli
index c85b453453b..1e3df3ec2ae 100644
--- a/ocaml/ocamldoc/odoc_global.mli
+++ b/ocaml/ocamldoc/odoc_global.mli
@@ -24,6 +24,9 @@ type source_file =
(** The include_dirs in the OCaml compiler. *)
val include_dirs : string list ref
+(** The hidden_include_dirs in the OCaml compiler. *)
+val hidden_include_dirs : string list ref
+
(** The merge options to be used. *)
val merge_options : Odoc_types.merge_option list ref
diff --git a/ocaml/ocamldoc/odoc_info.ml b/ocaml/ocamldoc/odoc_info.ml
index d4a73fe1ed9..8a80224452f 100644
--- a/ocaml/ocamldoc/odoc_info.ml
+++ b/ocaml/ocamldoc/odoc_info.ml
@@ -111,6 +111,7 @@ module Module = Odoc_module
let analyse_files
?(merge_options=([] : Odoc_types.merge_option list))
?(include_dirs=([] : string list))
+ ?(hidden_include_dirs=([] : string list))
?(labels=false)
?(sort_modules=false)
?(no_stop=false)
@@ -118,6 +119,7 @@ let analyse_files
files =
Odoc_global.merge_options := merge_options;
Odoc_global.include_dirs := include_dirs;
+ Odoc_global.hidden_include_dirs := hidden_include_dirs;
Odoc_global.classic := not labels;
Odoc_global.sort_modules := sort_modules;
Odoc_global.no_stop := no_stop;
diff --git a/ocaml/ocamldoc/odoc_info.mli b/ocaml/ocamldoc/odoc_info.mli
index da53554f892..bb4aa1d89e3 100644
--- a/ocaml/ocamldoc/odoc_info.mli
+++ b/ocaml/ocamldoc/odoc_info.mli
@@ -1082,12 +1082,13 @@ end
val analyse_files :
?merge_options:Odoc_types.merge_option list ->
?include_dirs:string list ->
- ?labels:bool ->
- ?sort_modules:bool ->
- ?no_stop:bool ->
- ?init: Odoc_module.t_module list ->
- Odoc_global.source_file list ->
- Module.t_module list
+ ?hidden_include_dirs:string list ->
+ ?labels:bool ->
+ ?sort_modules:bool ->
+ ?no_stop:bool ->
+ ?init: Odoc_module.t_module list ->
+ Odoc_global.source_file list ->
+ Module.t_module list
(** Dump of a list of modules into a file.
@raise Failure if an error occurs.*)
diff --git a/ocaml/parsing/ast_mapper.ml b/ocaml/parsing/ast_mapper.ml
index 2784aee0209..8a9e908da57 100644
--- a/ocaml/parsing/ast_mapper.ml
+++ b/ocaml/parsing/ast_mapper.ml
@@ -1124,11 +1124,16 @@ module PpxContext = struct
}
let make ~tool_name () =
+ let Load_path.{ visible; hidden } = Load_path.get_paths () in
let fields =
[
lid "tool_name", make_string tool_name;
- lid "include_dirs", make_list make_string !Clflags.include_dirs;
- lid "load_path", make_list make_string (Load_path.get_paths ());
+ lid "include_dirs", make_list make_string (!Clflags.include_dirs);
+ lid "hidden_include_dirs",
+ make_list make_string (!Clflags.hidden_include_dirs);
+ lid "load_path",
+ make_pair (make_list make_string) (make_list make_string)
+ (visible, hidden);
lid "open_modules", make_list make_string !Clflags.open_modules;
lid "for_package", make_option make_string !Clflags.for_package;
lid "debug", make_bool !Clflags.debug;
@@ -1197,6 +1202,8 @@ module PpxContext = struct
tool_name_ref := get_string payload
| "include_dirs" ->
Clflags.include_dirs := get_list get_string payload
+ | "hidden_include_dirs" ->
+ Clflags.hidden_include_dirs := get_list get_string payload
| "load_path" ->
(* Duplicates Compmisc.auto_include, since we can't reference Compmisc
from this module. *)
@@ -1207,7 +1214,10 @@ module PpxContext = struct
let alert = Location.auto_include_alert in
Load_path.auto_include_otherlibs alert find_in_dir fn
in
- Load_path.init ~auto_include (get_list get_string payload)
+ let visible, hidden =
+ get_pair (get_list get_string) (get_list get_string) payload
+ in
+ Load_path.init ~auto_include ~visible ~hidden
| "open_modules" ->
Clflags.open_modules := get_list get_string payload
| "for_package" ->
diff --git a/ocaml/parsing/ast_mapper.mli b/ocaml/parsing/ast_mapper.mli
index 25965b63792..89b6b23bb17 100644
--- a/ocaml/parsing/ast_mapper.mli
+++ b/ocaml/parsing/ast_mapper.mli
@@ -148,8 +148,8 @@ val tool_name: unit -> string
["ocaml"], ... Some global variables that reflect command-line
options are automatically synchronized between the calling tool
and the ppx preprocessor: {!Clflags.include_dirs},
- {!Load_path}, {!Clflags.open_modules}, {!Clflags.for_package},
- {!Clflags.debug}. *)
+ {!Clflags.hidden_include_dirs}, {!Load_path}, {!Clflags.open_modules},
+ {!Clflags.for_package}, {!Clflags.debug}. *)
val apply: source:string -> target:string -> mapper -> unit
diff --git a/ocaml/testsuite/tests/hidden_includes/cant_reference_hidden.ocamlc.reference b/ocaml/testsuite/tests/hidden_includes/cant_reference_hidden.ocamlc.reference
new file mode 100644
index 00000000000..caae6f73442
--- /dev/null
+++ b/ocaml/testsuite/tests/hidden_includes/cant_reference_hidden.ocamlc.reference
@@ -0,0 +1,4 @@
+File "libc/c3.ml", line 1, characters 8-11:
+1 | let x = A.x + 1
+ ^^^
+Error: Unbound module "A"
diff --git a/ocaml/testsuite/tests/hidden_includes/hidden_stays_hidden.ocamlc.reference b/ocaml/testsuite/tests/hidden_includes/hidden_stays_hidden.ocamlc.reference
new file mode 100644
index 00000000000..8ba62b877af
--- /dev/null
+++ b/ocaml/testsuite/tests/hidden_includes/hidden_stays_hidden.ocamlc.reference
@@ -0,0 +1,4 @@
+File "libc/c4.ml", line 2, characters 8-11:
+2 | let y = A.x + 1
+ ^^^
+Error: Unbound module "A"
diff --git a/ocaml/testsuite/tests/hidden_includes/liba/a.ml b/ocaml/testsuite/tests/hidden_includes/liba/a.ml
new file mode 100644
index 00000000000..22b40309256
--- /dev/null
+++ b/ocaml/testsuite/tests/hidden_includes/liba/a.ml
@@ -0,0 +1,5 @@
+type t = int
+
+let x = 1
+
+type s = Baz
diff --git a/ocaml/testsuite/tests/hidden_includes/liba_alt/a.ml b/ocaml/testsuite/tests/hidden_includes/liba_alt/a.ml
new file mode 100644
index 00000000000..e907a667ba2
--- /dev/null
+++ b/ocaml/testsuite/tests/hidden_includes/liba_alt/a.ml
@@ -0,0 +1,3 @@
+type t = string
+
+let x = "hi"
diff --git a/ocaml/testsuite/tests/hidden_includes/libb/b.ml b/ocaml/testsuite/tests/hidden_includes/libb/b.ml
new file mode 100644
index 00000000000..7e41643e960
--- /dev/null
+++ b/ocaml/testsuite/tests/hidden_includes/libb/b.ml
@@ -0,0 +1,7 @@
+type t = A.t
+
+let x : A.t = A.x
+
+let f : A.t -> A.t = fun x -> x
+
+let g : A.s -> unit = fun _ -> ()
diff --git a/ocaml/testsuite/tests/hidden_includes/libc/c1.ml b/ocaml/testsuite/tests/hidden_includes/libc/c1.ml
new file mode 100644
index 00000000000..e0e5a1fd8d9
--- /dev/null
+++ b/ocaml/testsuite/tests/hidden_includes/libc/c1.ml
@@ -0,0 +1,3 @@
+let x = B.x + 1
+
+let () = Printf.printf "%d\n" x
diff --git a/ocaml/testsuite/tests/hidden_includes/libc/c2.ml b/ocaml/testsuite/tests/hidden_includes/libc/c2.ml
new file mode 100644
index 00000000000..dee5a2e74b9
--- /dev/null
+++ b/ocaml/testsuite/tests/hidden_includes/libc/c2.ml
@@ -0,0 +1 @@
+let x = B.f B.x
diff --git a/ocaml/testsuite/tests/hidden_includes/libc/c3.ml b/ocaml/testsuite/tests/hidden_includes/libc/c3.ml
new file mode 100644
index 00000000000..88c0aa8d0d6
--- /dev/null
+++ b/ocaml/testsuite/tests/hidden_includes/libc/c3.ml
@@ -0,0 +1 @@
+let x = A.x + 1
diff --git a/ocaml/testsuite/tests/hidden_includes/libc/c4.ml b/ocaml/testsuite/tests/hidden_includes/libc/c4.ml
new file mode 100644
index 00000000000..7c14a26c835
--- /dev/null
+++ b/ocaml/testsuite/tests/hidden_includes/libc/c4.ml
@@ -0,0 +1,6 @@
+let x = B.x + 1
+let y = A.x + 1
+
+(* Typing x requires loading A's cmi. When it is made available with -H, y
+ should fail to typecheck because direct references to A are not allowed, even
+ though it has been loaded. *)
diff --git a/ocaml/testsuite/tests/hidden_includes/libc/c5.ml b/ocaml/testsuite/tests/hidden_includes/libc/c5.ml
new file mode 100644
index 00000000000..bd0715ec1e8
--- /dev/null
+++ b/ocaml/testsuite/tests/hidden_includes/libc/c5.ml
@@ -0,0 +1,7 @@
+let _ = B.g Baz
+
+(* Type-directed disambiguation: Baz is defined in A, and even when a.cmi is
+ provided with -H this still typechecks. It's not obvious that this is
+ necessary (rejecting this program also seems fine, in that case), but this
+ test is here to record the current behavior so any change will be
+ intentional. *)
diff --git a/ocaml/testsuite/tests/hidden_includes/not_included.ocamlc.reference b/ocaml/testsuite/tests/hidden_includes/not_included.ocamlc.reference
new file mode 100644
index 00000000000..a3c0d1055eb
--- /dev/null
+++ b/ocaml/testsuite/tests/hidden_includes/not_included.ocamlc.reference
@@ -0,0 +1,7 @@
+File "libc/c1.ml", line 1, characters 8-11:
+1 | let x = B.x + 1
+ ^^^
+Error: This expression has type "A.t" but an expression was expected of type
+ "int"
+ Type "A.t" is abstract because no corresponding cmi file was found
+ in path.
diff --git a/ocaml/testsuite/tests/hidden_includes/test.ml b/ocaml/testsuite/tests/hidden_includes/test.ml
new file mode 100644
index 00000000000..4ea735dfba6
--- /dev/null
+++ b/ocaml/testsuite/tests/hidden_includes/test.ml
@@ -0,0 +1,159 @@
+(* TEST
+(* This tests the -H flag.
+
+ The basic structure is that libc depends on libb, which depends on liba. We
+ want to test a few things:
+
+ - Compiling libc with -I liba allows the compiler to see the type definitions
+ in liba and allows c.ml to reference it directly.
+
+ - Compiling libc with -H liba allows the compiler to see the type definitions
+ in liba, but doesn't allow c.ml to reference it directly.
+
+ - If -H and -I are are passed for two different versions of liba, the -I one
+ takes priority.
+
+ - If -H is passed twice with two different versions of liba, the first takes
+ priority.
+
+ The liba_alt directory has an alternate versions of liba used for testing the
+ precedence order of the includes.
+*)
+
+subdirectories = "liba liba_alt libb libc";
+setup-ocamlc.byte-build-env;
+
+flags = "-I liba -nocwd";
+module = "liba/a.ml";
+ocamlc.byte;
+
+flags = "-I liba_alt -nocwd";
+module = "liba_alt/a.ml";
+ocamlc.byte;
+
+flags = "-I liba -I libb -nocwd";
+module = "libb/b.ml";
+ocamlc.byte;
+{
+ (* Test hiding A completely *)
+ flags = "-I libb -nocwd";
+ module = "libc/c2.ml";
+ setup-ocamlc.byte-build-env;
+ ocamlc.byte;
+}
+{
+ (* Test hiding A completely, but using it *)
+ flags = "-I libb -nocwd";
+ module = "libc/c1.ml";
+ setup-ocamlc.byte-build-env;
+ ocamlc_byte_exit_status = "2";
+ ocamlc.byte;
+ compiler_reference = "${test_source_directory}/not_included.ocamlc.reference";
+ check-ocamlc.byte-output;
+}
+{
+ (* Test transitive use of A's cmi with -I. *)
+ flags = "-I liba -I libb -nocwd";
+ module = "libc/c1.ml";
+ setup-ocamlc.byte-build-env;
+ ocamlc.byte;
+}
+{
+ (* Test transitive use of A's cmi with -H. *)
+ flags = "-H liba -I libb -nocwd";
+ module = "libc/c1.ml";
+ setup-ocamlc.byte-build-env;
+ ocamlc.byte;
+}
+{
+ (* Test direct use of A cmi with -H. *)
+ flags = "-H liba -I libb -nocwd";
+ module = "libc/c3.ml";
+ setup-ocamlc.byte-build-env;
+ ocamlc_byte_exit_status = "2";
+ ocamlc.byte;
+ compiler_reference =
+ "${test_source_directory}/cant_reference_hidden.ocamlc.reference";
+ check-ocamlc.byte-output;
+}
+
+(* The next four tests check that -I takes priority over -H regardless of the
+ order on the command line.
+*)
+{
+ flags = "-H liba_alt -I liba -I libb -nocwd";
+ module = "libc/c1.ml";
+ setup-ocamlc.byte-build-env;
+ ocamlc.byte;
+}
+{
+ flags = "-I liba -H liba_alt -I libb -nocwd";
+ module = "libc/c1.ml";
+ setup-ocamlc.byte-build-env;
+ ocamlc.byte;
+}
+{
+ not-windows;
+ flags = "-H liba -I liba_alt -I libb -nocwd";
+ module = "libc/c1.ml";
+ setup-ocamlc.byte-build-env;
+ ocamlc_byte_exit_status = "2";
+ ocamlc.byte;
+ compiler_reference =
+ "${test_source_directory}/wrong_include_order.ocamlc.reference";
+ check-ocamlc.byte-output;
+}
+{
+ not-windows;
+ flags = "-I liba_alt -H liba -I libb -nocwd";
+ module = "libc/c1.ml";
+ setup-ocamlc.byte-build-env;
+ ocamlc_byte_exit_status = "2";
+ ocamlc.byte;
+ compiler_reference =
+ "${test_source_directory}/wrong_include_order.ocamlc.reference";
+ check-ocamlc.byte-output;
+}
+
+(* The next two tests show that earlier -Hs take priority over later -Hs *)
+{
+ not-windows;
+ flags = "-H liba_alt -H liba -I libb -nocwd";
+ module = "libc/c1.ml";
+ setup-ocamlc.byte-build-env;
+ ocamlc_byte_exit_status = "2";
+ ocamlc.byte;
+ compiler_reference =
+ "${test_source_directory}/wrong_include_order.ocamlc.reference";
+ check-ocamlc.byte-output;
+}
+{
+ flags = "-H liba -H liba_alt -I libb -nocwd";
+ module = "libc/c1.ml";
+ setup-ocamlc.byte-build-env;
+ ocamlc.byte;
+}
+
+(* Test that a hidden `A` doesn't become visible as a result of the typechecker
+ using it. *)
+{
+ flags = "-H liba -I libb -nocwd";
+ module = "libc/c4.ml";
+ setup-ocamlc.byte-build-env;
+ ocamlc_byte_exit_status = "2";
+ ocamlc.byte;
+ compiler_reference =
+ "${test_source_directory}/hidden_stays_hidden.ocamlc.reference";
+ check-ocamlc.byte-output;
+}
+
+(* Test that type-directed constructor disambiguation works through -H (at
+ least, for now). *)
+{
+ flags = "-H liba -I libb -nocwd";
+ module = "libc/c5.ml";
+ setup-ocamlc.byte-build-env;
+ ocamlc.byte;
+}
+
+*)
diff --git a/ocaml/testsuite/tests/hidden_includes/wrong_include_order.ocamlc.reference b/ocaml/testsuite/tests/hidden_includes/wrong_include_order.ocamlc.reference
new file mode 100644
index 00000000000..6863dffdf5e
--- /dev/null
+++ b/ocaml/testsuite/tests/hidden_includes/wrong_include_order.ocamlc.reference
@@ -0,0 +1,3 @@
+File "libc/c1.ml", line 1:
+Error: The files "libb/b.cmi" and "liba_alt/a.cmi" make inconsistent assumptions
+ over interface "A"
diff --git a/ocaml/testsuite/tests/self-contained-toplevel/main.ml b/ocaml/testsuite/tests/self-contained-toplevel/main.ml
index c89c0485cdf..46cb4377627 100644
--- a/ocaml/testsuite/tests/self-contained-toplevel/main.ml
+++ b/ocaml/testsuite/tests/self-contained-toplevel/main.ml
@@ -23,7 +23,7 @@ let () =
if Sys.file_exists "foo.cmi" then Sys.remove "foo.cmi";
let module Persistent_signature = Persistent_env.Persistent_signature in
let old_loader = !Persistent_signature.load in
- Persistent_signature.load := (fun ~unit_name ->
+ Persistent_signature.load := (fun ~allow_hidden ~unit_name ->
match unit_name |> Compilation_unit.Name.to_string with
| "Foo" ->
let {Cmi_format.cmi_name; cmi_sign; cmi_crcs; cmi_flags} =
@@ -37,10 +37,11 @@ let () =
}
in
Some { Persistent_signature.
- filename = Sys.executable_name
- ; cmi = cmi
+ filename = Sys.executable_name
+ ; cmi = cmi
+ ; visibility = Visible
}
- | _ -> old_loader unit_name);
+ | _ -> old_loader ~allow_hidden ~unit_name);
Toploop.add_hook (function
| Toploop.After_setup ->
Toploop.toplevel_env :=
diff --git a/ocaml/tools/objinfo.ml b/ocaml/tools/objinfo.ml
index ba958bec3bf..2e20960d9a3 100644
--- a/ocaml/tools/objinfo.ml
+++ b/ocaml/tools/objinfo.ml
@@ -107,8 +107,10 @@ let print_cmt_infos cmt =
(match cmt.cmt_sourcefile with None -> "(none)" | Some f -> f);
printf "Compilation flags:";
Array.iter print_spaced_string cmt.cmt_args;
- printf "\nLoad path:";
- List.iter print_spaced_string cmt.cmt_loadpath;
+ printf "\nLoad path:\n Visible:";
+ List.iter print_spaced_string cmt.cmt_loadpath.visible;
+ printf "\n Hidden:";
+ List.iter print_spaced_string cmt.cmt_loadpath.hidden;
printf "\n";
printf "cmt interface digest: %s\n"
(match cmt.cmt_interface_digest with
diff --git a/ocaml/tools/ocamlcmt.ml b/ocaml/tools/ocamlcmt.ml
index 1bb39e10cc3..31169a1877d 100644
--- a/ocaml/tools/ocamlcmt.ml
+++ b/ocaml/tools/ocamlcmt.ml
@@ -69,7 +69,10 @@ let print_info cmt =
Printf.fprintf oc "sourcefile: %s\n" name;
end;
Printf.fprintf oc "build directory: %s\n" cmt.cmt_builddir;
- List.iter (Printf.fprintf oc "load path: %s\n%!") cmt.cmt_loadpath;
+ List.iter (Printf.fprintf oc "load path (visible): %s\n%!")
+ cmt.cmt_loadpath.visible;
+ List.iter (Printf.fprintf oc "load path (hidden): %s\n%!")
+ cmt.cmt_loadpath.hidden;
begin
match cmt.cmt_source_digest with
None -> ()
@@ -161,7 +164,8 @@ let record_cmt_info cmt =
Annot.Idef (location_file value)))
in
let open Cmt_format in
- List.iter (fun dir -> record_info "include" dir) cmt.cmt_loadpath;
+ List.iter (fun dir -> record_info "include" dir) cmt.cmt_loadpath.visible;
+ List.iter (fun dir -> record_info "include" dir) cmt.cmt_loadpath.hidden;
record_info "chdir" cmt.cmt_builddir;
(match cmt.cmt_sourcefile with
None -> () | Some file -> record_info "source" file)
@@ -186,7 +190,8 @@ let main () =
| Some _ as x -> x
in
Envaux.reset_cache ~preserve_persistent_env:false;
- List.iter Load_path.add_dir cmt.cmt_loadpath;
+ List.iter (Load_path.add_dir ~hidden:false) cmt.cmt_loadpath.visible;
+ List.iter (Load_path.add_dir ~hidden:true) cmt.cmt_loadpath.hidden;
Cmt2annot.gen_annot target_filename
~sourcefile:cmt.cmt_sourcefile
~use_summaries:cmt.cmt_use_summaries
diff --git a/ocaml/toplevel/topcommon.ml b/ocaml/toplevel/topcommon.ml
index 443fcc64f77..cba2403ca53 100644
--- a/ocaml/toplevel/topcommon.ml
+++ b/ocaml/toplevel/topcommon.ml
@@ -268,18 +268,23 @@ let set_paths ?(auto_include=Compmisc.auto_include) () =
but keep the directories that user code linked in with ocamlmktop
may have added to load_path. *)
let expand = Misc.expand_directory Config.standard_library in
- let current_load_path = Load_path.get_paths () in
- let load_path = List.concat [
+ let Load_path.{ visible; hidden } = Load_path.get_paths () in
+ let visible = List.concat [
[ "" ];
List.map expand (List.rev !Compenv.first_include_dirs);
List.map expand (List.rev !Clflags.include_dirs);
List.map expand (List.rev !Compenv.last_include_dirs);
- current_load_path;
+ visible;
[expand "+camlp4"];
]
in
- Load_path.init ~auto_include load_path;
- Dll.add_path load_path
+ let hidden = List.concat [
+ List.map expand (List.rev !Clflags.hidden_include_dirs);
+ hidden
+ ]
+ in
+ Load_path.init ~auto_include ~visible ~hidden;
+ Dll.add_path (visible @ hidden)
let update_search_path_from_env () =
let extra_paths =
diff --git a/ocaml/toplevel/topdirs.ml b/ocaml/toplevel/topdirs.ml
index 3c352c0c59b..2232e4cdc3d 100644
--- a/ocaml/toplevel/topdirs.ml
+++ b/ocaml/toplevel/topdirs.ml
@@ -75,7 +75,7 @@ let _ = add_directive "quit" (Directive_none dir_quit)
let dir_directory s =
let d = expand_directory Config.standard_library s in
Dll.add_path [d];
- let dir = Load_path.Dir.create d in
+ let dir = Load_path.Dir.create ~hidden:false d in
Load_path.prepend_dir dir;
toplevel_env :=
Stdlib.String.Set.fold
@@ -110,7 +110,7 @@ let _ = add_directive "remove_directory" (Directive_string dir_remove_directory)
}
let dir_show_dirs () =
- List.iter print_endline (Load_path.get_paths ())
+ List.iter print_endline (Load_path.get_path_list ())
let _ = add_directive "show_dirs" (Directive_none dir_show_dirs)
{
diff --git a/ocaml/typing/env.ml b/ocaml/typing/env.ml
index b34cb2c1077..7849f12efc3 100644
--- a/ocaml/typing/env.ml
+++ b/ocaml/typing/env.ml
@@ -1113,7 +1113,7 @@ let find_ident_module id env =
match find_same_module id env.modules with
| Mod_local data -> data
| Mod_unbound _ -> raise Not_found
- | Mod_persistent -> find_pers_mod (id |> modname_of_ident)
+ | Mod_persistent -> find_pers_mod ~allow_hidden:true (id |> modname_of_ident)
let rec find_module_components path env =
match path with
@@ -2695,8 +2695,10 @@ let save_signature_with_transform cmi_transform ~alerts sg modname filename =
let cmi =
Persistent_env.make_cmi !persistent_env modname sg alerts
|> cmi_transform in
- Persistent_env.save_cmi !persistent_env
- { Persistent_env.Persistent_signature.filename; cmi };
+ let pers_sig =
+ Persistent_env.Persistent_signature.{ filename; cmi; visibility = Visible }
+ in
+ Persistent_env.save_cmi !persistent_env pers_sig;
cmi
let save_signature ~alerts sg modname filename =
@@ -2935,10 +2937,10 @@ let lookup_ident_module (type a) (load : a load) ~errors ~use ~loc s env =
let name = s |> Compilation_unit.Name.of_string in
match load with
| Don't_load ->
- check_pers_mod ~loc name;
+ check_pers_mod ~allow_hidden:false ~loc name;
path, (() : a)
| Load -> begin
- match find_pers_mod name with
+ match find_pers_mod ~allow_hidden:false name with
| mda ->
use_module ~use ~loc path mda;
path, (mda : a)
@@ -3546,7 +3548,10 @@ let bound_module name env =
| exception Not_found ->
if Current_unit_name.is name then false
else begin
- match find_pers_mod (name |> Compilation_unit.Name.of_string) with
+ match
+ find_pers_mod ~allow_hidden:false
+ (name |> Compilation_unit.Name.of_string)
+ with
| _ -> true
| exception Not_found -> false
end
diff --git a/ocaml/typing/persistent_env.ml b/ocaml/typing/persistent_env.ml
index dc7430cf983..fd32d09c6da 100644
--- a/ocaml/typing/persistent_env.ml
+++ b/ocaml/typing/persistent_env.ml
@@ -40,13 +40,18 @@ let error err = raise (Error err)
module Persistent_signature = struct
type t =
{ filename : string;
- cmi : Cmi_format.cmi_infos_lazy }
-
- let load = ref (fun ~unit_name ->
- let unit_name = CU.Name.to_string unit_name in
- match Load_path.find_uncap (unit_name ^ ".cmi") with
- | filename -> Some { filename; cmi = read_cmi_lazy filename }
- | exception Not_found -> None)
+ cmi : Cmi_format.cmi_infos_lazy;
+ visibility : Load_path.visibility }
+
+ let load = ref (fun ~allow_hidden ~unit_name ->
+ let unit_name = CU.Name.to_string unit_name in
+ match Load_path.find_uncap_with_visibility (unit_name ^ ".cmi") with
+ | filename, visibility when allow_hidden ->
+ Some { filename; cmi = read_cmi_lazy filename; visibility}
+ | filename, Visible ->
+ Some { filename; cmi = read_cmi_lazy filename; visibility = Visible}
+ | _, Hidden
+ | exception Not_found -> None)
end
type can_load_cmis =
@@ -58,6 +63,7 @@ type pers_struct = {
ps_crcs: Import_info.t array;
ps_filename: string;
ps_flags: pers_flags list;
+ ps_visibility: Load_path.visibility;
}
(* If a .cmi file is missing (or invalid), we
@@ -180,7 +186,7 @@ let save_pers_struct penv crc comp_unit flags filename =
add_import penv modname
let process_pers_struct penv check modname pers_sig =
- let { Persistent_signature.filename; cmi } = pers_sig in
+ let { Persistent_signature.filename; cmi; visibility } = pers_sig in
let name = cmi.cmi_name in
let crcs = cmi.cmi_crcs in
let flags = cmi.cmi_flags in
@@ -188,6 +194,7 @@ let process_pers_struct penv check modname pers_sig =
ps_crcs = crcs;
ps_filename = filename;
ps_flags = flags;
+ ps_visibility = visibility;
} in
let found_name = CU.name name in
if not (CU.Name.equal modname found_name) then
@@ -225,27 +232,29 @@ let acknowledge_pers_struct penv check modname pers_sig pm =
let read_pers_struct penv val_of_pers_sig check modname filename ~add_binding =
add_import penv modname;
let cmi = read_cmi_lazy filename in
- let pers_sig = { Persistent_signature.filename; cmi } in
+ let pers_sig = { Persistent_signature.filename; cmi; visibility = Visible } in
let pm = val_of_pers_sig pers_sig in
let ps = process_pers_struct penv check modname pers_sig in
if add_binding then bind_pers_struct penv modname ps pm;
(ps, pm)
-let find_pers_struct penv val_of_pers_sig check name =
+let find_pers_struct ~allow_hidden penv val_of_pers_sig check name =
let {persistent_structures; _} = penv in
if CU.Name.equal name CU.Name.predef_exn then raise Not_found;
match Hashtbl.find persistent_structures name with
- | Found (ps, pm) -> (ps, pm)
+ | Found (ps, pm) when allow_hidden || ps.ps_visibility = Load_path.Visible ->
+ (ps, pm)
+ | Found _ -> raise Not_found
| Missing -> raise Not_found
| exception Not_found ->
match can_load_cmis penv with
| Cannot_load_cmis _ -> raise Not_found
| Can_load_cmis ->
let psig =
- match !Persistent_signature.load ~unit_name:name with
+ match !Persistent_signature.load ~allow_hidden ~unit_name:name with
| Some psig -> psig
| None ->
- Hashtbl.add persistent_structures name Missing;
+ if allow_hidden then Hashtbl.add persistent_structures name Missing;
raise Not_found
in
add_import penv name;
@@ -260,10 +269,10 @@ let describe_prefix ppf prefix =
Format.fprintf ppf "package %a" CU.Prefix.print prefix
(* Emits a warning if there is no valid cmi for name *)
-let check_pers_struct penv f ~loc name =
+let check_pers_struct ~allow_hidden penv f ~loc name =
let name_as_string = CU.Name.to_string name in
try
- ignore (find_pers_struct penv f false name)
+ ignore (find_pers_struct ~allow_hidden penv f false name)
with
| Not_found ->
let warn = Warnings.No_cmi_file(name_as_string, None) in
@@ -300,10 +309,10 @@ let check_pers_struct penv f ~loc name =
let read penv f modname filename ~add_binding =
snd (read_pers_struct penv f true modname filename ~add_binding)
-let find penv f name =
- snd (find_pers_struct penv f true name)
+let find ~allow_hidden penv f name =
+ snd (find_pers_struct ~allow_hidden penv f true name)
-let check penv f ~loc name =
+let check ~allow_hidden penv f ~loc name =
let {persistent_structures; _} = penv in
if not (Hashtbl.mem persistent_structures name) then begin
(* PR#6843: record the weak dependency ([add_import]) regardless of
@@ -312,7 +321,7 @@ let check penv f ~loc name =
add_import penv name;
if (Warnings.is_active (Warnings.No_cmi_file("", None))) then
!add_delayed_check_forward
- (fun () -> check_pers_struct penv f ~loc name)
+ (fun () -> check_pers_struct ~allow_hidden penv f ~loc name)
end
(* CR mshinwell: delete this having moved to 4.14 build compilers *)
@@ -336,7 +345,7 @@ let crc_of_unit penv f name =
match Consistbl.find penv.crc_units name with
| Some (_, crc) -> crc
| None ->
- let (ps, _pm) = find_pers_struct penv f true name in
+ let (ps, _pm) = find_pers_struct ~allow_hidden:true penv f true name in
match Array.find_opt (Import_info.has_name ~name) ps.ps_crcs with
| None -> assert false
| Some import_info ->
@@ -379,7 +388,7 @@ let make_cmi penv modname sign alerts =
}
let save_cmi penv psig =
- let { Persistent_signature.filename; cmi } = psig in
+ let { Persistent_signature.filename; cmi; _ } = psig in
Misc.try_finally (fun () ->
let {
cmi_name = modname;
diff --git a/ocaml/typing/persistent_env.mli b/ocaml/typing/persistent_env.mli
index bfa3f05345d..4e4e8a2cbea 100644
--- a/ocaml/typing/persistent_env.mli
+++ b/ocaml/typing/persistent_env.mli
@@ -38,12 +38,15 @@ val report_error: Format.formatter -> error -> unit
module Persistent_signature : sig
type t =
{ filename : string; (** Name of the file containing the signature. *)
- cmi : Cmi_format.cmi_infos_lazy }
+ cmi : Cmi_format.cmi_infos_lazy;
+ visibility : Load_path.visibility
+ }
(** Function used to load a persistent signature. The default is to look for
the .cmi file in the load path. This function can be overridden to load
it from memory, for instance to build a self-contained toplevel. *)
- val load : (unit_name:Compilation_unit.Name.t -> t option) ref
+ val load :
+ (allow_hidden:bool -> unit_name:Compilation_unit.Name.t -> t option) ref
end
type can_load_cmis =
@@ -63,12 +66,12 @@ val fold : 'a t -> (Compilation_unit.Name.t -> 'a -> 'b -> 'b) -> 'b -> 'b
bind the module name in the environment. *)
val read : 'a t -> (Persistent_signature.t -> 'a)
-> Compilation_unit.Name.t -> filepath -> add_binding:bool -> 'a
-val find : 'a t -> (Persistent_signature.t -> 'a)
+val find : allow_hidden:bool -> 'a t -> (Persistent_signature.t -> 'a)
-> Compilation_unit.Name.t -> 'a
val find_in_cache : 'a t -> Compilation_unit.Name.t -> 'a option
-val check : 'a t -> (Persistent_signature.t -> 'a)
+val check : allow_hidden:bool -> 'a t -> (Persistent_signature.t -> 'a)
-> loc:Location.t -> Compilation_unit.Name.t -> unit
(* [looked_up penv md] checks if one has already tried
diff --git a/ocaml/typing/typemod.ml b/ocaml/typing/typemod.ml
index 9a717f44e6f..b12439ed6a5 100644
--- a/ocaml/typing/typemod.ml
+++ b/ocaml/typing/typemod.ml
@@ -210,7 +210,7 @@ let initial_env ~loc ~initially_opened_module
env
in
let units =
- List.map Env.persistent_structures_of_dir (Load_path.get ())
+ List.map Env.persistent_structures_of_dir (Load_path.get_visible ())
in
let env, units =
match initially_opened_module with
diff --git a/ocaml/utils/ccomp.ml b/ocaml/utils/ccomp.ml
index 41d481b2e80..a6318cf0922 100644
--- a/ocaml/utils/ccomp.ml
+++ b/ocaml/utils/ccomp.ml
@@ -115,7 +115,8 @@ let compile_file ?output ?(opt="") ?stable_name name =
(String.concat " " (List.rev !Clflags.all_ccopts))
(quote_prefixed ~response_files:true "-I"
(List.map (Misc.expand_directory Config.standard_library)
- (List.rev !Clflags.include_dirs)))
+ (List.rev ( !Clflags.hidden_include_dirs
+ @ !Clflags.include_dirs))))
(Clflags.std_include_flag "-I")
(Filename.quote name)
(* cl tediously includes the name of the C file as the first thing it
@@ -185,7 +186,7 @@ let call_linker ?(native_toplevel = false) mode output_name files extra =
Config.native_pack_linker
(Filename.quote output_name)
(quote_prefixed ~response_files:true
- l_prefix (Load_path.get_paths ()))
+ l_prefix (Load_path.get_path_list ()))
(quote_files ~response_files:true (remove_Wl files))
extra
else
@@ -201,7 +202,8 @@ let call_linker ?(native_toplevel = false) mode output_name files extra =
"" (*(Clflags.std_include_flag "-I")*)
(if native_toplevel then ""
else
- quote_prefixed ~response_files:true "-L" (Load_path.get_paths ()))
+ quote_prefixed ~response_files:true "-L"
+ (Load_path.get_path_list ()))
(String.concat " " (List.rev !Clflags.all_ccopts))
(quote_files ~response_files:true files)
extra
diff --git a/ocaml/utils/clflags.ml b/ocaml/utils/clflags.ml
index adb56fdf341..e34c9fba21a 100644
--- a/ocaml/utils/clflags.ml
+++ b/ocaml/utils/clflags.ml
@@ -46,7 +46,8 @@ let cmi_file = ref None
let compile_only = ref false (* -c *)
and output_name = ref (None : string option) (* -o *)
-and include_dirs = ref ([] : string list)(* -I *)
+and include_dirs = ref ([] : string list) (* -I *)
+and hidden_include_dirs = ref ([] : string list) (* -H *)
and no_std_include = ref false (* -nostdlib *)
and no_cwd = ref false (* -nocwd *)
and print_types = ref false (* -i *)
diff --git a/ocaml/utils/clflags.mli b/ocaml/utils/clflags.mli
index 4bdae1f5dcc..c7857f53340 100644
--- a/ocaml/utils/clflags.mli
+++ b/ocaml/utils/clflags.mli
@@ -58,6 +58,7 @@ val cmi_file : string option ref
val compile_only : bool ref
val output_name : string option ref
val include_dirs : string list ref
+val hidden_include_dirs : string list ref
val no_std_include : bool ref
val no_cwd : bool ref
val print_types : bool ref
diff --git a/ocaml/utils/load_path.ml b/ocaml/utils/load_path.ml
index 42330d56232..d3e41aa12e3 100644
--- a/ocaml/utils/load_path.ml
+++ b/ocaml/utils/load_path.ml
@@ -19,17 +19,22 @@ module STbl = Misc.Stdlib.String.Tbl
(* Mapping from basenames to full filenames *)
type registry = string STbl.t
-let files : registry ref = s_table STbl.create 42
-let files_uncap : registry ref = s_table STbl.create 42
+let visible_files : registry ref = s_table STbl.create 42
+let visible_files_uncap : registry ref = s_table STbl.create 42
+
+let hidden_files : registry ref = s_table STbl.create 42
+let hidden_files_uncap : registry ref = s_table STbl.create 42
module Dir = struct
type t = {
path : string;
files : string list;
+ hidden : bool;
}
let path t = t.path
let files t = t.files
+ let hidden t = t.hidden
let find t fn =
if List.mem fn t.files then
@@ -56,26 +61,43 @@ module Dir = struct
with Sys_error _ ->
[||]
- let create path =
- { path; files = Array.to_list (readdir_compat path) }
+ let create ~hidden path =
+ { path; files = Array.to_list (readdir_compat path); hidden }
end
type auto_include_callback =
(Dir.t -> string -> string option) -> string -> string
-let dirs = s_ref []
+let visible_dirs = s_ref []
+let hidden_dirs = s_ref []
let no_auto_include _ _ = raise Not_found
let auto_include_callback = ref no_auto_include
let reset () =
assert (not Config.merlin || Local_store.is_bound ());
- STbl.clear !files;
- STbl.clear !files_uncap;
- dirs := [];
+ STbl.clear !hidden_files;
+ STbl.clear !hidden_files_uncap;
+ STbl.clear !visible_files;
+ STbl.clear !visible_files_uncap;
+ hidden_dirs := [];
+ visible_dirs := [];
auto_include_callback := no_auto_include
-let get () = List.rev !dirs
-let get_paths () = List.rev_map Dir.path !dirs
+let get_visible () = List.rev !visible_dirs
+
+let get_path_list () =
+ Misc.rev_map_end Dir.path !visible_dirs (List.rev_map Dir.path !hidden_dirs)
+
+type paths =
+ { visible : string list;
+ hidden : string list }
+
+let get_paths () =
+ { visible = List.rev_map Dir.path !visible_dirs;
+ hidden = List.rev_map Dir.path !hidden_dirs }
+
+let get_visible_path_list () = List.rev_map Dir.path !visible_dirs
+let get_hidden_path_list () = List.rev_map Dir.path !hidden_dirs
(* Optimized version of [add] below, for use in [init] and [remove_dir]: since
we are starting from an empty cache, we can avoid checking whether a unit
@@ -84,51 +106,72 @@ let get_paths () = List.rev_map Dir.path !dirs
let prepend_add dir =
List.iter (fun base ->
let fn = Filename.concat dir.Dir.path base in
- STbl.replace !files base fn;
- STbl.replace !files_uncap (String.uncapitalize_ascii base) fn
+ if dir.Dir.hidden then begin
+ STbl.replace !hidden_files base fn;
+ STbl.replace !hidden_files_uncap (String.uncapitalize_ascii base) fn
+ end else begin
+ STbl.replace !visible_files base fn;
+ STbl.replace !visible_files_uncap (String.uncapitalize_ascii base) fn
+ end
) dir.Dir.files
-let init ~auto_include l =
+let init ~auto_include ~visible ~hidden =
reset ();
- dirs := List.rev_map Dir.create l;
- List.iter prepend_add !dirs;
+ visible_dirs := List.rev_map (Dir.create ~hidden:false) visible;
+ hidden_dirs := List.rev_map (Dir.create ~hidden:true) hidden;
+ List.iter prepend_add !hidden_dirs;
+ List.iter prepend_add !visible_dirs;
auto_include_callback := auto_include
let remove_dir dir =
assert (not Config.merlin || Local_store.is_bound ());
- let new_dirs = List.filter (fun d -> Dir.path d <> dir) !dirs in
- if List.compare_lengths new_dirs !dirs <> 0 then begin
+ let visible = List.filter (fun d -> Dir.path d <> dir) !visible_dirs in
+ let hidden = List.filter (fun d -> Dir.path d <> dir) !hidden_dirs in
+ if List.compare_lengths visible !visible_dirs <> 0
+ || List.compare_lengths hidden !hidden_dirs <> 0 then begin
reset ();
- List.iter prepend_add new_dirs;
- dirs := new_dirs
+ visible_dirs := visible;
+ hidden_dirs := hidden;
+ List.iter prepend_add hidden;
+ List.iter prepend_add visible
end
(* General purpose version of function to add a new entry to load path: We only
- add a basename to the cache if it is not already present in the cache, in
- order to enforce left-to-right precedence. *)
-let add dir =
+ add a basename to the cache if it is not already present, in order to enforce
+ left-to-right precedence. *)
+let add (dir : Dir.t) =
assert (not Config.merlin || Local_store.is_bound ());
+ let update base fn visible_files hidden_files =
+ if dir.hidden && not (STbl.mem !hidden_files base) then
+ STbl.replace !hidden_files base fn
+ else if not (STbl.mem !visible_files base) then
+ STbl.replace !visible_files base fn
+ in
List.iter
(fun base ->
let fn = Filename.concat dir.Dir.path base in
- if not (STbl.mem !files base) then
- STbl.replace !files base fn;
+ update base fn visible_files hidden_files;
let ubase = String.uncapitalize_ascii base in
- if not (STbl.mem !files_uncap ubase) then
- STbl.replace !files_uncap ubase fn)
- dir.Dir.files;
- dirs := dir :: !dirs
+ update ubase fn visible_files_uncap hidden_files_uncap)
+ dir.files;
+ if dir.hidden then
+ hidden_dirs := dir :: !hidden_dirs
+ else
+ visible_dirs := dir :: !visible_dirs
let append_dir = add
-let add_dir dir = add (Dir.create dir)
+let add_dir ~hidden dir = add (Dir.create ~hidden dir)
(* Add the directory at the start of load path - so basenames are
unconditionally added. *)
-let prepend_dir dir =
+let prepend_dir (dir : Dir.t) =
assert (not Config.merlin || Local_store.is_bound ());
prepend_add dir;
- dirs := !dirs @ [dir]
+ if dir.hidden then
+ hidden_dirs := !hidden_dirs @ [dir]
+ else
+ visible_dirs := !visible_dirs @ [dir]
let is_basename fn = Filename.basename fn = fn
@@ -150,27 +193,40 @@ let auto_include_otherlibs =
(* Ensure directories are only ever scanned once *)
let expand = Misc.expand_directory Config.standard_library in
let otherlibs =
- let read_lib lib = lazy (Dir.create (expand ("+" ^ lib))) in
+ let read_lib lib = lazy (Dir.create ~hidden:false (expand ("+" ^ lib))) in
List.map (fun lib -> (lib, read_lib lib)) ["dynlink"; "str"; "unix"] in
auto_include_libs otherlibs
+type visibility = Visible | Hidden
+
+let find_file_in_cache fn visible_files hidden_files =
+ try (STbl.find !visible_files fn, Visible) with
+ | Not_found -> (STbl.find !hidden_files fn, Hidden)
+
let find fn =
assert (not Config.merlin || Local_store.is_bound ());
try
if is_basename fn && not !Sys.interactive then
- STbl.find !files fn
+ fst (find_file_in_cache fn visible_files hidden_files)
else
- Misc.find_in_path (get_paths ()) fn
+ Misc.find_in_path (get_path_list ()) fn
with Not_found ->
!auto_include_callback Dir.find fn
-let find_uncap fn =
+let find_uncap_with_visibility fn =
assert (not Config.merlin || Local_store.is_bound ());
try
if is_basename fn && not !Sys.interactive then
- STbl.find !files_uncap (String.uncapitalize_ascii fn)
+ find_file_in_cache (String.uncapitalize_ascii fn)
+ visible_files_uncap hidden_files_uncap
else
- Misc.find_in_path_uncap (get_paths ()) fn
+ try
+ (Misc.find_in_path_uncap (get_visible_path_list ()) fn, Visible)
+ with
+ | Not_found ->
+ (Misc.find_in_path_uncap (get_hidden_path_list ()) fn, Hidden)
with Not_found ->
let fn_uncap = String.uncapitalize_ascii fn in
- !auto_include_callback Dir.find_uncap fn_uncap
+ (!auto_include_callback Dir.find_uncap fn_uncap, Visible)
+
+let find_uncap fn = fst (find_uncap_with_visibility fn)
diff --git a/ocaml/utils/load_path.mli b/ocaml/utils/load_path.mli
index fe3abaf95de..757324f148c 100644
--- a/ocaml/utils/load_path.mli
+++ b/ocaml/utils/load_path.mli
@@ -14,15 +14,15 @@
(** Management of include directories.
- This module offers a high level interface to locating files in the
- load path, which is constructed from [-I] command line flags and a few
+ This module offers a high level interface to locating files in the load
+ path, which is constructed from [-I] and [-H] command line flags and a few
other parameters.
It makes the assumption that the contents of include directories
doesn't change during the execution of the compiler.
*)
-val add_dir : string -> unit
+val add_dir : hidden:bool -> string -> unit
(** Add a directory to the end of the load path (i.e. at lowest priority.) *)
val remove_dir : string -> unit
@@ -35,7 +35,7 @@ module Dir : sig
type t
(** Represent one directory in the load path. *)
- val create : string -> t
+ val create : hidden:bool -> string -> t
val path : t -> string
@@ -43,6 +43,10 @@ module Dir : sig
(** All the files in that directory. This doesn't include files in
sub-directories of this directory. *)
+ val hidden : t -> bool
+ (** If the modules in this directory should not be bound in the initial
+ scope *)
+
val find : t -> string -> string option
(** [find dir fn] returns the full path to [fn] in [dir]. *)
@@ -59,8 +63,13 @@ val no_auto_include : auto_include_callback
(** No automatic directory inclusion: misses in the load path raise [Not_found]
as normal. *)
-val init : auto_include:auto_include_callback -> string list -> unit
-(** [init l] is the same as [reset (); List.iter add_dir (List.rev l)] *)
+val init :
+ auto_include:auto_include_callback -> visible:string list ->
+ hidden:string list -> unit
+(** [init ~visible ~hidden] is the same as
+ [reset ();
+ List.iter add_dir (List.rev hidden);
+ List.iter add_dir (List.rev visible)] *)
val auto_include_otherlibs :
(string -> unit) -> auto_include_callback
@@ -68,9 +77,16 @@ val auto_include_otherlibs :
{!Load_path.init} and automatically adds [-I +lib] to the load path after
calling [alert lib]. *)
-val get_paths : unit -> string list
+val get_path_list : unit -> string list
(** Return the list of directories passed to [add_dir] so far. *)
+type paths =
+ { visible : string list;
+ hidden : string list }
+
+val get_paths : unit -> paths
+(** Return the directories passed to [add_dir] so far. *)
+
val find : string -> string
(** Locate a file in the load path. Raise [Not_found] if the file
cannot be found. This function is optimized for the case where the
@@ -81,6 +97,12 @@ val find_uncap : string -> string
(** Same as [find], but search also for uncapitalized name, i.e. if
name is Foo.ml, allow /path/Foo.ml and /path/foo.ml to match. *)
+type visibility = Visible | Hidden
+
+val find_uncap_with_visibility : string -> string * visibility
+(** Same as [find_uncap], but also reports whether the cmi was found in a -I
+ directory (Visible) or a -H directory (Hidden) *)
+
val[@deprecated] add : Dir.t -> unit
(** Old name for {!append_dir} *)
@@ -92,5 +114,6 @@ val prepend_dir : Dir.t -> unit
(** [prepend_dir d] adds [d] to the start of the load path (i.e. at highest
priority. *)
-val get : unit -> Dir.t list
-(** Same as [get_paths ()], except that it returns a [Dir.t list]. *)
+val get_visible : unit -> Dir.t list
+(** Same as [get_paths ()], except that it returns a [Dir.t list], and doesn't
+ include the -H paths. *)
diff --git a/ocaml/utils/misc.ml b/ocaml/utils/misc.ml
index 68fd23bd1a1..674b9ebd45e 100644
--- a/ocaml/utils/misc.ml
+++ b/ocaml/utils/misc.ml
@@ -70,6 +70,13 @@ let rec map_end f l1 l2 =
[] -> l2
| hd::tl -> f hd :: map_end f tl l2
+let rev_map_end f l1 l2 =
+ let rec rmap_f accu = function
+ | [] -> accu
+ | hd::tl -> rmap_f (f hd :: accu) tl
+ in
+ rmap_f l2 l1
+
let rec map_left_right f = function
[] -> []
| hd::tl -> let res = f hd in res :: map_left_right f tl
diff --git a/ocaml/utils/misc.mli b/ocaml/utils/misc.mli
index 263749f7374..170c080c588 100644
--- a/ocaml/utils/misc.mli
+++ b/ocaml/utils/misc.mli
@@ -77,6 +77,9 @@ val reraise_preserving_backtrace : exn -> (unit -> unit) -> 'a
val map_end: ('a -> 'b) -> 'a list -> 'b list -> 'b list
(** [map_end f l t] is [map f l @ t], just more efficient. *)
+val rev_map_end: ('a -> 'b) -> 'a list -> 'b list -> 'b list
+ (** [rev_map_end f l t] is [map f (rev l) @ t], just more efficient. *)
+
val map_left_right: ('a -> 'b) -> 'a list -> 'b list
(** Like [List.map], with guaranteed left-to-right evaluation order *)
diff --git a/tools/flambda_backend_objinfo.ml b/tools/flambda_backend_objinfo.ml
index e9889d8ae5d..730b24640a2 100644
--- a/tools/flambda_backend_objinfo.ml
+++ b/tools/flambda_backend_objinfo.ml
@@ -113,8 +113,10 @@ let print_cmt_infos cmt =
(match cmt.cmt_sourcefile with None -> "(none)" | Some f -> f);
printf "Compilation flags:";
Array.iter print_spaced_string cmt.cmt_args;
- printf "\nLoad path:";
- List.iter print_spaced_string cmt.cmt_loadpath;
+ printf "\nLoad path:\n Visible:";
+ List.iter print_spaced_string cmt.cmt_loadpath.visible;
+ printf "\n Hidden:";
+ List.iter print_spaced_string cmt.cmt_loadpath.hidden;
printf "\n";
printf "cmt interface digest: %s\n"
(match cmt.cmt_interface_digest with
diff --git a/tools/merge_archives.ml b/tools/merge_archives.ml
index 2863e6da8ec..fb3aa897206 100644
--- a/tools/merge_archives.ml
+++ b/tools/merge_archives.ml
@@ -23,7 +23,7 @@ let merge_cma ~target ~archives =
Clflags.all_ccopts := [];
Clflags.dllibs := [];
List.iter
- (fun archive -> Load_path.add_dir (Filename.dirname archive))
+ (fun archive -> Load_path.add_dir ~hidden:false (Filename.dirname archive))
archives;
let error reporter err =
Format.eprintf "Error whilst merging .cma files:@ %a\n%!" reporter err;