summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Allsopp <david.allsopp@metastack.com>2022-04-06 15:34:00 +0100
committerDavid Allsopp <david.allsopp@metastack.com>2022-05-24 14:10:09 +0100
commit7c8d877ee4b3875e6316be53dbbcf4f04503c1a1 (patch)
tree9ff8d7bc998eeec55d6abf112603a85d80cda854
parent2986a94fa2603db5cca748d1bdbc7edae98c46d7 (diff)
downloadocaml-7c8d877ee4b3875e6316be53dbbcf4f04503c1a1.tar.gz
Add a hook for misses in Load_path
Load_path.find{,_uncap} now invoke a hook before raising Not_found, which provides a mechanism for automatically adding directories to the Load_path if required.
-rw-r--r--.depend1
-rw-r--r--compilerlibs/Makefile.compilerlibs4
-rw-r--r--driver/compmisc.ml6
-rw-r--r--driver/compmisc.mli6
-rw-r--r--otherlibs/dynlink/Makefile4
-rw-r--r--parsing/location.ml13
-rw-r--r--parsing/location.mli3
-rw-r--r--tools/Makefile12
-rw-r--r--utils/load_path.ml71
-rw-r--r--utils/load_path.mli43
10 files changed, 128 insertions, 35 deletions
diff --git a/.depend b/.depend
index 79d4c29b02..57789239b9 100644
--- a/.depend
+++ b/.depend
@@ -6019,6 +6019,7 @@ driver/compmisc.cmx : \
utils/clflags.cmx \
driver/compmisc.cmi
driver/compmisc.cmi : \
+ utils/load_path.cmi \
typing/env.cmi \
utils/clflags.cmi
driver/errors.cmo : \
diff --git a/compilerlibs/Makefile.compilerlibs b/compilerlibs/Makefile.compilerlibs
index 4eb49ca60d..7e044d69be 100644
--- a/compilerlibs/Makefile.compilerlibs
+++ b/compilerlibs/Makefile.compilerlibs
@@ -31,10 +31,10 @@ UTILS = \
utils/identifiable.cmo \
utils/numbers.cmo \
utils/arg_helper.cmo \
- utils/clflags.cmo \
- utils/profile.cmo \
utils/local_store.cmo \
utils/load_path.cmo \
+ utils/clflags.cmo \
+ utils/profile.cmo \
utils/terminfo.cmo \
utils/ccomp.cmo \
utils/warnings.cmo \
diff --git a/driver/compmisc.ml b/driver/compmisc.ml
index 9e1edd6f40..f3db8728c6 100644
--- a/driver/compmisc.ml
+++ b/driver/compmisc.ml
@@ -13,6 +13,12 @@
(* *)
(**************************************************************************)
+let auto_include find_in_dir fn =
+ if !Clflags.no_std_include then
+ raise Not_found
+ else
+ Load_path.auto_include_otherlibs Location.auto_include_alert find_in_dir fn
+
(* Initialize the search path.
[dir] (default: the current directory)
is always searched first unless -nocwd is specified,
diff --git a/driver/compmisc.mli b/driver/compmisc.mli
index bb4c292b4f..80394c5eb6 100644
--- a/driver/compmisc.mli
+++ b/driver/compmisc.mli
@@ -21,3 +21,9 @@ val set_from_env : 'a option ref -> 'a Clflags.env_reader -> unit
val read_clflags_from_env : unit -> unit
val with_ppf_dump : file_prefix:string -> (Format.formatter -> 'a) -> 'a
+
+val auto_include :
+ (Load_path.Dir.t -> string -> string option) -> string -> string
+(** [auto_include find_in_dir fn] is a callback function to be passed to
+ {!Load_path.init} and automatically adds [-I +lib] to the load path after
+ displaying an alert. *)
diff --git a/otherlibs/dynlink/Makefile b/otherlibs/dynlink/Makefile
index 21ed5af164..3a41405c36 100644
--- a/otherlibs/dynlink/Makefile
+++ b/otherlibs/dynlink/Makefile
@@ -79,13 +79,13 @@ COMPILERLIBS_SOURCES=\
utils/identifiable.ml \
utils/numbers.ml \
utils/arg_helper.ml \
+ utils/local_store.ml \
+ utils/load_path.ml \
utils/clflags.ml \
utils/profile.ml \
utils/consistbl.ml \
utils/terminfo.ml \
utils/warnings.ml \
- utils/local_store.ml \
- utils/load_path.ml \
utils/int_replace_polymorphic_compare.ml \
utils/lazy_backtrack.ml \
parsing/location.ml \
diff --git a/parsing/location.ml b/parsing/location.ml
index d97d43d9c3..0aba4a85ba 100644
--- a/parsing/location.ml
+++ b/parsing/location.ml
@@ -895,6 +895,19 @@ let alert ?(def = none) ?(use = none) ~kind loc message =
let deprecated ?def ?use loc message =
alert ?def ?use ~kind:"deprecated" loc message
+let auto_include_alert lib =
+ let message = Printf.sprintf "\
+ OCaml's lib directory layout changed in 5.0. The %s subdirectory has been \
+ automatically added to the search path, but you should add -I +%s to the \
+ command-line to silence this alert (e.g. by adding %s to the list of \
+ libraries in your dune file, or adding use_%s to your _tags file for \
+ ocamlbuild, or using -package %s for ocamlfind)." lib lib lib lib lib in
+ let alert =
+ {Warnings.kind="ocaml_deprecated_auto_include"; use=none; def=none;
+ message = Format.asprintf "@[@\n%a@]" Format.pp_print_text message}
+ in
+ prerr_alert none alert
+
(******************************************************************************)
(* Reporting errors on exceptions *)
diff --git a/parsing/location.mli b/parsing/location.mli
index 5ba80b04da..b33bcdf4b5 100644
--- a/parsing/location.mli
+++ b/parsing/location.mli
@@ -243,6 +243,9 @@ val deprecated: ?def:t -> ?use:t -> t -> string -> unit
val alert: ?def:t -> ?use:t -> kind:string -> t -> string -> unit
(** Prints an arbitrary alert. *)
+val auto_include_alert: string -> unit
+(** Prints an alert that -I +lib has been automatically added ot the load
+ path *)
(** {1 Reporting errors} *)
diff --git a/tools/Makefile b/tools/Makefile
index 66ce89f9cd..afbae6fae3 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -84,8 +84,8 @@ ocamldep.opt$(EXE): $(call byte2native, $(ocamldep_objects))
# The profiler
OCAMLPROF=config.cmo build_path_prefix_map.cmo misc.cmo identifiable.cmo \
- numbers.cmo arg_helper.cmo clflags.cmo terminfo.cmo \
- warnings.cmo location.cmo longident.cmo docstrings.cmo \
+ numbers.cmo arg_helper.cmo local_store.cmo load_path.cmo clflags.cmo \
+ terminfo.cmo warnings.cmo location.cmo longident.cmo docstrings.cmo \
syntaxerr.cmo ast_helper.cmo \
camlinternalMenhirLib.cmo parser.cmo \
pprintast.cmo \
@@ -98,8 +98,8 @@ opt.opt: profiling.cmx
OCAMLCP = config.cmo build_path_prefix_map.cmo misc.cmo profile.cmo \
warnings.cmo identifiable.cmo numbers.cmo arg_helper.cmo \
- clflags.cmo local_store.cmo \
- terminfo.cmo location.cmo load_path.cmo ccomp.cmo compenv.cmo \
+ local_store.cmo load_path.cmo clflags.cmo \
+ terminfo.cmo location.cmo ccomp.cmo compenv.cmo \
main_args.cmo ocamlcp_common.cmo
ocamlcp$(EXE): $(OCAMLCP) ocamlcp.cmo
@@ -139,8 +139,8 @@ ocamlmklib.opt$(EXE): $(call byte2native, $(OCAMLMKLIB))
# To make custom toplevels
OCAMLMKTOP=config.cmo build_path_prefix_map.cmo misc.cmo \
- identifiable.cmo numbers.cmo arg_helper.cmo clflags.cmo \
- local_store.cmo load_path.cmo profile.cmo ccomp.cmo ocamlmktop.cmo
+ identifiable.cmo numbers.cmo arg_helper.cmo local_store.cmo \
+ load_path.cmo clflags.cmo profile.cmo ccomp.cmo ocamlmktop.cmo
ocamlmktop$(EXE): $(OCAMLMKTOP)
ocamlmktop.opt$(EXE): $(call byte2native, $(OCAMLMKTOP))
diff --git a/utils/load_path.ml b/utils/load_path.ml
index 2b1d02654b..8873d7b583 100644
--- a/utils/load_path.ml
+++ b/utils/load_path.ml
@@ -31,6 +31,22 @@ module Dir = struct
let path t = t.path
let files t = t.files
+ let find t fn =
+ if List.mem fn t.files then
+ Some (Filename.concat t.path fn)
+ else
+ None
+
+ let find_uncap t fn =
+ let fn = String.uncapitalize_ascii fn in
+ let search base =
+ if String.uncapitalize_ascii base = fn then
+ Some (Filename.concat t.path base)
+ else
+ None
+ in
+ List.find_map search t.files
+
(* For backward compatibility reason, simulate the behavior of
[Misc.find_in_path]: silently ignore directories that don't exist
+ treat [""] as the current directory. *)
@@ -45,12 +61,15 @@ module Dir = struct
end
let dirs = s_ref []
+let default_auto_include_callback _ _ = raise Not_found
+let auto_include_callback = ref default_auto_include_callback
let reset () =
assert (not Config.merlin || Local_store.is_bound ());
STbl.clear !files;
STbl.clear !files_uncap;
- dirs := []
+ dirs := [];
+ auto_include_callback := default_auto_include_callback
let get () = List.rev !dirs
let get_paths () = List.rev_map Dir.path !dirs
@@ -66,10 +85,11 @@ let prepend_add dir =
STbl.replace !files_uncap (String.uncapitalize_ascii base) fn
) dir.Dir.files
-let init l =
+let init ?(auto_include=default_auto_include_callback) l =
reset ();
dirs := List.rev_map Dir.create l;
- List.iter prepend_add !dirs
+ List.iter prepend_add !dirs;
+ auto_include_callback := auto_include
let remove_dir dir =
assert (not Config.merlin || Local_store.is_bound ());
@@ -109,16 +129,45 @@ let prepend_dir dir =
let is_basename fn = Filename.basename fn = fn
+let auto_include_libs libs alert find_in_dir fn =
+ let scan (lib, lazy dir) =
+ let file = find_in_dir dir fn in
+ let alert_and_add_dir _ =
+ alert lib;
+ append_dir dir
+ in
+ Option.iter alert_and_add_dir file;
+ file
+ in
+ match List.find_map scan libs with
+ | Some base -> base
+ | None -> raise Not_found
+
+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
+ List.map (fun lib -> (lib, read_lib lib)) ["dynlink"; "str"; "unix"] in
+ auto_include_libs otherlibs
+
let find fn =
assert (not Config.merlin || Local_store.is_bound ());
- if is_basename fn && not !Sys.interactive then
- STbl.find !files fn
- else
- Misc.find_in_path (get_paths ()) fn
+ try
+ if is_basename fn && not !Sys.interactive then
+ STbl.find !files fn
+ else
+ Misc.find_in_path (get_paths ()) fn
+ with Not_found ->
+ !auto_include_callback Dir.find fn
let find_uncap fn =
assert (not Config.merlin || Local_store.is_bound ());
- if is_basename fn && not !Sys.interactive then
- STbl.find !files_uncap (String.uncapitalize_ascii fn)
- else
- Misc.find_in_path_uncap (get_paths ()) fn
+ try
+ if is_basename fn && not !Sys.interactive then
+ STbl.find !files_uncap (String.uncapitalize_ascii fn)
+ else
+ Misc.find_in_path_uncap (get_paths ()) fn
+ with Not_found ->
+ let fn_uncap = String.uncapitalize_ascii fn in
+ !auto_include_callback Dir.find_uncap fn_uncap
diff --git a/utils/load_path.mli b/utils/load_path.mli
index 1f9aba28bf..570f477701 100644
--- a/utils/load_path.mli
+++ b/utils/load_path.mli
@@ -31,9 +31,37 @@ val remove_dir : string -> unit
val reset : unit -> unit
(** Remove all directories *)
-val init : string list -> unit
+module Dir : sig
+ type t
+ (** Represent one directory in the load path. *)
+
+ val create : string -> t
+
+ val path : t -> string
+
+ val files : t -> string list
+ (** All the files in that directory. This doesn't include files in
+ sub-directories of this directory. *)
+
+ val find : t -> string -> string option
+ (** [find dir fn] returns the full path to [fn] in [dir]. *)
+
+ val find_uncap : t -> string -> string option
+ (** As {!find}, but search also for uncapitalized name, i.e. if name is
+ Foo.ml, either /path/Foo.ml or /path/foo.ml may be returned. *)
+end
+
+val init :
+ ?auto_include:((Dir.t -> string -> string option) -> string -> string) ->
+ string list -> unit
(** [init l] is the same as [reset (); List.iter add_dir (List.rev l)] *)
+val auto_include_otherlibs :
+ (string -> unit) -> (Dir.t -> string -> string option) -> string -> string
+(** [auto_include_otherlibs alert] is a callback function to be passed to
+ {!Load_path.init} and automatically adds [-I +lib] to the load path after
+ calling [alert lib]. *)
+
val get_paths : unit -> string list
(** Return the list of directories passed to [add_dir] so far. *)
@@ -47,19 +75,6 @@ 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. *)
-module Dir : sig
- type t
- (** Represent one directory in the load path. *)
-
- val create : string -> t
-
- val path : t -> string
-
- val files : t -> string list
- (** All the files in that directory. This doesn't include files in
- sub-directories of this directory. *)
-end
-
val[@deprecated] add : Dir.t -> unit
(** Old name for {!append_dir} *)