diff options
author | David Allsopp <david.allsopp@metastack.com> | 2022-04-06 15:34:00 +0100 |
---|---|---|
committer | David Allsopp <david.allsopp@metastack.com> | 2022-05-24 14:10:09 +0100 |
commit | 7c8d877ee4b3875e6316be53dbbcf4f04503c1a1 (patch) | |
tree | 9ff8d7bc998eeec55d6abf112603a85d80cda854 | |
parent | 2986a94fa2603db5cca748d1bdbc7edae98c46d7 (diff) | |
download | ocaml-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-- | .depend | 1 | ||||
-rw-r--r-- | compilerlibs/Makefile.compilerlibs | 4 | ||||
-rw-r--r-- | driver/compmisc.ml | 6 | ||||
-rw-r--r-- | driver/compmisc.mli | 6 | ||||
-rw-r--r-- | otherlibs/dynlink/Makefile | 4 | ||||
-rw-r--r-- | parsing/location.ml | 13 | ||||
-rw-r--r-- | parsing/location.mli | 3 | ||||
-rw-r--r-- | tools/Makefile | 12 | ||||
-rw-r--r-- | utils/load_path.ml | 71 | ||||
-rw-r--r-- | utils/load_path.mli | 43 |
10 files changed, 128 insertions, 35 deletions
@@ -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} *) |