From cbfa4f0a276a46b95e22a09efce5716985ffffa1 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Mon, 5 Sep 2022 15:05:18 -0600 Subject: [PATCH] feature: enable (include_subdirs qualified) Signed-off-by: Rudi Grinberg ps-id: f0bdd789-87c5-4047-b418-47cdaf7749ae --- CHANGES.md | 7 + bin/describe.ml | 2 +- bin/ocaml_merlin.ml | 3 +- bin/top.ml | 4 +- src/dune_rules/action_unexpanded.ml | 12 +- src/dune_rules/buildable_rules.ml | 7 +- src/dune_rules/copy_line_directive.ml | 78 +- src/dune_rules/copy_line_directive.mli | 11 +- src/dune_rules/dep_rules.ml | 19 +- src/dune_rules/dune_file.ml | 15 +- src/dune_rules/merlin.ml | 146 ++-- src/dune_rules/merlin.mli | 4 +- src/dune_rules/ml_sources.ml | 77 +- src/dune_rules/ml_sources.mli | 2 +- src/dune_rules/module.ml | 116 ++- src/dune_rules/module.mli | 19 +- src/dune_rules/module_compilation.ml | 65 +- src/dune_rules/module_compilation.mli | 2 - src/dune_rules/module_name.ml | 34 +- src/dune_rules/module_name.mli | 28 +- src/dune_rules/module_trie.ml | 151 ++++ src/dune_rules/module_trie.mli | 49 ++ src/dune_rules/modules.ml | 667 ++++++++++++------ src/dune_rules/modules.mli | 35 +- src/dune_rules/modules_field_evaluator.ml | 87 +-- src/dune_rules/modules_field_evaluator.mli | 6 +- src/dune_rules/ocamldep.ml | 85 +-- src/dune_rules/per_item.ml | 4 + src/dune_rules/per_item_intf.ml | 2 + src/dune_rules/simple_rules.ml | 3 +- src/dune_sexp/encoder.ml | 5 + src/dune_sexp/encoder.mli | 2 + test/blackbox-tests/test-cases/describe.t | 6 +- .../test-cases/dune-package.t/run.t | 57 +- test/blackbox-tests/test-cases/gh5267.t | 10 +- .../test-cases/github1549.t/run.t | 16 +- .../test-cases/github1946.t/run.t | 4 +- .../test-cases/github759.t/run.t | 6 +- .../include-qualified/basic.t/dune-project | 2 +- .../include-qualified/basic.t/run.t | 31 +- .../test-cases/include-qualified/exe.t | 19 + .../include-qualified/group-module-overlap.t | 26 + .../invalid-deps/group-interface-sub-module.t | 31 + .../invalid-deps/group-interface.t | 31 + .../invalid-deps/toplevel-lib-interface.t | 24 + .../nested-lib-interface.t/dune-project | 2 +- .../nested-lib-interface.t/exe/dune | 3 + .../nested-lib-interface.t/run.t | 40 +- .../nested-virtual.t/dune-project | 2 +- .../include-qualified/nested-virtual.t/run.t | 14 +- .../include-qualified/ocamldep-regression.t | 69 ++ .../include-qualified/pp.t/dune-project | 2 +- .../test-cases/include-qualified/pp.t/run.t | 12 +- .../include-qualified/private-modules.t | 22 + .../test-cases/melange/merlin.t | 7 +- .../test-cases/merlin/dump-dot-merlin.t/run.t | 2 +- .../merlin/merlin-from-subdir.t/dune | 2 +- .../test-cases/merlin/merlin-tests.t/run.t | 22 +- .../test-cases/merlin/per-module-pp.t/run.t | 10 +- .../test-cases/merlin/server.t/dune | 2 + .../merlin/server.t/foobar/lib3.foobar.ml | 0 .../test-cases/merlin/server.t/lib3.ml | 1 - .../test-cases/merlin/server.t/run.t | 14 +- .../test-cases/merlin/src-dirs-of-deps.t | 9 +- .../merlin/unit-names-merlin-gh1233.t/run.t | 9 +- .../re-exported-deps/transitive.t/run.t | 9 +- .../dune-package-info.t/run.t | 54 +- .../impl-private-modules.t/run.t | 4 +- .../vlib-default-impl/dune-package.t/run.t | 50 +- 69 files changed, 1704 insertions(+), 667 deletions(-) create mode 100644 src/dune_rules/module_trie.ml create mode 100644 src/dune_rules/module_trie.mli create mode 100644 test/blackbox-tests/test-cases/include-qualified/exe.t create mode 100644 test/blackbox-tests/test-cases/include-qualified/group-module-overlap.t create mode 100644 test/blackbox-tests/test-cases/include-qualified/invalid-deps/group-interface-sub-module.t create mode 100644 test/blackbox-tests/test-cases/include-qualified/invalid-deps/group-interface.t create mode 100644 test/blackbox-tests/test-cases/include-qualified/invalid-deps/toplevel-lib-interface.t create mode 100644 test/blackbox-tests/test-cases/include-qualified/nested-lib-interface.t/exe/dune create mode 100644 test/blackbox-tests/test-cases/include-qualified/ocamldep-regression.t create mode 100644 test/blackbox-tests/test-cases/include-qualified/private-modules.t create mode 100644 test/blackbox-tests/test-cases/merlin/server.t/foobar/lib3.foobar.ml delete mode 100644 test/blackbox-tests/test-cases/merlin/server.t/lib3.ml diff --git a/CHANGES.md b/CHANGES.md index c318f670028..3bac396f704 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -43,6 +43,13 @@ Unreleased - Fix missing dependencies when detecting the kind of C compiler we're using (#6610, fixes #6415, @emillon) +- Allow `(include_subdirs qualified)` for OCaml projects. (#6594, fixes #1084, + @rgrinberg) + +- Accurately determine merlin configuration for all sources selected with + `copy#` and `copy_files#`. The old heuristic of looking for a module in + parent directories is removed (#6594, @rgrinberg) + 3.6.0 (2022-11-14) ------------------ diff --git a/bin/describe.ml b/bin/describe.ml index 96326f2301e..d4722d3bc7c 100644 --- a/bin/describe.ml +++ b/bin/describe.ml @@ -262,7 +262,7 @@ module Crawl = struct Memo.return @@ match Module.kind unit with - | Alias -> + | Alias _ -> (* TODO: handle Alias modules, that are generated by dune. They are currently associated to no ocamldep-related rules. *) Action_builder.return no_deps diff --git a/bin/ocaml_merlin.ml b/bin/ocaml_merlin.ml index 8e424e93ca9..7296ddeac53 100644 --- a/bin/ocaml_merlin.ml +++ b/bin/ocaml_merlin.ml @@ -78,14 +78,13 @@ end = struct let load_merlin_file file = (* We search for an appropriate merlin configuration in the current directory and its parents *) - let filename = String.lowercase_ascii (Path.Build.basename file) in let rec find_closest path = match get_merlin_files_paths path |> List.find_map ~f:(fun file_path -> match Merlin.Processed.load_file file_path with | Error msg -> Some (Merlin_conf.make_error msg) - | Ok config -> Merlin.Processed.get config ~filename) + | Ok config -> Merlin.Processed.get config ~file) with | Some p -> Some p | None -> ( diff --git a/bin/top.ml b/bin/top.ml index 7b114342701..d6b29fdbeb8 100644 --- a/bin/top.ml +++ b/bin/top.ml @@ -190,9 +190,7 @@ module Module = struct let+ (pp, ppx), files_to_load = Memo.fork_and_join pps files_to_load in let code = let modules = Dune_rules.Compilation_context.modules cctx in - let opens_ = - Dune_rules.Module_compilation.open_modules modules module_ - in + let opens_ = Dune_rules.Modules.local_open modules module_ in List.map opens_ ~f:(fun name -> sprintf "open %s" (Dune_rules.Module_name.to_string name)) in diff --git a/src/dune_rules/action_unexpanded.ml b/src/dune_rules/action_unexpanded.ml index af9172f45d1..4da7cde7a0e 100644 --- a/src/dune_rules/action_unexpanded.ml +++ b/src/dune_rules/action_unexpanded.ml @@ -370,11 +370,12 @@ end = struct end end -let rec expand (t : Dune_lang.Action.t) : Action.t Action_expander.t = +let rec expand (t : Dune_lang.Action.t) ~context : Action.t Action_expander.t = let module A = Action_expander in let module E = Action_expander.E in let open Action_expander.O in let module O (* [O] for "outcome" *) = Action in + let expand = expand ~context in let expand_run prog args = let+ args = A.all (List.map args ~f:E.strings) and+ prog, more_args = E.prog_and_args prog in @@ -434,7 +435,7 @@ let rec expand (t : Dune_lang.Action.t) : Action.t Action_expander.t = | Copy_and_add_line_directive (x, y) -> let+ x = E.dep x and+ y = E.target y in - Copy_line_directive.action x y + Copy_line_directive.action context ~src:x ~dst:y | System x -> let+ x = E.string x in O.System x @@ -485,8 +486,9 @@ let expand_no_targets t ~loc ~deps:deps_written_by_user ~expander ~what = Expander.set_expanding_what expander (User_action_without_targets { what }) in let* { Action_builder.With_targets.build; targets } = + let context = Expander.context expander in Action_builder.of_memo - (Action_expander.run (expand t) ~targets_dir:None ~expander) + (Action_expander.run (expand ~context t) ~targets_dir:None ~expander) in if not (Targets.is_empty targets) then User_error.raise ~loc @@ -529,7 +531,9 @@ let expand t ~loc ~deps:deps_written_by_user ~targets_dir Expander.set_expanding_what expander (User_action targets_written_by_user) in let+! { Action_builder.With_targets.build; targets } = - Action_expander.run (expand t) ~targets_dir:(Some targets_dir) ~expander + let context = Expander.context expander in + Action_expander.run (expand ~context t) ~targets_dir:(Some targets_dir) + ~expander in let targets = match (targets_written_by_user : _ Targets_spec.t) with diff --git a/src/dune_rules/buildable_rules.ml b/src/dune_rules/buildable_rules.ml index ab3d39ddf55..f8561de581e 100644 --- a/src/dune_rules/buildable_rules.ml +++ b/src/dune_rules/buildable_rules.ml @@ -1,20 +1,21 @@ open Import open Memo.O -let gen_select_rules t ~dir compile_info = +let gen_select_rules sctx ~dir compile_info = let open Memo.O in Lib.Compile.resolved_selects compile_info |> Resolve.Memo.read_memo >>= Memo.parallel_iter ~f:(fun { Lib.Compile.Resolved_select.dst_fn; src_fn } -> let dst = Path.Build.relative dir dst_fn in - Super_context.add_rule t ~dir + Super_context.add_rule sctx ~dir (Action_builder.with_file_targets ~file_targets:[ dst ] (let open Action_builder.O in let* src_fn = Resolve.read src_fn in let src = Path.build (Path.Build.relative dir src_fn) in let+ () = Action_builder.path src in - Action.Full.make (Copy_line_directive.action src dst)))) + let context = Super_context.context sctx in + Action.Full.make (Copy_line_directive.action context ~src ~dst)))) let with_lib_deps (t : Context.t) compile_info ~dir ~f = let prefix = diff --git a/src/dune_rules/copy_line_directive.ml b/src/dune_rules/copy_line_directive.ml index e5a83324faa..c4269869e23 100644 --- a/src/dune_rules/copy_line_directive.ml +++ b/src/dune_rules/copy_line_directive.ml @@ -1,5 +1,54 @@ open Import +module DB = struct + (* Needed to tell resolve the configuration of sources merlin gives us. + + This is all ugly and doesn't work well for watch mode, but it's better + than the old hack. It's temporary until we have something RPC based. + *) + module Persistent = Dune_util.Persistent.Make (struct + type nonrec t = Path.Build.t Path.Build.Table.t + + let name = "COPY-LINE-DIRECTIVE-MAP" + + let version = 1 + + let to_dyn = Path.Build.Table.to_dyn Path.Build.to_dyn + end) + + let needs_dumping = ref false + + let file = Path.relative Path.build_dir ".copy-db" + + let t = + (* This mutable table is safe: it's only observed by [$ dune ocaml merlin] *) + lazy + (match Persistent.load file with + | None -> Path.Build.Table.create 128 + | Some t -> t) + + let dump () = + if !needs_dumping && Path.build_dir_exists () then ( + needs_dumping := false; + Persistent.dump file (Lazy.force t)) + + let () = at_exit dump + + let rec follow_while path ~f = + let t = Lazy.force t in + match Path.Build.Table.find t path with + | None -> None + | Some p -> ( + match f p with + | None -> follow_while p ~f + | Some p -> Some p) + + let set ~src ~dst = + let t = Lazy.force t in + needs_dumping := true; + Path.Build.Table.set t src dst +end + let line_directive ~filename:fn ~line_number = let directive = if Foreign_language.has_foreign_extension ~fn then "line" else "" @@ -7,24 +56,33 @@ let line_directive ~filename:fn ~line_number = sprintf "#%s %d %S\n" directive line_number fn module Spec = struct - type ('path, 'target) t = 'path * 'target + type merlin = + | Yes + | No + + let bool_of_merlin = function + | Yes -> true + | No -> false + + type ('path, 'target) t = 'path * 'target * merlin let name = "copy-line-directive" let version = 1 - let bimap (src, dst) f g = (f src, g dst) + let bimap (src, dst, merlin) f g = (f src, g dst, merlin) let is_useful_to ~distribute:_ ~memoize = memoize - let encode (src, dst) path target : Dune_lang.t = + let encode (src, dst, merlin) path target : Dune_lang.t = List [ Dune_lang.atom_or_quoted_string "copy-line-directive" ; path src ; target dst + ; Dune_lang.atom_or_quoted_string (Bool.to_string (bool_of_merlin merlin)) ] - let action (src, dst) ~ectx:_ ~eenv:_ = + let action (src, dst, merlin) ~ectx:_ ~eenv:_ = Io.with_file_in src ~f:(fun ic -> Path.build dst |> Io.with_file_out ~f:(fun oc -> @@ -32,10 +90,14 @@ module Spec = struct output_string oc (line_directive ~filename:(Path.to_string fn) ~line_number:1); Io.copy_channels ic oc)); + (match merlin with + | No -> () + | Yes -> + Path.as_in_build_dir src |> Option.iter ~f:(fun src -> DB.set ~src ~dst)); Fiber.return () end -let action src dst = +let action (context : Context.t) ~src ~dst = let module M = struct type path = Path.t @@ -43,12 +105,12 @@ let action src dst = module Spec = Spec - let v = (src, dst) + let v = (src, dst, if context.merlin then Spec.Yes else No) end in Action.Extension (module M) -let builder ~src ~dst = +let builder context ~src ~dst = let open Action_builder.O in Action_builder.with_file_targets ~file_targets:[ dst ] (Action_builder.path src - >>> Action_builder.return (Action.Full.make (action src dst))) + >>> Action_builder.return (Action.Full.make (action context ~src ~dst))) diff --git a/src/dune_rules/copy_line_directive.mli b/src/dune_rules/copy_line_directive.mli index 2bd883fa742..8363ebbcd81 100644 --- a/src/dune_rules/copy_line_directive.mli +++ b/src/dune_rules/copy_line_directive.mli @@ -1,6 +1,13 @@ open Import -val action : Path.t -> Path.Build.t -> Action.t +module DB : sig + val follow_while : Path.Build.t -> f:(Path.Build.t -> 'a option) -> 'a option +end + +val action : Context.t -> src:Path.t -> dst:Path.Build.t -> Action.t val builder : - src:Path.t -> dst:Path.Build.t -> Action.Full.t Action_builder.With_targets.t + Context.t + -> src:Path.t + -> dst:Path.Build.t + -> Action.Full.t Action_builder.With_targets.t diff --git a/src/dune_rules/dep_rules.ml b/src/dune_rules/dep_rules.ml index 1f65cb05e7e..3b9859402d0 100644 --- a/src/dune_rules/dep_rules.ml +++ b/src/dune_rules/dep_rules.ml @@ -43,17 +43,23 @@ let ooi_deps { vimpl; sctx; dir; obj_dir; modules = _; stdlib = _; sandbox = _ } in read -let deps_of_module md ~ml_kind m = +let deps_of_module ({ modules; _ } as md) ~ml_kind m = match Module.kind m with | Wrapped_compat -> - let modules = md.modules in let interface_module = match Modules.lib_interface modules with | Some m -> m | None -> Modules.compat_for_exn modules m in - Action_builder.return (List.singleton interface_module) |> Memo.return - | _ -> Ocamldep.deps_of md ~ml_kind m + List.singleton interface_module |> Action_builder.return |> Memo.return + | _ -> ( + let+ deps = Ocamldep.deps_of md ~ml_kind m in + match Modules.alias_for modules m with + | [] -> deps + | aliases -> + let open Action_builder.O in + let+ deps = deps in + aliases @ deps) let deps_of_vlib_module ({ obj_dir; vimpl; dir; sctx; _ } as md) ~ml_kind m = let vimpl = Option.value_exn vimpl in @@ -82,8 +88,11 @@ let deps_of_vlib_module ({ obj_dir; vimpl; dir; sctx; _ } as md) ~ml_kind m = let rec deps_of md ~ml_kind (m : Modules.Sourced_module.t) = let is_alias = match m with - | Imported_from_vlib m | Normal m -> Module.kind m = Alias | Impl_of_virtual_module _ -> false + | Imported_from_vlib m | Normal m -> ( + match Module.kind m with + | Alias _ -> true + | _ -> false) in if is_alias then Memo.return (Action_builder.return []) else diff --git a/src/dune_rules/dune_file.ml b/src/dune_rules/dune_file.ml index 93b422d9d3f..5e793d592fb 100644 --- a/src/dune_rules/dune_file.ml +++ b/src/dune_rules/dune_file.ml @@ -2143,11 +2143,16 @@ module Include_subdirs = struct | Include of qualification let decode ~enable_qualified = - let opts_list = - [ ("no", No); ("unqualified", Include Unqualified) ] - @ if enable_qualified then [ ("qualified", Include Qualified) ] else [] - in - enum opts_list + sum + [ ("no", return No) + ; ("unqualified", return (Include Unqualified)) + ; ( "qualified" + , let+ () = + if enable_qualified then return () + else Syntax.since Stanza.syntax (3, 7) + in + Include Qualified ) + ] end module Library_redirect = struct diff --git a/src/dune_rules/merlin.ml b/src/dune_rules/merlin.ml index d7c45918c92..6d9f86fbfdf 100644 --- a/src/dune_rules/merlin.ml +++ b/src/dune_rules/merlin.ml @@ -1,5 +1,10 @@ open Import +let remove_extension file = + let dir = Path.Build.parent_exn file in + let basename, _ext = String.lsplit2_exn (Path.Build.basename file) ~on:'.' in + Path.Build.relative dir basename + module Processed = struct (* The actual content of the merlin file as built by the [Unprocessed.process] function from the unprocessed info gathered through [gen_rules]. The first @@ -12,6 +17,12 @@ module Processed = struct | Pp | Ppx + let to_dyn = + let open Dyn in + function + | Pp -> variant "Pp" [] + | Ppx -> variant "Ppx" [] + let to_flag = function | Pp -> "-pp" | Ppx -> "-ppx" @@ -22,6 +33,10 @@ module Processed = struct ; args : string } + let dyn_of_pp_flag { flag; args } = + let open Dyn in + record [ ("flag", Pp_kind.to_dyn flag); ("args", string args) ] + let pp_kind x = x.flag let pp_args x = x.args @@ -36,19 +51,53 @@ module Processed = struct ; melc_flags : string list } + let dyn_of_config + { stdlib_dir; obj_dirs; src_dirs; flags; extensions; melc_flags } = + let open Dyn in + record + [ ("stdlib_dir", option Path.to_dyn stdlib_dir) + ; ("obj_dirs", Path.Set.to_dyn obj_dirs) + ; ("src_dirs", Path.Set.to_dyn src_dirs) + ; ("flags", list string flags) + ; ("extensions", list (Ml_kind.Dict.to_dyn string) extensions) + ; ("melc_flags", list string melc_flags) + ] + + type module_config = + { opens : Module_name.t list + ; module_ : Module.t + } + + let dyn_of_module_config { opens; module_ } = + let open Dyn in + record + [ ("opens", list Module_name.to_dyn opens) + ; ("module_", Module.to_dyn module_) + ] + (* ...but modules can have different preprocessing specifications*) type t = { config : config - ; modules : Module_name.t list + ; per_module_config : module_config Path.Build.Map.t ; pp_config : pp_flag option Module_name.Per_item.t } + let to_dyn { config; per_module_config; pp_config } = + let open Dyn in + record + [ ("config", dyn_of_config config) + ; ( "per_module_config" + , Path.Build.Map.to_dyn dyn_of_module_config per_module_config ) + ; ( "pp_config" + , Module_name.Per_item.to_dyn (option dyn_of_pp_flag) pp_config ) + ] + module D = struct type nonrec t = t let name = "merlin-conf" - let version = 3 + let version = 4 let to_dyn _ = Dyn.String "Use [dune ocaml dump-dot-merlin] instead" end @@ -69,7 +118,7 @@ module Processed = struct let serialize_path = Path.to_absolute_filename - let to_sexp ~pp + let to_sexp ~opens ~pp { stdlib_dir; obj_dirs; src_dirs; flags; extensions; melc_flags } = let make_directive tag value = Sexp.List [ Atom tag; value ] in let make_directive_of_path tag path = @@ -104,11 +153,21 @@ module Processed = struct (Sexp.List [ Atom (Pp_kind.to_flag flag); Atom args ]) :: flags in - match melc_flags with + let flags = + match melc_flags with + | [] -> flags + | melc_flags -> + make_directive "FLG" + (Sexp.List (List.map ~f:(fun s -> Sexp.Atom s) melc_flags)) + :: flags + in + match opens with | [] -> flags - | melc_flags -> + | opens -> make_directive "FLG" - (Sexp.List (List.map ~f:(fun s -> Sexp.Atom s) melc_flags)) + (Sexp.List + (List.concat_map opens ~f:(fun name -> + [ Sexp.Atom "-open"; Atom (Module_name.to_string name) ]))) :: flags in let suffixes = @@ -157,36 +216,41 @@ module Processed = struct print "\n"); Buffer.contents b - let get { modules; pp_config; config } ~filename = + let get { per_module_config; pp_config; config } ~file = (* We only match the first part of the filename : foo.ml -> foo foo.cppo.ml -> foo *) - let fname = - String.lsplit2 filename ~on:'.' - |> Option.map ~f:fst - |> Option.value ~default:filename - |> String.lowercase + let open Option.O in + let+ { module_; opens } = + let find file = + let file_without_ext = remove_extension file in + Path.Build.Map.find per_module_config file_without_ext + in + match find file with + | Some _ as s -> s + | None -> Copy_line_directive.DB.follow_while file ~f:find in - List.find_opt modules ~f:(fun name -> - let fname' = Module_name.to_string name |> String.lowercase in - String.equal fname fname') - |> Option.map ~f:(fun name -> - let pp = Module_name.Per_item.get pp_config name in - to_sexp ~pp config) + let pp = Module_name.Per_item.get pp_config (Module.name module_) in + to_sexp ~opens ~pp config let print_file path = match load_file path with | Error msg -> Printf.eprintf "%s\n" msg - | Ok { modules; pp_config; config } -> - let pp_one module_ = - let pp = Module_name.Per_item.get pp_config module_ in - let sexp = to_sexp ~pp config in + | Ok { per_module_config; pp_config; config } -> + let pp_one { module_; opens } = let open Pp.O in - Pp.vbox (Pp.text (Module_name.to_string module_)) + let name = Module.name module_ in + let pp = Module_name.Per_item.get pp_config name in + let sexp = to_sexp ~opens ~pp config in + Pp.vbox (Pp.text (Module_name.to_string name)) ++ Pp.newline ++ Pp.vbox (Sexp.pp sexp) - ++ Pp.newline in - Format.printf "%a%!" Pp.to_fmt (Pp.concat_map modules ~f:pp_one) + let pp = + Path.Build.Map.values per_module_config + |> Pp.concat_map ~sep:Pp.cut ~f:pp_one + |> Pp.vbox + in + Format.printf "%a@." Pp.to_fmt pp let print_generic_dot_merlin paths = match Result.List.map paths ~f:load_file with @@ -205,7 +269,7 @@ module Processed = struct , init.config.melc_flags ) ~f:(fun (acc_pp, acc_obj, acc_src, acc_flags, acc_ext, acc_melc_flags) - { modules = _ + { per_module_config = _ ; pp_config ; config = { stdlib_dir = _ @@ -289,16 +353,7 @@ module Unprocessed = struct Path.Set.singleton @@ obj_dir_of_lib `Private mode (Obj_dir.of_local obj_dir) in - let flags = - Ocaml_flags.common - @@ - match Modules.alias_module modules with - | None -> flags - | Some m -> - Ocaml_flags.prepend_common - [ "-open"; Module_name.to_string (Module.name m) ] - flags - in + let flags = Ocaml_flags.common flags in let extensions = Dialect.DB.extensions_for_merlin dialects in let config = { stdlib_dir @@ -465,12 +520,23 @@ module Unprocessed = struct ; melc_flags } and+ pp_config = pp_config t sctx ~expander in - let modules = + let per_module_config = (* And copy for each module the resulting pp flags *) - Modules.fold_no_vlib modules ~init:[] ~f:(fun m acc -> - Module.name m :: acc) + Modules.fold_no_vlib modules ~init:[] ~f:(fun m init -> + Module.sources m + |> Path.Build.Set.of_list_map ~f:(fun src -> + Path.as_in_build_dir_exn src |> remove_extension) + |> Path.Build.Set.fold ~init ~f:(fun src acc -> + let config = + { Processed.module_ = Module.set_pp m None + ; opens = + Modules.alias_for modules m |> List.map ~f:Module.name + } + in + (src, config) :: acc)) + |> Path.Build.Map.of_list_exn in - { Processed.modules; pp_config; config } + { Processed.pp_config; config; per_module_config } end let dot_merlin sctx ~dir ~more_src_dirs ~expander (t : Unprocessed.t) = diff --git a/src/dune_rules/merlin.mli b/src/dune_rules/merlin.mli index 16c1857580f..0633f909317 100644 --- a/src/dune_rules/merlin.mli +++ b/src/dune_rules/merlin.mli @@ -15,6 +15,8 @@ module Processed : sig (** Type of "processed" merlin information *) type t + val to_dyn : t -> Dyn.t + module Pp_kind : sig type t = | Pp @@ -37,7 +39,7 @@ module Processed : sig print the resulting configuration in dot-merlin syntax. *) val print_generic_dot_merlin : Path.t list -> unit - val get : t -> filename:string -> Sexp.t option + val get : t -> file:Path.Build.t -> Sexp.t option end val make : diff --git a/src/dune_rules/ml_sources.ml b/src/dune_rules/ml_sources.ml index 237d2c51783..592fc80fb84 100644 --- a/src/dune_rules/ml_sources.ml +++ b/src/dune_rules/ml_sources.ml @@ -21,14 +21,14 @@ module Modules = struct ; executables : (Modules.t * Path.Build.t Obj_dir.t) String.Map.t ; melange_emits : (Modules.t * Path.Build.t Obj_dir.t) String.Map.t ; (* Map from modules to the origin they are part of *) - rev_map : Origin.t Module_name.Map.t + rev_map : Origin.t Module_name.Path.Map.t } let empty = { libraries = Lib_name.Map.empty ; executables = String.Map.empty ; melange_emits = String.Map.empty - ; rev_map = Module_name.Map.empty + ; rev_map = Module_name.Path.Map.empty } type groups = @@ -79,29 +79,30 @@ module Modules = struct in let rev_map = let modules = - let by_name (origin : Origin.t) = + let by_path (origin : Origin.t) = Modules.fold_user_available ~init:[] ~f:(fun m acc -> - (Module.name m, origin) :: acc) + (Module.path m, origin) :: acc) in List.concat - [ List.concat_map libs ~f:(fun (l, m, _) -> by_name (Library l) m) - ; List.concat_map exes ~f:(fun (e, m, _) -> by_name (Executables e) m) - ; List.concat_map emits ~f:(fun (l, m, _) -> by_name (Melange l) m) + [ List.concat_map libs ~f:(fun (l, m, _) -> by_path (Library l) m) + ; List.concat_map exes ~f:(fun (e, m, _) -> by_path (Executables e) m) + ; List.concat_map emits ~f:(fun (l, m, _) -> by_path (Melange l) m) ] in - match Module_name.Map.of_list modules with + match Module_name.Path.Map.of_list modules with | Ok x -> x - | Error (name, _, _) -> - let open Module_name.Infix in + | Error (path, _, _) -> let locs = List.filter_map modules ~f:(fun (n, origin) -> - Option.some_if (n = name) (Origin.loc origin)) + Option.some_if + (Ordering.is_eq (Module_name.Path.compare n path)) + (Origin.loc origin)) |> List.sort ~compare:Loc.compare in User_error.raise ~loc:(Loc.drop_position (List.hd locs)) [ Pp.textf "Module %S is used in several stanzas:" - (Module_name.to_string name) + (Module_name.Path.to_string path) ; Pp.enumerate locs ~f:(fun loc -> Pp.verbatim (Loc.to_file_colon_line loc)) ; Pp.text @@ -215,7 +216,9 @@ let modules_and_obj_dir t ~for_ = let modules t ~for_ = modules_and_obj_dir t ~for_ |> fst -let find_origin (t : t) name = Module_name.Map.find t.modules.rev_map name +let find_origin (t : t) name = + (* TODO generalize to any path *) + Module_name.Path.Map.find t.modules.rev_map [ name ] let virtual_modules ~lookup_vlib vlib = let info = Lib.info vlib in @@ -361,7 +364,9 @@ let modules_of_stanzas dune_file ~dir ~scope ~lookup_vlib ~modules = let project = Scope.project scope in if Dune_project.wrapped_executables project then Modules_group.make_wrapped ~src_dir:dir ~modules `Exe - else Modules_group.exe_unwrapped modules + else + let modules = Module_trie.to_map modules in + Modules_group.exe_unwrapped modules in let obj_dir = Dune_file.Executables.obj_dir ~dir exes in let modules = @@ -398,30 +403,34 @@ let modules_of_stanzas dune_file ~dir ~scope ~lookup_vlib ~modules = | _ -> Memo.return `Skip) >>| filter_partition_map -let check_no_qualified (loc, include_subdirs) = - if include_subdirs = Dune_file.Include_subdirs.Include Qualified then - User_error.raise ~loc - [ Pp.text "(include_subdirs qualified) is not supported yet" ] - -let make dune_file ~dir ~scope ~lib_config ~loc ~lookup_vlib ~include_subdirs +let make dune_file ~dir ~scope ~lib_config ~loc ~lookup_vlib + ~include_subdirs:(_loc, (include_subdirs : Dune_file.Include_subdirs.t)) ~dirs = let+ modules_of_stanzas = - check_no_qualified include_subdirs; let modules = let dialects = Dune_project.dialects (Scope.project scope) in - List.fold_left dirs ~init:Module_name.Map.empty - ~f:(fun acc ((dir : Path.Build.t), _local, files) -> - let modules = modules_of_files ~dialects ~dir ~files in - Module_name.Map.union acc modules ~f:(fun name x y -> - User_error.raise ~loc - [ Pp.textf "Module %S appears in several directories:" - (Module_name.to_string name) - ; Pp.textf "- %s" - (Path.to_string_maybe_quoted (Module.Source.src_dir x)) - ; Pp.textf "- %s" - (Path.to_string_maybe_quoted (Module.Source.src_dir y)) - ; Pp.text "This is not allowed, please rename one of them." - ])) + match include_subdirs with + | Include Qualified -> + List.fold_left dirs ~init:Module_trie.empty + ~f:(fun acc ((dir : Path.Build.t), local, files) -> + let modules = modules_of_files ~dialects ~dir ~files in + let path = List.map local ~f:Module_name.of_string in + Module_trie.set_map acc path modules) + | No | Include Unqualified -> + List.fold_left dirs ~init:Module_name.Map.empty + ~f:(fun acc ((dir : Path.Build.t), _local, files) -> + let modules = modules_of_files ~dialects ~dir ~files in + Module_name.Map.union acc modules ~f:(fun name x y -> + User_error.raise ~loc + [ Pp.textf "Module %S appears in several directories:" + (Module_name.to_string name) + ; Pp.textf "- %s" + (Path.to_string_maybe_quoted (Module.Source.src_dir x)) + ; Pp.textf "- %s" + (Path.to_string_maybe_quoted (Module.Source.src_dir y)) + ; Pp.text "This is not allowed, please rename one of them." + ])) + |> Module_trie.of_map in modules_of_stanzas dune_file ~dir ~scope ~lookup_vlib ~modules in diff --git a/src/dune_rules/ml_sources.mli b/src/dune_rules/ml_sources.mli index 865882754ad..d7b649dcf02 100644 --- a/src/dune_rules/ml_sources.mli +++ b/src/dune_rules/ml_sources.mli @@ -60,5 +60,5 @@ val make : -> loc:Loc.t -> lookup_vlib:(loc:Loc.t -> dir:Path.Build.t -> t Memo.t) -> include_subdirs:Loc.t * Dune_file.Include_subdirs.t - -> dirs:(Path.Build.t * 'a list * String.Set.t) list + -> dirs:(Path.Build.t * string list * String.Set.t) list -> t Memo.t diff --git a/src/dune_rules/module.ml b/src/dune_rules/module.ml index 4e05627ca3e..88947b28f32 100644 --- a/src/dune_rules/module.ml +++ b/src/dune_rules/module.ml @@ -18,7 +18,10 @@ module File = struct let to_dyn { path; dialect } = let open Dyn in - record [ ("path", Path.to_dyn path); ("dialect", Dialect.to_dyn dialect) ] + record + [ ("path", Path.to_dyn path) + ; ("dialect", Dyn.string @@ Dialect.name dialect) + ] end module Kind = struct @@ -26,35 +29,58 @@ module Kind = struct | Intf_only | Virtual | Impl - | Alias + | Alias of Module_name.Path.t | Impl_vmodule | Wrapped_compat | Root - let all = - [ (Intf_only, "intf_only") - ; (Virtual, "virtual") - ; (Impl, "impl") - ; (Alias, "alias") - ; (Impl_vmodule, "impl_vmodule") - ; (Wrapped_compat, "wrapped_compat") - ; (Root, "root") - ] - - let rev_all = List.rev_map ~f:(fun (x, y) -> (y, x)) all - - let to_string s = Option.value_exn (List.assoc all s) - - let to_dyn t = Dyn.string (to_string t) - - let encode t = Dune_lang.Encoder.string (to_string t) + let to_dyn = + let open Dyn in + function + | Intf_only -> variant "Intf_only" [] + | Virtual -> variant "Virtual" [] + | Impl -> variant "Impl" [] + | Alias path -> variant "Alias" [ Module_name.Path.to_dyn path ] + | Impl_vmodule -> variant "Impl_vmodule" [] + | Wrapped_compat -> variant "Wrapped_compat" [] + | Root -> variant "Root" [] + + let encode = + let open Dune_lang.Encoder in + function + | Intf_only -> string "intf_only" + | Virtual -> string "virtual" + | Impl -> string "impl" + | Alias path -> ( + match path with + | [] -> string "alias" + | _ :: _ -> + constr "alias" (fun x -> List (Module_name.Path.encode x)) path) + | Impl_vmodule -> string "impl_vmodule" + | Wrapped_compat -> string "wrapped_compat" + | Root -> string "root" let decode = let open Dune_lang.Decoder in - enum rev_all + sum + [ ("intf_only", return Intf_only) + ; ("virtual", return Virtual) + ; ("impl", return Impl) + ; ("impl_vmodule", return Impl_vmodule) + ; ("wrapped_compat", return Wrapped_compat) + ; ("root", return Root) + ; ( "alias" + , let* next = peek in + (* TODO remove this once everyone recompiles *) + match next with + | None -> return (Alias []) + | Some _ -> + let+ path = Module_name.Path.decode in + Alias path ) + ] let has_impl = function - | Alias | Impl_vmodule | Wrapped_compat | Root | Impl -> true + | Alias _ | Impl_vmodule | Wrapped_compat | Root | Impl -> true | Intf_only | Virtual -> false end @@ -116,17 +142,20 @@ type t = ; pp : (string list Action_builder.t * Sandbox_config.t) option ; visibility : Visibility.t ; kind : Kind.t + ; path : Module_name.Path.t } let name t = t.source.name +let path t = t.path + let kind t = t.kind let pp_flags t = t.pp -let of_source ?obj_name ~visibility ~(kind : Kind.t) (source : Source.t) = +let of_source ~path ~obj_name ~visibility ~(kind : Kind.t) (source : Source.t) = (match (kind, visibility) with - | (Alias | Impl_vmodule | Virtual | Wrapped_compat), Visibility.Public + | (Alias _ | Impl_vmodule | Virtual | Wrapped_compat), Visibility.Public | Root, Private | (Impl | Intf_only), _ -> () | _, _ -> @@ -136,8 +165,8 @@ let of_source ?obj_name ~visibility ~(kind : Kind.t) (source : Source.t) = ; ("visibility", Visibility.to_dyn visibility) ]); (match (kind, source.files.impl, source.files.intf) with - | (Alias | Impl_vmodule | Impl | Wrapped_compat), None, _ - | (Alias | Impl_vmodule | Wrapped_compat), Some _, Some _ + | (Alias _ | Impl_vmodule | Impl | Wrapped_compat), None, _ + | (Alias _ | Impl_vmodule | Wrapped_compat), Some _, Some _ | (Intf_only | Virtual), Some _, _ | (Intf_only | Virtual), _, None -> let open Dyn in @@ -158,7 +187,8 @@ let of_source ?obj_name ~visibility ~(kind : Kind.t) (source : Source.t) = Module_name.Unique.of_path_assuming_needs_no_mangling_allow_invalid file.path in - { source; obj_name; pp = None; visibility; kind } + let path = Option.value ~default:[ source.name ] path in + { source; obj_name; pp = None; visibility; kind; path } let has t ~ml_kind = match (ml_kind : Ml_kind.t) with @@ -175,8 +205,9 @@ let iter t ~f = Memo.parallel_iter Ml_kind.all ~f:(fun kind -> Memo.Option.iter (Ml_kind.Dict.get t.source.files kind) ~f:(f kind)) -let with_wrapper t ~main_module_name = - { t with obj_name = Module_name.wrap t.source.name ~with_:main_module_name } +let set_obj_name t obj_name = { t with obj_name } + +let set_path t path = { t with path } let add_file t kind file = let source = Source.add_file t.source kind file in @@ -196,13 +227,14 @@ let src_dir t = Source.src_dir t.source let set_pp t pp = { t with pp } -let to_dyn { source; obj_name; pp; visibility; kind } = +let to_dyn { source; obj_name; pp; visibility; kind; path } = Dyn.record [ ("source", Source.to_dyn source) ; ("obj_name", Module_name.Unique.to_dyn obj_name) ; ("pp", Dyn.(option string) (Option.map ~f:(fun _ -> "has pp") pp)) ; ("visibility", Visibility.to_dyn visibility) ; ("kind", Kind.to_dyn kind) + ; ("path", Module_name.Path.to_dyn path) ] let ml_gen = ".ml-gen" @@ -248,20 +280,26 @@ end module Obj_map_traversals = Memo.Make_map_traversals (Obj_map) let encode - ({ source = { name; files = _ }; obj_name; pp = _; visibility; kind } as t) - = + ({ path; source = { name; files = _ }; obj_name; pp = _; visibility; kind } + as t) = let open Dune_lang.Encoder in let has_impl = has t ~ml_kind:Impl in let kind = match kind with | Kind.Impl when has_impl -> None | Intf_only when not has_impl -> None - | Root | Wrapped_compat | Impl_vmodule | Alias | Impl | Virtual | Intf_only - -> Some kind + | Root + | Wrapped_compat + | Impl_vmodule + | Alias _ + | Impl + | Virtual + | Intf_only -> Some kind in record_fields [ field "name" Module_name.encode name ; field "obj_name" Module_name.Unique.encode obj_name + ; field_l "path" (fun x -> x) (Module_name.Path.encode path) ; field "visibility" Visibility.encode visibility ; field_o "kind" Kind.encode kind ; field_b "impl" has_impl @@ -277,6 +315,7 @@ let decode ~src_dir = fields (let+ name = field "name" Module_name.decode and+ obj_name = field "obj_name" Module_name.Unique.decode + and+ path = field ~default:[] "path" Module_name.Path.decode and+ visibility = field "visibility" Visibility.decode and+ kind = field_o "kind" Kind.decode and+ impl = field_b "impl" @@ -296,7 +335,8 @@ let decode ~src_dir = let intf = file intf Intf in let impl = file impl Impl in let source = Source.make ?impl ?intf name in - of_source ~obj_name ~visibility ~kind source) + of_source ~path:(Some path) ~obj_name:(Some obj_name) ~visibility ~kind + source) let pped = map_files ~f:(fun _kind (file : File.t) -> @@ -315,7 +355,7 @@ let ml_source = let set_src_dir t ~src_dir = map_files t ~f:(fun _ -> File.set_src_dir ~src_dir) -let generated ?obj_name ~(kind : Kind.t) ~src_dir name = +let generated ?obj_name ?path ~(kind : Kind.t) ~src_dir name = let obj_name = match obj_name with | Some obj_name -> obj_name @@ -324,7 +364,6 @@ let generated ?obj_name ~(kind : Kind.t) ~src_dir name = let source = let impl = let basename = String.uncapitalize (Module_name.to_string name) in - (* XXX should we use the obj_name here? *) Path.Build.relative src_dir (basename ^ ml_gen) |> Path.build |> File.make Dialect.ocaml in @@ -335,9 +374,10 @@ let generated ?obj_name ~(kind : Kind.t) ~src_dir name = | Root -> Private | _ -> Public in - of_source ~visibility ~kind ~obj_name source + of_source ~path ~visibility ~kind ~obj_name:(Some obj_name) source -let of_source ~visibility ~kind source = of_source ~visibility ~kind source +let of_source ?path ~visibility ~kind source = + of_source ~obj_name:None ~path ~visibility ~kind source module Name_map = struct type nonrec t = t Module_name.Map.t diff --git a/src/dune_rules/module.mli b/src/dune_rules/module.mli index 55632ff8ca5..a6a8e6c0263 100644 --- a/src/dune_rules/module.mli +++ b/src/dune_rules/module.mli @@ -17,7 +17,7 @@ module Kind : sig | Intf_only | Virtual | Impl - | Alias + | Alias of Module_name.Path.t | Impl_vmodule | Wrapped_compat | Root @@ -35,6 +35,8 @@ module Source : sig val has : t -> ml_kind:Ml_kind.t -> bool + val to_dyn : t -> Dyn.t + val src_dir : t -> Path.t end @@ -47,10 +49,17 @@ val to_dyn : t -> Dyn.t (** When you initially construct a [t] using [of_source], it assumes no wrapping (so reports an incorrect [obj_name] if wrapping is used) and you might need to fix it later with [with_wrapper]. *) -val of_source : visibility:Visibility.t -> kind:Kind.t -> Source.t -> t +val of_source : + ?path:Module_name.Path.t + -> visibility:Visibility.t + -> kind:Kind.t + -> Source.t + -> t val name : t -> Module_name.t +val path : t -> Module_name.Path.t + val source : t -> ml_kind:Ml_kind.t -> File.t option val pp_flags : t -> (string list Action_builder.t * Sandbox_config.t) option @@ -63,8 +72,9 @@ val iter : t -> f:(Ml_kind.t -> File.t -> unit Memo.t) -> unit Memo.t val has : t -> ml_kind:Ml_kind.t -> bool -(** Prefix the object name with the library name. *) -val with_wrapper : t -> main_module_name:Module_name.t -> t +val set_obj_name : t -> Module_name.Unique.t -> t + +val set_path : t -> Module_name.Path.t -> t val add_file : t -> Ml_kind.t -> File.t -> t @@ -132,6 +142,7 @@ val set_src_dir : t -> src_dir:Path.t -> t be used to create the rule to generate this file *) val generated : ?obj_name:Module_name.Unique.t + -> ?path:Module_name.Path.t -> kind:Kind.t -> src_dir:Path.Build.t -> Module_name.t diff --git a/src/dune_rules/module_compilation.ml b/src/dune_rules/module_compilation.ml index 731e0702edf..796b79c311a 100644 --- a/src/dune_rules/module_compilation.ml +++ b/src/dune_rules/module_compilation.ml @@ -14,13 +14,8 @@ let force_read_cmi source_file = [ "-intf-suffix"; Path.extension source_file ] the mli is not present it is added as additional target to the .cmo generation *) -let open_modules modules m = - match Modules.alias_for modules m with - | None -> [] - | Some (m : Module.t) -> [ Module.name m ] - let opens modules m = - match open_modules modules m with + match Modules.local_open modules m with | [] -> Command.Args.empty | modules -> Command.Args.S @@ -392,30 +387,32 @@ module Alias_module = struct (Module_name.to_string shadowed)); Buffer.contents b - let of_modules project modules ~alias_module = + let of_modules project modules ~alias_module ~group = let main_module = Modules.main_module_name modules |> Option.value_exn in let aliases = - Modules.for_alias modules - |> Module_name.Map.to_list_map ~f:(fun local_name m -> - let obj_name = Module.obj_name m in - { local_name; obj_name }) + Module_name.Map.to_list_map group ~f:(fun local_name m -> + let obj_name = Module.obj_name m in + { local_name; obj_name }) in let shadowed = if Dune_project.dune_version project < (3, 5) then [] else match Modules.lib_interface modules with | None -> [] - | Some m -> - if Module.kind m = Alias then [] else [ Module.name alias_module ] + | Some m -> ( + match Module.kind m with + | Alias _ -> [] + | _ -> [ Module.name alias_module ]) in { main_module; aliases; shadowed } end -let build_alias_module cctx alias_module = +let build_alias_module cctx alias_module group = let modules = Compilation_context.modules cctx in let alias_file () = let project = Compilation_context.scope cctx |> Scope.project in - Alias_module.of_modules project modules ~alias_module |> Alias_module.to_ml + Alias_module.of_modules project modules ~alias_module ~group + |> Alias_module.to_ml in let cctx = Compilation_context.for_alias_module cctx alias_module in let sctx = Compilation_context.super_context cctx in @@ -459,23 +456,27 @@ let build_all cctx = let for_wrapped_compat = lazy (Compilation_context.for_wrapped_compat cctx) in let modules = Compilation_context.modules cctx in Memo.parallel_iter - (Modules.fold_no_vlib modules ~init:[] ~f:(fun x acc -> x :: acc)) - ~f:(fun m -> - match Module.kind m with - | Root -> build_root_module cctx m - | Alias -> build_alias_module cctx m - | Wrapped_compat -> - let cctx = Lazy.force for_wrapped_compat in - build_module cctx m - | _ -> - let cctx = - if Modules.is_stdlib_alias modules m then - (* XXX it would probably be simpler if the flags were just for this - module in the definition of the stanza *) - Compilation_context.for_alias_module cctx m - else cctx - in - build_module cctx m) + (Modules.fold_no_vlib_with_aliases modules ~init:[] + ~normal:(fun x acc -> `Normal x :: acc) + ~alias:(fun m group acc -> `Alias (m, group) :: acc)) + ~f:(function + | `Alias (m, group) -> build_alias_module cctx m group + | `Normal m -> ( + match Module.kind m with + | Alias _ -> assert false + | Root -> build_root_module cctx m + | Wrapped_compat -> + let cctx = Lazy.force for_wrapped_compat in + build_module cctx m + | _ -> + let cctx = + if Modules.is_stdlib_alias modules m then + (* XXX it would probably be simpler if the flags were just for this + module in the definition of the stanza *) + Compilation_context.for_alias_module cctx m + else cctx + in + build_module cctx m)) let with_empty_intf ~sctx ~dir module_ = let name = diff --git a/src/dune_rules/module_compilation.mli b/src/dune_rules/module_compilation.mli index 9f592d28b81..5f412b52989 100644 --- a/src/dune_rules/module_compilation.mli +++ b/src/dune_rules/module_compilation.mli @@ -10,8 +10,6 @@ val build_module : -> Module.t -> unit Memo.t -val open_modules : Modules.t -> Module.t -> Module_name.t list - val ocamlc_i : deps:Module.t list Action_builder.t Ml_kind.Dict.t -> Compilation_context.t diff --git a/src/dune_rules/module_name.ml b/src/dune_rules/module_name.ml index 2cf8deb799d..97e29fdfade 100644 --- a/src/dune_rules/module_name.ml +++ b/src/dune_rules/module_name.ml @@ -94,6 +94,8 @@ module Unique = struct let of_string s = of_name_assuming_needs_no_mangling (of_string s) + let to_string s = s + let decode = let open Dune_lang.Decoder in let+ s = Dune_lang.Decoder.string in @@ -117,5 +119,33 @@ module Unique = struct module Set = Set end -let wrap t ~with_ = - sprintf "%s__%s" (Unique.of_name_assuming_needs_no_mangling with_) t +module Path = struct + module T = struct + type nonrec t = t list + + let to_dyn = Dyn.list to_dyn + + let compare = List.compare ~compare + + let to_string t = List.map ~f:to_string t |> String.concat ~sep:"." + end + + include T + + let uncapitalize s = to_string s |> String.uncapitalize + + module C = Comparable.Make (T) + module Set = C.Set + module Map = C.Map + + let wrap path = + Unique.of_name_assuming_needs_no_mangling @@ String.concat ~sep:"__" path + + let append_double_underscore t = t @ [ "" ] + + let encode (t : t) = List.map t ~f:encode + + let decode = Dune_lang.Decoder.(repeat decode) +end + +let wrap t ~with_ = Path.wrap (t :: with_) diff --git a/src/dune_rules/module_name.mli b/src/dune_rules/module_name.mli index 1217f7aa23e..8b78aa8c6e2 100644 --- a/src/dune_rules/module_name.mli +++ b/src/dune_rules/module_name.mli @@ -55,6 +55,8 @@ module Unique : sig val to_name : t -> loc:Loc.t -> name + val to_string : t -> string + val compare : t -> t -> Ordering.t val equal : t -> t -> bool @@ -66,7 +68,31 @@ module Unique : sig include Comparable_intf.S with type key := t end -val wrap : t -> with_:t -> Unique.t +module Path : sig + type nonrec t = t list + + val compare : t -> t -> Ordering.t + + val to_dyn : t -> Dyn.t + + val to_string : t -> string + + val uncapitalize : t -> string + + module Map : Stdune.Map.S with type key = t + + module Set : Stdune.Set.S with type elt = t and type 'a map = 'a Map.t + + val wrap : t -> Unique.t + + val encode : t -> Dune_lang.t list + + val decode : t Dune_lang.Decoder.t + + val append_double_underscore : t -> t +end + +val wrap : t -> with_:Path.t -> Unique.t include Comparable_intf.S with type key := t diff --git a/src/dune_rules/module_trie.ml b/src/dune_rules/module_trie.ml new file mode 100644 index 00000000000..ab8ccca86a2 --- /dev/null +++ b/src/dune_rules/module_trie.ml @@ -0,0 +1,151 @@ +open! Import +module Map = Module_name.Map + +type key = Module_name.Path.t + +type 'a t = 'a node Map.t + +and 'a node = + | Leaf of 'a + | Map of 'a t + +let empty = Map.empty + +let mapi = + let rec loop t f acc = + Map.mapi t ~f:(fun name node -> + let path = name :: acc in + match node with + | Leaf a -> Leaf (f (List.rev path) a) + | Map m -> Map (loop m f path)) + in + fun t ~f -> loop t f [] + +let map t ~f = mapi t ~f:(fun _key m -> f m) + +let of_map t : _ t = Map.map t ~f:(fun v -> Leaf v) + +let rec find t = function + | [] -> None + | p :: ps -> ( + match Map.find t p with + | None -> None + | Some (Leaf a) -> Option.some_if (List.is_empty ps) a + | Some (Map t) -> find t ps) + +let rec gen_set t ps v = + match ps with + | [] -> ( + match v with + | Leaf _ -> Code_error.raise "gen_set: no top level leaf" [] + | Map m -> m) + | p :: ps -> + Map.update t p ~f:(fun x -> + if List.is_empty ps then Some v + else + match x with + | None -> None + | Some (Leaf _ as leaf) -> Some leaf + | Some (Map m) -> Some (Map (gen_set m ps v))) + +let set t k v = gen_set t k (Leaf v) + +let set_map t k v = gen_set t k (Map (of_map v)) + +let non_empty_map m = if Map.is_empty m then None else Some (Map m) + +let rec filter_map t ~f = + Map.filter_map t ~f:(function + | Map m -> non_empty_map (filter_map m ~f) + | Leaf a -> ( + match f a with + | None -> None + | Some a -> Some (Leaf a))) + +let rec remove t = function + | [] -> t + | p :: ps -> + Map.update t p ~f:(fun x -> + if List.is_empty ps then None + else + match x with + | None -> None + | Some (Leaf _ as leaf) -> Some leaf + | Some (Map m) -> non_empty_map (remove m ps)) + +let mem t p = Option.is_some (find t p) + +let foldi t ~init ~f = + let rec loop acc path t = + Map.foldi ~init:acc t ~f:(fun k v acc -> + match v with + | Leaf s -> f (List.rev (k :: path)) s acc + | Map t -> loop acc (k :: path) t) + in + loop init [] t + +let fold t ~init ~f = foldi t ~init ~f:(fun _key -> f) + +let rec to_dyn f t = + Map.to_dyn + (function + | Leaf a -> f a + | Map a -> to_dyn f a) + t + +let merge x y ~f = + let base _path _ = (* TODO *) assert false in + let rec loop path x y = + match (x, y) with + | None, None -> assert false + | Some x, None -> base path x + | None, Some x -> base path x + | Some x, Some y -> + Map.merge x y ~f:(fun name x y -> + let path = name :: path in + let rev_path = List.rev path in + let leaf l r = + match f rev_path l r with + | None -> None + | Some x -> Some (Leaf x) + in + match (x, y) with + | None, None -> assert false + (* leaves *) + | None, Some (Leaf y) -> leaf None (Some y) + | Some (Leaf x), None -> leaf (Some x) None + | Some (Leaf x), Some (Leaf y) -> leaf (Some x) (Some y) + (* maps *) + | None, Some (Map v) -> non_empty_map (base path v) + | Some (Map v), None -> non_empty_map (base path v) + | Some (Map x), Some (Map y) -> + non_empty_map (loop path (Some x) (Some y)) + (* mixed *) + | Some (Leaf _), Some (Map y) -> + non_empty_map (loop path None (Some y)) + | Some (Map x), Some (Leaf _) -> + non_empty_map (loop path (Some x) None)) + in + loop [] (Some x) (Some y) + +let singleton path v = set empty path v + +let as_singleton t = + match + fold t ~init:None ~f:(fun v acc -> + match acc with + | None -> Some v + | Some _ -> raise_notrace Exit) + with + | None | (exception Exit) -> None + | Some v -> Some v + +let to_map t = + Module_name.Map.map t ~f:(function + | Leaf v -> v + | Map _ -> assert false) + +let toplevel_only (t : _ t) = + Module_name.Map.filter_map t ~f:(function + | Leaf v -> Some v + | Map _ -> None) diff --git a/src/dune_rules/module_trie.mli b/src/dune_rules/module_trie.mli new file mode 100644 index 00000000000..5995aba54c2 --- /dev/null +++ b/src/dune_rules/module_trie.mli @@ -0,0 +1,49 @@ +(** This module defines a prefix tree that stores a map of module names at every + non-leaf node. Most notably, it's used to implement `(include_subdirs + qualified)` where the directory name qualifies the namespace for its + descendant modules in the file system. *) + +type 'a t = 'a node Module_name.Map.t + +and 'a node = + | Leaf of 'a + | Map of 'a t + +type key = Module_name.Path.t + +val empty : 'a t + +val map : 'a t -> f:('a -> 'b) -> 'b t + +val mapi : 'a t -> f:(key -> 'a -> 'b) -> 'b t + +val of_map : 'a Module_name.Map.t -> 'a t + +val find : 'a t -> key -> 'a option + +val set : 'a t -> key -> 'a -> 'a t + +val set_map : 'a t -> key -> 'a Module_name.Map.t -> 'a t + +val remove : 'a t -> key -> 'a t + +val mem : 'a t -> key -> bool + +val fold : 'a t -> init:'acc -> f:('a -> 'acc -> 'acc) -> 'acc + +val foldi : 'a t -> init:'acc -> f:(key -> 'a -> 'acc -> 'acc) -> 'acc + +val to_dyn : ('a -> Dyn.t) -> 'a t -> Dyn.t + +val to_map : 'a t -> 'a Module_name.Map.t + +val singleton : key -> 'a -> 'a t + +val merge : + 'a t -> 'b t -> f:(key -> 'a option -> 'b option -> 'c option) -> 'c t + +val as_singleton : 'a t -> 'a option + +val filter_map : 'a t -> f:('a -> 'b option) -> 'b t + +val toplevel_only : 'a t -> 'a Module_name.Map.t diff --git a/src/dune_rules/modules.ml b/src/dune_rules/modules.ml index 1d73d4e9d73..74a50fe8f8a 100644 --- a/src/dune_rules/modules.ml +++ b/src/dune_rules/modules.ml @@ -8,7 +8,7 @@ module Common = struct let main_module_name = field "main_module_name" Module_name.encode let modules ?(name = "modules") modules = - field_l name (fun x -> x) (Module.Name_map.encode modules) + field_l name Fun.id (Module.Name_map.encode modules) end module Decode = struct @@ -100,7 +100,10 @@ module Stdlib = struct if Module.name m = main_module_name || special_compiler_module stdlib m then m - else Module.with_wrapper m ~main_module_name) + else + let path = [ main_module_name; Module.name m ] in + let m = Module.set_path m path in + Module.set_obj_name m (Module_name.Path.wrap path)) in let unwrapped = stdlib.modules_before_stdlib in let exit_module = stdlib.exit_module in @@ -154,7 +157,7 @@ module Mangle = struct let of_lib ~lib_name ~implements ~main_module_name ~modules = let kind : Lib.kind = if implements then Implementation lib_name - else if Module_name.Map.mem modules main_module_name then + else if Module_trie.mem modules [ main_module_name ] then Has_lib_interface else Neither in @@ -167,7 +170,7 @@ module Mangle = struct | Has_lib_interface | Neither -> Visibility.Map.make_both main_module_name | Implementation lib -> { private_ = - sprintf "%s__%s__" + sprintf "%s__%s" (Module_name.to_string main_module_name) (Lib_name.Local.to_string lib) |> Module_name.of_string @@ -178,31 +181,45 @@ module Mangle = struct | Melange -> sprintf "melange" |> Module_name.of_string |> Visibility.Map.make_both - let make_alias_module t ~src_dir = - let prefix = prefix t in - let name = + let make_alias_module (t : t) ~has_lib_interface ~src_dir ~interface path = + let kind : Module.Kind.t = Alias path in + let prefix, has_lib_interface = + let prefix = prefix t in match t with - | Lib { kind = Has_lib_interface; _ } -> - Module_name.add_suffix prefix.public "__" - | Lib { kind = Implementation _; _ } -> prefix.private_ - | _ -> prefix.public + | Lib { kind = Implementation _; _ } -> (prefix.private_, true) + | _ -> (prefix.public, has_lib_interface) in - Module.generated ~kind:Alias ~src_dir name - - let wrap_modules t modules = - let prefix = prefix t in - let f = + let obj_name = + Module_name.Path.wrap + @@ prefix + :: + (if has_lib_interface then + Module_name.Path.append_double_underscore path + else path) + in + let name = Module_name.Unique.to_name ~loc:Loc.none obj_name in + let path = if has_lib_interface then [ name ] else interface :: path in + Module.generated ~path ~obj_name ~kind ~src_dir name + + let wrap_module t m ~interface = + let is_lib_interface = + match interface with + | None -> false + | Some interface -> Module_name.equal interface (Module.name m) + in + let path_for_mangle = + let path = Module.path m in + let prefix = prefix t in match t with - | Exe | Melange -> Module.with_wrapper ~main_module_name:prefix.public - | Lib { main_module_name; kind = _ } -> - fun m -> - if Module.name m = main_module_name then m - else - let visibility = Module.visibility m in - let prefix = Visibility.Map.find prefix visibility in - Module.with_wrapper m ~main_module_name:prefix + | Exe | Melange -> prefix.public :: path + | Lib _ -> + let path = + if is_lib_interface then List.rev path |> List.tl |> List.rev + else path + in + Visibility.Map.find prefix (Module.visibility m) :: path in - Module_name.Map.map modules ~f + Module.set_obj_name m (Module_name.Path.wrap path_for_mangle) end let impl_only_of_map m = @@ -210,170 +227,356 @@ let impl_only_of_map m = if Module.has m ~ml_kind:Impl then m :: acc else acc) module Wrapped = struct + module Group = struct + type t = + { alias : Module.t + ; modules : node Module_name.Map.t + ; name : Module_name.t + } + + and node = + | Group of t + | Module of Module.t + + let of_trie (trie : Module.t Module_trie.t) ~mangle ~src_dir : t = + let rec loop interface rev_path trie = + let has_lib_interface = + match Module_name.Map.find trie interface with + | None | Some (Module_trie.Map _) -> false + | Some (Leaf _) -> true + in + { alias = + Mangle.make_alias_module mangle ~has_lib_interface ~src_dir + ~interface (List.rev rev_path) + ; name = interface + ; modules = + Module_name.Map.mapi trie ~f:(fun name (m : 'a Module_trie.node) -> + let rev_path = name :: rev_path in + match m with + | Map m -> Group (loop name rev_path m) + | Leaf m -> + let m = Module.set_path m (List.rev rev_path) in + Module + (Mangle.wrap_module mangle m ~interface:(Some interface))) + } + in + let prefix = (Mangle.prefix mangle).public in + loop prefix [] trie + + let rec relocate_alias_module t ~src_dir = + { t with + alias = Module.set_src_dir t.alias ~src_dir + ; modules = + Module_name.Map.map t.modules ~f:(function + | Module m -> Module m + | Group g -> Group (relocate_alias_module g ~src_dir)) + } + + let rec fold { alias; modules; name = _ } ~f ~init = + let init = f alias init in + Module_name.Map.fold modules ~init ~f:(fun node init -> + match node with + | Module m -> f m init + | Group t -> fold t ~f ~init) + + let rec exists { alias; modules; name = _ } ~f = + f alias + || Module_name.Map.exists modules ~f:(function + | Module m -> f m + | Group p -> exists p ~f) + + let rec to_dyn { alias; modules; name } = + let open Dyn in + record + [ ("alias", Module.to_dyn alias) + ; ("name", Module_name.to_dyn name) + ; ( "modules" + , Module_name.Map.to_dyn + (function + | Module m -> variant "module" [ Module.to_dyn m ] + | Group g -> variant "group" [ to_dyn g ]) + modules ) + ] + + let rec map ({ alias; modules; name = _ } as t) ~f = + let alias = f alias in + let modules = + Module_name.Map.map modules ~f:(function + | Module m -> Module (f m) + | Group g -> Group (map g ~f)) + in + { t with alias; modules } + + let lib_interface t = + match Module_name.Map.find t.modules t.name with + | None | Some (Group _) -> t.alias + | Some (Module m) -> m + + let decode ~src_dir = + let open Dune_lang.Decoder in + let rec t = + lazy + (fields + @@ let+ alias = field "alias" (Module.decode ~src_dir) + and+ modules = + field ~default:[] "modules" (repeat (Lazy.force node)) + and+ name = field "name" Module_name.decode in + { alias; modules = Module_name.Map.of_list_exn modules; name }) + and node = + lazy + (sum + [ ( "module" + , let+ m = Module.decode ~src_dir in + (Module.name m, Module m) ) + ; ( "group" + , let* p = Module_name.decode in + let+ m = Lazy.force t in + (p, Group m) ) + ]) + in + Lazy.force t + + let rec encode { alias; modules; name } = + let open Dune_lang.Encoder in + record_fields + [ field_l "alias" sexp (Module.encode alias) + ; field "name" Module_name.encode name + ; field_l "modules" Fun.id + (Module_name.Map.to_list_map modules ~f:(fun _ t -> + Dune_lang.List + (match t with + | Group g -> Dune_lang.atom "group" :: encode g + | Module m -> Dune_lang.atom "module" :: Module.encode m))) + ] + + let parents t m = + let rec loop acc t = function + | [] -> acc + | p :: ps -> ( + match Module_name.Map.find t.modules p with + | None -> + (* TODO this happens with "side" modules like menhir mock modules *) + acc + | Some (Module _) -> acc + | Some (Group g) -> loop (g :: acc) g ps) + in + loop [ t ] t (Module.path m) + + module Memo_traversals = struct + let rec parallel_map ({ alias; modules; name = _ } as t) ~f = + let open Memo.O in + let+ alias, modules = + Memo.fork_and_join + (fun () -> f alias) + (fun () -> + Module_name.Map_traversals.parallel_map modules ~f:(fun _ n -> + match n with + | Module m -> + let+ m = f m in + Module m + | Group g -> + let+ g = parallel_map g ~f in + Group g)) + in + { t with alias; modules } + end + end + type t = - { modules : Module.Name_map.t + { group : Group.t ; wrapped_compat : Module.Name_map.t - ; alias_module : Module.t - ; main_module_name : Module_name.t ; wrapped : Mode.t + ; toplevel_module : [ `Exported | `Hidden ] } - let encode - { modules; wrapped_compat; alias_module; main_module_name; wrapped } = + let to_dyn { group; wrapped_compat; wrapped; toplevel_module = _ } = + let open Dyn in + record + [ ("group", Group.to_dyn group) + ; ("wrapped_compat", Module_name.Map.to_dyn Module.to_dyn wrapped_compat) + ; ("wrapped", Mode.to_dyn wrapped) + ] + + let lib_interface t = Group.lib_interface t.group + + let fold_user_available { group; toplevel_module; _ } ~init ~f = + let init = + match toplevel_module with + | `Hidden -> init + | `Exported -> f group.alias init + in + Module_name.Map.fold group.modules ~init ~f:(fun node init -> + match node with + | Module m -> f m init + | Group t -> Group.fold t ~f ~init) + + let for_alias (t : t) alias = + let path = + match Module.kind alias with + | Alias for_ -> for_ + | _ -> Code_error.raise "for_alias: not an alias module" [] + in + let rec loop (t : Group.t) = function + | [] -> t + | name :: path -> ( + match Module_name.Map.find t.modules name with + | Some (Group g) -> loop g path + | Some (Module m) -> + Code_error.raise "for_alias: unexpected module" + [ ("m", Module.to_dyn m); ("alias", Module.to_dyn alias) ] + | None -> + Code_error.raise "for_alias: not found" + [ ("alias", Module.to_dyn alias) ]) + in + let group = loop t.group path in + Module_name.Map.remove group.modules group.name + |> Module_name.Map.map ~f:(fun (g : Group.node) -> + match g with + | Module m -> m + | Group g -> Group.lib_interface g) + + let encode { group; wrapped_compat; wrapped; toplevel_module = _ } = let open Dune_lang.Encoder in let module E = Common.Encode in record_fields - [ E.main_module_name main_module_name - ; E.modules modules - ; field_l "alias_module" sexp (Module.encode alias_module) - ; field "wrapped" Wrapped.encode wrapped + [ field_l "group" Fun.id (Group.encode group) ; E.modules ~name:"wrapped_compat" wrapped_compat + ; field "wrapped" Wrapped.encode wrapped ] - let decode ~src_dir = + (* TODO remove this eventually *) + let old_decode ~src_dir = let open Dune_lang.Decoder in let open Common.Decode in fields (let+ main_module_name = main_module_name and+ modules = modules ~src_dir () and+ wrapped_compat = modules ~name:"wrapped_compat" ~src_dir () - and+ alias_module = field "alias_module" (Module.decode ~src_dir) + and+ alias = field "alias_module" (Module.decode ~src_dir) + and+ wrapped = field "wrapped" Mode.decode in + let group = + { Group.alias + ; name = main_module_name + ; modules = modules |> Module_name.Map.map ~f:(fun m -> Group.Module m) + } + in + { group; wrapped_compat; wrapped; toplevel_module = `Exported }) + + let new_decode ~src_dir = + let open Dune_lang.Decoder in + let open Common.Decode in + fields + (let+ group = field "group" (Group.decode ~src_dir) + and+ wrapped_compat = modules ~name:"wrapped_compat" ~src_dir () and+ wrapped = field "wrapped" Mode.decode in - { main_module_name; modules; wrapped_compat; alias_module; wrapped }) - - let map - ({ modules - ; wrapped_compat - ; alias_module - ; main_module_name = _ - ; wrapped = _ - } as t) ~f = + { group; wrapped_compat; wrapped; toplevel_module = `Exported }) + + let decode ~src_dir = + let open Dune_lang.Decoder in + new_decode ~src_dir <|> old_decode ~src_dir + + let map ({ group; wrapped_compat; toplevel_module = _; wrapped = _ } as t) ~f + = { t with - modules = Module_name.Map.map modules ~f + group = Group.map group ~f ; wrapped_compat = Module_name.Map.map wrapped_compat ~f - ; alias_module = f alias_module } let make ~src_dir ~lib_name ~implements ~modules ~main_module_name ~wrapped = let mangle = Mangle.of_lib ~main_module_name ~lib_name ~implements ~modules in - let modules, wrapped_compat = - let wrapped_modules = Mangle.wrap_modules mangle modules in + let wrapped_compat = match (wrapped : Mode.t) with | Simple false -> assert false - | Simple true -> (wrapped_modules, Module_name.Map.empty) + | Simple true -> Module_name.Map.empty | Yes_with_transition _ -> - ( wrapped_modules - , Module_name.Map.remove modules main_module_name - |> Module_name.Map.filter_map ~f:(fun m -> - match Module.visibility m with - | Public -> Some (Module.wrapped_compat m) - | Private -> None) ) + let toplevel = Module_trie.toplevel_only modules in + Module_name.Map.remove toplevel main_module_name + |> Module_name.Map.filter_map ~f:(fun m -> + match Module.visibility m with + | Private -> None + | Public -> Some (Module.wrapped_compat m)) in - let alias_module = Mangle.make_alias_module ~src_dir mangle in - { modules; alias_module; wrapped_compat; main_module_name; wrapped } + let group = Group.of_trie modules ~mangle ~src_dir in + { group; wrapped_compat; wrapped; toplevel_module = `Exported } let make_exe_or_melange ~src_dir ~modules mangle = - let alias_module = Mangle.make_alias_module mangle ~src_dir in - let modules = Mangle.wrap_modules mangle modules in - { modules + let group = Group.of_trie modules ~mangle ~src_dir in + { group ; wrapped_compat = Module_name.Map.empty - ; alias_module - (* XXX exe's don't have a main module, but this is harmless *) - ; main_module_name = Module.name alias_module ; wrapped = Simple true + ; toplevel_module = `Hidden } - let obj_map - { modules - ; wrapped_compat - ; alias_module - ; main_module_name = _ - ; wrapped = _ - } ~f = - let init = Module.Obj_map.singleton alias_module (f alias_module) in - let acc = - Module_name.Map.fold ~f:(fun m acc -> Module.Obj_map.add_exn acc m (f m)) - in - acc modules ~init:(acc wrapped_compat ~init) + let obj_map { group; wrapped_compat; wrapped = _; toplevel_module = _ } ~f = + let add_module m acc = Module.Obj_map.add_exn acc m (f m) in + let init = Group.fold group ~init:Module.Obj_map.empty ~f:add_module in + Module_name.Map.fold ~init wrapped_compat ~f:add_module - let to_dyn - { modules; wrapped_compat; alias_module; main_module_name; wrapped } = - let open Dyn in - record - [ ("modules", Module.Name_map.to_dyn modules) - ; ("wrapped_compat", Module.Name_map.to_dyn wrapped_compat) - ; ("alias_module", Module.to_dyn alias_module) - ; ("main_module_name", Module_name.to_dyn main_module_name) - ; ("wrapped", Wrapped.to_dyn wrapped) - ] - - let is_alias_name t name = Module.name t.alias_module = name + let impl_only { group; wrapped_compat; wrapped = _; toplevel_module = _ } = + let init = Module_name.Map.values wrapped_compat in + Group.fold group ~init ~f:(fun v acc -> + if Module.has v ~ml_kind:Impl then v :: acc else acc) - let impl_only - { modules - ; wrapped_compat - ; alias_module - ; main_module_name = _ - ; wrapped = _ - } = - let modules = - impl_only_of_map modules @ Module_name.Map.values wrapped_compat - in - alias_module :: modules - - let fold - { modules - ; wrapped_compat - ; alias_module - ; main_module_name = _ - ; wrapped = _ - } ~init ~f = - let init = f alias_module init in - let init = Module_name.Map.fold modules ~f ~init in + let fold { group; wrapped_compat; wrapped = _; toplevel_module = _ } ~init ~f + = + let init = Group.fold group ~f ~init in Module_name.Map.fold wrapped_compat ~f ~init - let exists - { modules - ; wrapped_compat - ; alias_module - ; main_module_name = _ - ; wrapped = _ - } ~f = - f alias_module - || Module_name.Map.exists modules ~f - || Module_name.Map.exists wrapped_compat ~f - - let lib_interface t = - Module_name.Map.find t.modules t.main_module_name - |> Option.value ~default:t.alias_module + let exists { group; wrapped_compat; wrapped = _; toplevel_module = _ } ~f = + Group.exists group ~f || Module_name.Map.exists wrapped_compat ~f let find t name = - if is_alias_name t name then Some t.alias_module - else - match Module_name.Map.find t.modules name with - | Some _ as m -> m - | None -> Module_name.Map.find t.wrapped_compat name - - let find_dep t ~of_ name = - match Module.kind of_ with - | Alias -> None - | Wrapped_compat -> - let li = lib_interface t in - Option.some_if (name = Module.name li) li - | _ -> - if is_alias_name t name then Some t.alias_module - else Module_name.Map.find t.modules name + match Module_name.Map.find t.group.modules name with + | Some (Module m) -> Some m + | Some (Group _) | None -> None + + let find_dep = + let rec closure_group g = + let lib_interface = Group.lib_interface g in + match Module.kind lib_interface with + | Alias _ -> + (* XXX ocamldep can't currently give us precise dependencies for + modules under [(include_subdirs qualified)] directories. For that + reason we currently depend on everything under the sub-directory. *) + Module_name.Map.values g.modules |> List.concat_map ~f:closure_node + | _ -> [ lib_interface ] + and closure_node = function + | Module m -> [ m ] + | Group g -> closure_group g + in + fun t ~of_ name -> + match Module.kind of_ with + | Alias _ -> Ok [] + | Wrapped_compat -> + let li = lib_interface t in + Ok (if Module_name.equal name (Module.name li) then [ li ] else []) + | _ -> ( + (* TODO don't recompute this *) + let parents = Group.parents t.group of_ in + match + List.find_map parents ~f:(fun parent -> + if Module_name.equal parent.name name then Some `Parent_cycle + else + Module_name.Map.find parent.modules name + |> Option.map ~f:(fun x -> `Found x)) + with + | None -> Ok [] + | Some `Parent_cycle -> Error `Parent_cycle + | Some (`Found m) -> Ok (closure_node m)) + + let group_interfaces (t : t) m = + Group.parents t.group m |> List.map ~f:Group.lib_interface let alias_for t m = match Module.kind m with - | Alias | Wrapped_compat -> None - | _ -> Some t.alias_module + | Alias _ | Wrapped_compat -> [] + | _ -> Group.parents t.group m |> List.map ~f:(fun (s : Group.t) -> s.alias) let relocate_alias_module t ~src_dir = - let alias_module = Module.set_src_dir t.alias_module ~src_dir in - { t with alias_module } + let group = Group.relocate_alias_module t.group ~src_dir in + { t with group } end type t = @@ -399,10 +602,6 @@ let rec encode t = | Stdlib m -> List (atom "stdlib" :: Stdlib.encode m) | Impl { impl; _ } -> encode impl -let as_singleton m = - if Module_name.Map.cardinal m <> 1 then None - else Module_name.Map.choose m |> Option.map ~f:snd - let singleton m = Singleton m let decode ~src_dir = @@ -445,7 +644,7 @@ let rec lib_interface = function let rec main_module_name = function | Singleton m -> Some (Module.name m) | Unwrapped _ -> None - | Wrapped w -> Some w.main_module_name + | Wrapped w -> Some w.group.name | Stdlib w -> Some w.main_module_name | Impl { vlib; impl = _ } -> main_module_name vlib @@ -459,11 +658,14 @@ let lib ~src_dir ~main_module_name ~wrapped ~stdlib ~lib_name ~implements match stdlib with | Some stdlib -> let main_module_name = Option.value_exn main_module_name in + let modules = Module_trie.to_map modules in Stdlib (Stdlib.make ~stdlib ~modules ~main_module_name) | None -> ( - match (wrapped, main_module_name, as_singleton modules) with + match (wrapped, main_module_name, Module_trie.as_singleton modules) with | Simple false, _, Some m -> Singleton m - | Simple false, _, None -> Unwrapped modules + | Simple false, _, None -> + (* TODO allow unwrapped modules to use [(include_subdirs qualified)] *) + Unwrapped (Module_trie.to_map modules) | (Yes_with_transition _ | Simple true), Some main_module_name, Some m -> if Module.name m = main_module_name && not implements then Singleton m else make_wrapped main_module_name @@ -490,37 +692,45 @@ let rec find t name = | Some _ as m -> m | None -> find vlib name) -type from = - | Vlib - | Impl_or_lib +exception Parent_cycle -let from_impl_or_lib = Option.map ~f:(fun m -> (Impl_or_lib, m)) - -let rec find_dep t ~of_ name = - if Module.name of_ = name then None - else - let open Option.O in - let* from, m = - match t with - | Stdlib s -> from_impl_or_lib (Stdlib.find_dep s ~of_ name) - | Wrapped w -> from_impl_or_lib (Wrapped.find_dep w ~of_ name) - | Impl { vlib; impl } -> ( - match find_dep impl ~of_ name with - | Some m -> Some (Impl_or_lib, m) - | None -> - let open Option.O in - let+ m = find_dep vlib ~of_ name in - (Vlib, m)) - | _ -> from_impl_or_lib (find t name) - in - match from with - | Impl_or_lib -> Some m - | Vlib -> Option.some_if (Module.visibility m = Public) m +let find_dep = + let from_impl_or_lib = List.map ~f:(fun m -> (`Impl_or_lib, m)) in + let find_dep_result = + List.filter_map ~f:(fun (from, m) -> + match from with + | `Impl_or_lib -> Some m + | `Vlib -> Option.some_if (Module.visibility m = Public) m) + in + let rec find_dep t ~of_ name : Module.t list = + if Module.name of_ = name then [] + else + let result = + match t with + | Wrapped w -> ( + match Wrapped.find_dep w ~of_ name with + | Ok s -> from_impl_or_lib s + | Error `Parent_cycle -> raise_notrace Parent_cycle) + | Stdlib s -> + Stdlib.find_dep s ~of_ name |> Option.to_list |> from_impl_or_lib + | Impl { vlib; impl } -> ( + match find_dep impl ~of_ name with + | [] -> find_dep vlib ~of_ name |> List.map ~f:(fun m -> (`Vlib, m)) + | xs -> from_impl_or_lib xs) + | _ -> find t name |> Option.to_list |> from_impl_or_lib + in + find_dep_result result + in + fun t ~of_ name -> + match find_dep t ~of_ name with + | s -> Ok s + | exception Parent_cycle -> Error `Parent_cycle let make_singleton m mangle = Singleton - (let main_module_name = (Mangle.prefix mangle).public in - Module.with_wrapper m ~main_module_name) + (let name = Module.name m in + let m = Module.set_path m [ name ] in + Mangle.wrap_module mangle m ~interface:None) let singleton_exe m = make_singleton m Exe @@ -532,7 +742,7 @@ let make_wrapped ~src_dir ~modules kind = | `Exe -> Exe | `Melange -> Melange in - match as_singleton modules with + match Module_trie.as_singleton modules with | Some m -> make_singleton m mangle | None -> Wrapped (Wrapped.make_exe_or_melange ~src_dir ~modules mangle) @@ -563,6 +773,25 @@ let rec fold_no_vlib t ~init ~f = | Wrapped w -> Wrapped.fold w ~init ~f | Impl { vlib = _; impl } -> fold_no_vlib impl ~f ~init +let rec for_alias t m = + match t with + | Stdlib _ | Singleton _ | Unwrapped _ -> Module_name.Map.empty + | Wrapped w -> Wrapped.for_alias w m + | Impl { vlib; impl } -> + let impl = for_alias impl m in + let vlib = for_alias vlib m in + Module_name.Map.merge impl vlib ~f:(fun _ impl vlib -> + match (impl, vlib) with + | None, None -> assert false + | Some _, _ -> impl + | _, Some vlib -> Option.some_if (Module.visibility vlib = Public) vlib) + +let fold_no_vlib_with_aliases t ~init ~normal ~alias = + fold_no_vlib t ~init ~f:(fun m acc -> + match Module.kind m with + | Alias _ -> alias m (for_alias t m) acc + | _ -> normal m acc) + type split_by_lib = { vlib : Module.t list ; impl : Module.t list @@ -581,27 +810,12 @@ let split_by_lib t = let compat_for_exn t m = match t with | Singleton _ | Stdlib _ | Unwrapped _ -> assert false - | Wrapped { modules; _ } -> - Module_name.Map.find modules (Module.name m) |> Option.value_exn | Impl _ -> Code_error.raise "wrapped compat not supported for vlib" [] - -let rec for_alias = function - | Stdlib _ | Singleton _ | Unwrapped _ -> Module_name.Map.empty - | Wrapped - { modules - ; main_module_name - ; alias_module = _ - ; wrapped_compat = _ - ; wrapped = _ - } -> Module_name.Map.remove modules main_module_name - | Impl { vlib; impl } -> - let impl = for_alias impl in - let vlib = for_alias vlib in - Module_name.Map.merge impl vlib ~f:(fun _ impl vlib -> - match (impl, vlib) with - | None, None -> assert false - | Some _, _ -> impl - | _, Some vlib -> Option.some_if (Module.visibility vlib = Public) vlib) + | Wrapped { group; _ } -> ( + match Module_name.Map.find group.modules (Module.name m) with + | None -> assert false + | Some (Module m) -> m + | Some (Group g) -> Wrapped.Group.lib_interface g) let wrapped_compat = function | Stdlib _ | Singleton _ | Impl _ | Unwrapped _ -> Module_name.Map.empty @@ -611,19 +825,15 @@ let rec fold_user_available t ~f ~init = match t with | Stdlib w -> Stdlib.fold w ~init ~f | Singleton m -> f m init - | Wrapped { modules; _ } | Unwrapped modules -> - Module_name.Map.fold modules ~init ~f + | Unwrapped modules -> Module_name.Map.fold modules ~init ~f + | Wrapped w -> Wrapped.fold_user_available w ~init ~f | Impl { impl; vlib = _ } -> (* XXX shouldn't we folding over [vlib] as well? *) fold_user_available impl ~f ~init let is_user_written m = match Module.kind m with - | Root -> false - | Wrapped_compat | Alias -> - (* Logically, this should be [acc]. But this is unreachable these are stored - separately *) - assert false + | Root | Wrapped_compat | Alias _ -> false | _ -> true let rec fold_user_written t ~f ~init = @@ -631,8 +841,8 @@ let rec fold_user_written t ~f ~init = match t with | Stdlib w -> Stdlib.fold w ~init ~f | Singleton m -> f m init - | Wrapped { modules; _ } | Unwrapped modules -> - Module_name.Map.fold modules ~init ~f + | Unwrapped modules -> Module_name.Map.fold modules ~init ~f + | Wrapped { group; _ } -> Wrapped.Group.fold group ~init ~f | Impl { impl; vlib = _ } -> fold_user_written impl ~f ~init let rec map_user_written t ~f = @@ -649,16 +859,9 @@ let rec map_user_written t ~f = let+ res = Stdlib.traverse w ~f in Stdlib res | Wrapped - ({ modules - ; alias_module = _ - ; main_module_name = _ - ; wrapped_compat = _ - ; wrapped = _ - } as w) -> - let+ modules = - Module_name.Map_traversals.parallel_map modules ~f:(fun _ -> f) - in - Wrapped { w with modules } + ({ group; wrapped_compat = _; wrapped = _; toplevel_module = _ } as w) -> + let+ group = Wrapped.Group.Memo_traversals.parallel_map group ~f in + Wrapped { w with group } | Impl t -> let+ vlib = map_user_written t.vlib ~f in Impl { t with vlib } @@ -718,23 +921,19 @@ let entry_modules t = | Singleton m -> [ m ] | Unwrapped m -> Module_name.Map.values m | Wrapped m -> - (* we assume this is never called for implementations *) - [ Wrapped.lib_interface m ] + [ (* we assume this is never called for implementations *) + Wrapped.lib_interface m + ] | Impl i -> Code_error.raise "entry_modules: not defined for implementations" [ ("impl", dyn_of_impl i) ]) let virtual_module_names = - fold_no_vlib ~init:Module_name.Set.empty ~f:(fun m acc -> + fold_no_vlib ~init:Module_name.Path.Set.empty ~f:(fun m acc -> match Module.kind m with - | Virtual -> Module_name.Set.add acc (Module.name m) + | Virtual -> Module_name.Path.Set.add acc [ Module.name m ] | _ -> acc) -let rec alias_module = function - | Stdlib _ | Singleton _ | Unwrapped _ -> None - | Wrapped w -> Some w.alias_module - | Impl { impl; vlib = _ } -> alias_module impl - let rec wrapped = function | Wrapped w -> w.wrapped | Singleton _ | Unwrapped _ -> Simple false @@ -743,14 +942,26 @@ let rec wrapped = function let rec alias_for t m = match Module.kind m with - | Root -> None + | Root -> [] | _ -> ( match t with - | Singleton _ | Unwrapped _ -> None + | Singleton _ | Unwrapped _ -> [] | Wrapped w -> Wrapped.alias_for w m - | Stdlib w -> Stdlib.alias_for w m + | Stdlib w -> Stdlib.alias_for w m |> Option.to_list | Impl { impl; vlib = _ } -> alias_for impl m) +let rec group_interfaces t m = + match t with + | Wrapped w -> Wrapped.group_interfaces w m + | Impl { impl; vlib } -> group_interfaces impl m @ group_interfaces vlib m + | Singleton w -> [ w ] + | _ -> [] + +let local_open t m = + alias_for t m + |> List.map ~f:(fun m -> + Module.obj_name m |> Module_name.Unique.to_name ~loc:Loc.none) + let is_stdlib_alias t m = match t with | Stdlib w -> w.main_module_name = Module.name m diff --git a/src/dune_rules/modules.mli b/src/dune_rules/modules.mli index 230d910c870..d5257a62e69 100644 --- a/src/dune_rules/modules.mli +++ b/src/dune_rules/modules.mli @@ -15,7 +15,7 @@ val lib : -> stdlib:Ocaml_stdlib.t option -> lib_name:Lib_name.Local.t -> implements:bool - -> modules:Module.Name_map.t + -> modules:Module.t Module_trie.t -> t val encode : t -> Dune_lang.t @@ -24,7 +24,11 @@ val decode : src_dir:Path.t -> t Dune_lang.Decoder.t val impl : t -> vlib:t -> t -val find_dep : t -> of_:Module.t -> Module_name.t -> Module.t option +val find_dep : + t + -> of_:Module.t + -> Module_name.t + -> (Module.t list, [ `Parent_cycle ]) result val find : t -> Module_name.t -> Module.t option @@ -41,18 +45,25 @@ val singleton_exe : Module.t -> t val fold_no_vlib : t -> init:'acc -> f:(Module.t -> 'acc -> 'acc) -> 'acc +val fold_no_vlib_with_aliases : + t + -> init:'acc + -> normal:(Module.t -> 'acc -> 'acc) + -> alias:(Module.t -> Module.t Module_name.Map.t -> 'acc -> 'acc) + -> 'acc + val exe_unwrapped : Module.Name_map.t -> t val make_wrapped : - src_dir:Path.Build.t -> modules:Module.Name_map.t -> [ `Exe | `Melange ] -> t + src_dir:Path.Build.t + -> modules:Module.t Module_trie.t + -> [ `Exe | `Melange ] + -> t (** For wrapped libraries, this is the user written entry module for the library. For single module libraries, it's the sole module in the library *) val lib_interface : t -> Module.t option -(** Returns the modules that need to be aliased in the alias module *) -val for_alias : t -> Module.Name_map.t - val fold_user_written : t -> f:(Module.t -> 'acc -> 'acc) -> init:'acc -> 'acc val map_user_written : t -> f:(Module.t -> Module.t Memo.t) -> t Memo.t @@ -84,17 +95,17 @@ val entry_modules : t -> Module.t list val main_module_name : t -> Module_name.t option (** Returns only the virtual module names in the library *) -val virtual_module_names : t -> Module_name.Set.t - -(** Returns the alias module if it exists. This module only exists for - [(wrapped true)] and when there is more than 1 module. *) -val alias_module : t -> Module.t option +val virtual_module_names : t -> Module_name.Path.Set.t val wrapped : t -> Wrapped.t val version_installed : t -> install_dir:Path.t -> t -val alias_for : t -> Module.t -> Module.t option +val alias_for : t -> Module.t -> Module.t list + +val group_interfaces : t -> Module.t -> Module.t list + +val local_open : t -> Module.t -> Module_name.t list val is_stdlib_alias : t -> Module.t -> bool diff --git a/src/dune_rules/modules_field_evaluator.ml b/src/dune_rules/modules_field_evaluator.ml index 65394aa65a1..ce4363721a6 100644 --- a/src/dune_rules/modules_field_evaluator.ml +++ b/src/dune_rules/modules_field_evaluator.ml @@ -7,7 +7,7 @@ end module Implementation = struct type t = - { existing_virtual_modules : Module_name.Set.t + { existing_virtual_modules : Module_name.Path.Set.t ; allow_new_public_modules : bool } end @@ -19,13 +19,20 @@ type kind = let eval = let key = function - | Error s -> s - | Ok m -> Module.Source.name m + | Error s -> [ s ] + | Ok m -> [ Module.Source.name m ] in - let module Unordered = Ordered_set_lang.Unordered (Module_name) in + let module Key = struct + type t = Module_name.Path.t + + let compare = Module_name.Path.compare + + module Map = Module_trie + end in + let module Unordered = Ordered_set_lang.Unordered (Key) in let parse ~all_modules ~fake_modules ~loc s = let name = Module_name.of_string_allow_invalid (loc, s) in - match Module_name.Map.find all_modules name with + match Module_trie.find all_modules [ name ] with | Some m -> Ok m | None -> fake_modules := Module_name.Map.set !fake_modules name loc; @@ -33,9 +40,9 @@ let eval = in fun ~loc ~fake_modules ~all_modules ~standard osl -> let parse = parse ~fake_modules ~all_modules in - let standard = Module_name.Map.map standard ~f:(fun m -> (loc, Ok m)) in + let standard = Module_trie.map standard ~f:(fun m -> (loc, Ok m)) in let modules = Unordered.eval_loc ~parse ~standard ~key osl in - Module_name.Map.filter_map modules ~f:(fun (loc, m) -> + Module_trie.filter_map modules ~f:(fun (loc, m) -> match m with | Ok m -> Some (loc, m) | Error s -> @@ -55,8 +62,8 @@ type single_module_error = | Vmodule_impls_with_own_intf type errors = - { errors : (single_module_error * Loc.t * Module_name.t) list - ; unimplemented_virt_modules : Module_name.Set.t + { errors : (single_module_error * Loc.t * Module_name.Path.t) list + ; unimplemented_virt_modules : Module_name.Path.Set.t } let find_errors ~modules ~intf_only ~virtual_modules ~private_modules @@ -65,21 +72,21 @@ let find_errors ~modules ~intf_only ~virtual_modules ~private_modules (* We expect that [modules] is big and all the other ones are small, that's why the code is implemented this way. *) List.fold_left [ intf_only; virtual_modules; private_modules ] - ~init:(Module_name.Map.map modules ~f:snd) ~f:(fun acc map -> - Module_name.Map.foldi map ~init:acc ~f:(fun name (_loc, m) acc -> - Module_name.Map.set acc name m)) + ~init:(Module_trie.map modules ~f:snd) ~f:(fun acc map -> + Module_trie.foldi map ~init:acc ~f:(fun name (_loc, m) acc -> + Module_trie.set acc name m)) in let errors = - Module_name.Map.foldi all ~init:[] ~f:(fun module_name module_ acc -> + Module_trie.foldi all ~init:[] ~f:(fun module_name module_ acc -> let has_impl = Module.Source.has module_ ~ml_kind:Impl in let has_intf = Module.Source.has module_ ~ml_kind:Intf in let impl_vmodule = - Module_name.Set.mem existing_virtual_modules module_name + Module_name.Path.Set.mem existing_virtual_modules module_name in - let modules = Module_name.Map.find modules module_name in - let private_ = Module_name.Map.find private_modules module_name in - let virtual_ = Module_name.Map.find virtual_modules module_name in - let intf_only = Module_name.Map.find intf_only module_name in + let modules = Module_trie.find modules module_name in + let private_ = Module_trie.find private_modules module_name in + let virtual_ = Module_trie.find virtual_modules module_name in + let intf_only = Module_trie.find intf_only module_name in let with_property prop f acc = match prop with | None -> acc @@ -112,8 +119,8 @@ let find_errors ~modules ~intf_only ~virtual_modules ~private_modules @@ acc) in let unimplemented_virt_modules = - Module_name.Set.filter existing_virtual_modules ~f:(fun module_name -> - match Module_name.Map.find all module_name with + Module_name.Path.Set.filter existing_virtual_modules ~f:(fun module_name -> + match Module_trie.find all module_name with | None -> true | Some m -> not (Module.Source.has m ~ml_kind:Impl)) in @@ -128,12 +135,12 @@ let check_invalid_module_listing ~stanza_loc ~modules_without_implementation in if List.is_non_empty errors - || not (Module_name.Set.is_empty unimplemented_virt_modules) + || not (Module_name.Path.Set.is_empty unimplemented_virt_modules) then ( let get kind = List.filter_map errors ~f:(fun (k, loc, m) -> Option.some_if (kind = k) (loc, m)) - |> List.sort ~compare:(fun (_, a) (_, b) -> Module_name.compare a b) + |> List.sort ~compare:(fun (_, a) (_, b) -> Module_name.Path.compare a b) in let vmodule_impls_with_own_intf = get Vmodule_impls_with_own_intf in let forbidden_new_public_modules = get Forbidden_new_public_module in @@ -148,11 +155,11 @@ let check_invalid_module_listing ~stanza_loc ~modules_without_implementation let spurious_modules_intf = get Spurious_module_intf in let spurious_modules_virtual = get Spurious_module_virtual in let uncapitalized = - List.map ~f:(fun (_, m) -> Module_name.uncapitalize m) + List.map ~f:(fun (_, m) -> Module_name.Path.uncapitalize m) in let line_list modules = Pp.enumerate modules ~f:(fun (_, m) -> - Pp.verbatim (Module_name.to_string m)) + Pp.verbatim (Module_name.Path.to_string m)) in let print before l after = match l with @@ -203,7 +210,7 @@ let check_invalid_module_listing ~stanza_loc ~modules_without_implementation [ Pp.text "This is not possible." ]; print [ Pp.text "These modules are declared virtual, but are missing." ] - (unimplemented_virt_modules |> Module_name.Set.to_list + (unimplemented_virt_modules |> Module_name.Path.Set.to_list |> List.map ~f:(fun name -> (stanza_loc, name))) [ Pp.text "You must provide an implementation for all of these modules." ]; (if missing_intf_only <> [] then @@ -241,16 +248,16 @@ let check_invalid_module_listing ~stanza_loc ~modules_without_implementation ] spurious_modules_virtual []) -let eval ~modules:all_modules ~stanza_loc ~modules_field - ~modules_without_implementation ~root_module ~private_modules ~kind ~src_dir - = +let eval ~modules:(all_modules : Module.Source.t Module_trie.t) ~stanza_loc + ~modules_field ~modules_without_implementation ~root_module ~private_modules + ~kind ~src_dir = (* Fake modules are modules that do not exist but it doesn't matter because they are only removed from a set (for jbuild file compatibility) *) let fake_modules = ref Module_name.Map.empty in let eval = eval ~loc:stanza_loc ~fake_modules ~all_modules in let modules = eval ~standard:all_modules modules_field in let intf_only = - eval ~standard:Module_name.Map.empty modules_without_implementation + eval ~standard:Module_trie.empty modules_without_implementation in let allow_new_public_modules = match kind with @@ -259,16 +266,16 @@ let eval ~modules:all_modules ~stanza_loc ~modules_field in let existing_virtual_modules = match kind with - | Exe_or_normal_lib | Virtual _ -> Module_name.Set.empty + | Exe_or_normal_lib | Virtual _ -> Module_name.Path.Set.empty | Implementation { existing_virtual_modules; _ } -> existing_virtual_modules in let virtual_modules = match kind with - | Exe_or_normal_lib | Implementation _ -> Module_name.Map.empty + | Exe_or_normal_lib | Implementation _ -> Module_trie.empty | Virtual { virtual_modules } -> - eval ~standard:Module_name.Map.empty virtual_modules + eval ~standard:Module_trie.empty virtual_modules in - let private_modules = eval ~standard:Module_name.Map.empty private_modules in + let private_modules = eval ~standard:Module_trie.empty private_modules in Module_name.Map.iteri !fake_modules ~f:(fun m loc -> User_error.raise ~loc [ Pp.textf "Module %s is excluded but it doesn't exist." @@ -278,25 +285,25 @@ let eval ~modules:all_modules ~stanza_loc ~modules_field ~intf_only ~modules ~virtual_modules ~private_modules ~existing_virtual_modules ~allow_new_public_modules; let all_modules = - Module_name.Map.map modules ~f:(fun (_, m) -> - let name = Module.Source.name m in + Module_trie.mapi modules ~f:(fun path (_, m) -> + let name = [ Module.Source.name m ] in let visibility = - if Module_name.Map.mem private_modules name then Visibility.Private + if Module_trie.mem private_modules name then Visibility.Private else Public in let kind = - if Module_name.Map.mem virtual_modules name then Module.Kind.Virtual + if Module_trie.mem virtual_modules name then Module.Kind.Virtual else if Module.Source.has m ~ml_kind:Impl then let name = Module.Source.name m in - if Module_name.Set.mem existing_virtual_modules name then + if Module_name.Path.Set.mem existing_virtual_modules [ name ] then Impl_vmodule else Impl else Intf_only in - Module.of_source m ~kind ~visibility) + Module.of_source m ~path ~kind ~visibility) in match root_module with | None -> all_modules | Some (_, name) -> let module_ = Module.generated ~kind:Root ~src_dir name in - Module_name.Map.set all_modules name module_ + Module_trie.set all_modules [ name ] module_ diff --git a/src/dune_rules/modules_field_evaluator.mli b/src/dune_rules/modules_field_evaluator.mli index b6665a97aaa..617b727a2c6 100644 --- a/src/dune_rules/modules_field_evaluator.mli +++ b/src/dune_rules/modules_field_evaluator.mli @@ -9,7 +9,7 @@ end module Implementation : sig type t = - { existing_virtual_modules : Module_name.Set.t + { existing_virtual_modules : Module_name.Path.Set.t ; allow_new_public_modules : bool } end @@ -20,7 +20,7 @@ type kind = | Exe_or_normal_lib val eval : - modules:Module.Source.t Module_name.Map.t + modules:Module.Source.t Module_trie.t -> stanza_loc:Loc.t -> modules_field:Ordered_set_lang.t -> modules_without_implementation:Ordered_set_lang.t @@ -28,4 +28,4 @@ val eval : -> private_modules:Ordered_set_lang.t -> kind:kind -> src_dir:Path.Build.t - -> Module.t Module_name.Map.t + -> Module.t Module_trie.t diff --git a/src/dune_rules/ocamldep.ml b/src/dune_rules/ocamldep.ml index c131a1d6cfb..9419b5175cb 100644 --- a/src/dune_rules/ocamldep.ml +++ b/src/dune_rules/ocamldep.ml @@ -14,10 +14,39 @@ end open Modules_data -let parse_module_names ~(unit : Module.t) ~modules words = - List.filter_map words ~f:(fun m -> +let parse_module_names ~dir ~(unit : Module.t) ~modules words = + List.concat_map words ~f:(fun m -> let m = Module_name.of_string m in - Modules.find_dep modules ~of_:unit m) + match Modules.find_dep modules ~of_:unit m with + | Ok s -> s + | Error `Parent_cycle -> + User_error.raise + [ Pp.textf "Module %s in directory %s depends on %s." + (Module_name.to_string (Module.name unit)) + (Path.to_string_maybe_quoted (Path.build dir)) + (Module_name.to_string m) + ; Pp.textf "This doesn't make sense to me." + ; Pp.nop + ; Pp.textf + "%s is the main module of the library and is the only module \ + exposed outside of the library. Consequently, it should be the \ + one depending on all the other modules in the library." + (Module_name.to_string m) + ]) + +let parse_compilation_units ~modules = + let obj_map = + Modules.obj_map modules ~f:(function + | Normal m -> m + | Imported_from_vlib m -> m + | Impl_of_virtual_module { intf = _; impl } -> impl) + |> Module.Obj_map.to_list_map ~f:(fun m _ -> (Module.obj_name m, m)) + |> Module_name.Unique.Map.of_list_exn + in + Staged.stage + (List.filter_map ~f:(fun m -> + let obj_name = Module_name.Unique.of_string m in + Module_name.Unique.Map.find obj_map obj_name)) let parse_deps_exn ~file lines = let invalid () = @@ -39,43 +68,12 @@ let parse_deps_exn ~file lines = if basename <> Path.basename file then invalid (); String.extract_blank_separated_words deps) -let interpret_deps md ~unit deps = - let dir = md.dir in - let modules = md.modules in - let deps = parse_module_names ~unit ~modules deps in - if Option.is_none md.stdlib then - Modules.main_module_name modules - |> Option.iter ~f:(fun (main_module_name : Module_name.t) -> - if - Module_name.Infix.(Module.name unit <> main_module_name) - && (not (Module.kind unit = Alias)) - && List.exists deps ~f:(fun x -> Module.name x = main_module_name) - then - User_error.raise - [ Pp.textf "Module %s in directory %s depends on %s." - (Module_name.to_string (Module.name unit)) - (Path.to_string_maybe_quoted (Path.build dir)) - (Module_name.to_string main_module_name) - ; Pp.textf "This doesn't make sense to me." - ; Pp.nop - ; Pp.textf - "%s is the main module of the library and is the only \ - module exposed outside of the library. Consequently, it \ - should be the one depending on all the other modules in \ - the library." - (Module_name.to_string main_module_name) - ]); - match Modules.alias_for modules unit with - | None -> deps - | Some m -> m :: deps - let deps_of ({ sandbox; modules; sctx; dir; obj_dir; vimpl = _; stdlib = _ } as md) ~ml_kind unit = let source = Option.value_exn (Module.source unit ~ml_kind) in let dep = Obj_dir.Module.dep obj_dir in let context = Super_context.context sctx in - let parse_module_names = parse_module_names ~modules in let all_deps_file = dep (Transitive (unit, ml_kind)) in let ocamldep_output = dep (Immediate source) in let open Memo.O in @@ -99,9 +97,10 @@ let deps_of let build_paths dependencies = let dependency_file_path m = let ml_kind m = - if Module.kind m = Alias then None - else if Module.has m ~ml_kind:Intf then Some Ml_kind.Intf - else Some Impl + match Module.kind m with + | Alias _ -> None + | _ -> + if Module.has m ~ml_kind:Intf then Some Ml_kind.Intf else Some Impl in ml_kind m |> Option.map ~f:(fun ml_kind -> @@ -115,10 +114,11 @@ let deps_of let+ lines = Action_builder.lines_of (Path.build ocamldep_output) in let modules = parse_deps_exn ~file:(Module.File.path source) lines - |> interpret_deps md ~unit + |> parse_module_names ~dir:md.dir ~unit ~modules in ( build_paths modules - , List.map modules ~f:(fun m -> Module_name.to_string (Module.name m)) ) + , List.map modules ~f:(fun m -> + Module.obj_name m |> Module_name.Unique.to_string) ) in Action_builder.with_file_targets ~file_targets:[ all_deps_file ] (let+ sources, extras = @@ -135,7 +135,8 @@ let deps_of let all_deps_file = Path.build all_deps_file in Action_builder.memoize (Path.to_string all_deps_file) - (Action_builder.map ~f:(parse_module_names ~unit) + (Action_builder.map + ~f:(Staged.unstage @@ parse_compilation_units ~modules) (Action_builder.lines_of all_deps_file)) let read_deps_of ~obj_dir ~modules ~ml_kind unit = @@ -143,7 +144,7 @@ let read_deps_of ~obj_dir ~modules ~ml_kind unit = Action_builder.memoize (Path.Build.to_string all_deps_file) (Action_builder.map - ~f:(parse_module_names ~unit ~modules) + ~f:(Staged.unstage @@ parse_compilation_units ~modules) (Action_builder.lines_of (Path.build all_deps_file))) let read_immediate_deps_of ~obj_dir ~modules ~ml_kind unit = @@ -156,5 +157,5 @@ let read_immediate_deps_of ~obj_dir ~modules ~ml_kind unit = (Action_builder.map ~f:(fun lines -> parse_deps_exn ~file:(Module.File.path source) lines - |> parse_module_names ~unit ~modules) + |> parse_module_names ~dir:(Obj_dir.dir obj_dir) ~unit ~modules) (Action_builder.lines_of (Path.build ocamldep_output))) diff --git a/src/dune_rules/per_item.ml b/src/dune_rules/per_item.ml index c91a2c047ea..f012f1381b5 100644 --- a/src/dune_rules/per_item.ml +++ b/src/dune_rules/per_item.ml @@ -10,6 +10,10 @@ module Make (Key : Map.Key) : Per_item_intf.S with type key = Key.t = struct ; values : 'a array } + let to_dyn f { map; values } = + let open Dyn in + record [ ("map", Map.to_dyn int map); ("values", array f values) ] + let equal f t { values; map } = Array.equal f t.values values && Map.equal ~equal:Int.equal t.map map diff --git a/src/dune_rules/per_item_intf.ml b/src/dune_rules/per_item_intf.ml index 42a10cc716c..604db01459d 100644 --- a/src/dune_rules/per_item_intf.ml +++ b/src/dune_rules/per_item_intf.ml @@ -5,6 +5,8 @@ module type S = sig type 'a t + val to_dyn : ('a -> Dyn.t) -> 'a t -> Dyn.t + val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> bool (** Create a mapping where all keys map to the same value *) diff --git a/src/dune_rules/simple_rules.ml b/src/dune_rules/simple_rules.ml index 119aa89641d..79e371603bf 100644 --- a/src/dune_rules/simple_rules.ml +++ b/src/dune_rules/simple_rules.ml @@ -202,8 +202,9 @@ let copy_files sctx ~dir ~expander ~src_dir (def : Copy_files.t) = ~f:(fun file_src -> let basename = Path.basename file_src in let file_dst = Path.Build.relative dir basename in + let context = Super_context.context sctx in Super_context.add_rule sctx ~loc ~dir ~mode:def.mode - ((if def.add_line_directive then Copy_line_directive.builder + ((if def.add_line_directive then Copy_line_directive.builder context else Action_builder.copy) ~src:file_src ~dst:file_dst)) in diff --git a/src/dune_sexp/encoder.ml b/src/dune_sexp/encoder.ml index 53245b15545..7b46022cf95 100644 --- a/src/dune_sexp/encoder.ml +++ b/src/dune_sexp/encoder.ml @@ -68,3 +68,8 @@ let record_fields (l : field list) = | Inlined_list (name, l) -> Some (List (Atom (Atom.of_string name) :: l))) let unknown _ = atom "" + +let enum xs x = + match List.find_map xs ~f:(fun (s, x') -> Option.some_if (x = x') s) with + | None -> Code_error.raise "Encoder.enum: invalid definition" [] + | Some s -> atom_or_quoted_string s diff --git a/src/dune_sexp/encoder.mli b/src/dune_sexp/encoder.mli index 00c1b1fa7b7..16ee46ff214 100644 --- a/src/dune_sexp/encoder.mli +++ b/src/dune_sexp/encoder.mli @@ -28,3 +28,5 @@ val field_i : string -> ('a -> T.t list) -> 'a -> field val record_fields : field list -> T.t list val unknown : _ t + +val enum : (string * 'a) list -> 'a t diff --git a/test/blackbox-tests/test-cases/describe.t b/test/blackbox-tests/test-cases/describe.t index 99bc332afba..3b927ab05f6 100644 --- a/test/blackbox-tests/test-cases/describe.t +++ b/test/blackbox-tests/test-cases/describe.t @@ -53,9 +53,9 @@ Setup > (modules_without_implementation main2_aux4)) > > (executable - > (name main3) - > (libraries cmdliner) - > (modules main3)) + > (name main3) + > (libraries cmdliner) + > (modules main3)) > > (library > (name per_module_pp_lib) diff --git a/test/blackbox-tests/test-cases/dune-package.t/run.t b/test/blackbox-tests/test-cases/dune-package.t/run.t index cfd6559af2f..82f1edc891d 100644 --- a/test/blackbox-tests/test-cases/dune-package.t/run.t +++ b/test/blackbox-tests/test-cases/dune-package.t/run.t @@ -56,14 +56,17 @@ (modes byte native) (modules (wrapped - (main_module_name A) - (modules ((name X) (obj_name a__X) (visibility public) (impl))) - (alias_module + (group + (alias + (name A) + (obj_name a) + (path A) + (visibility public) + (kind alias) + (impl)) (name A) - (obj_name a) - (visibility public) - (kind alias) - (impl)) + (modules + (module (name X) (obj_name a__X) (path X) (visibility public) (impl)))) (wrapped true)))) (library (name a.b.c) @@ -76,14 +79,23 @@ (obj_dir (private_dir .private)) (modules (wrapped - (main_module_name C) - (modules ((name Y) (obj_name c__Y) (visibility private) (impl) (intf))) - (alias_module + (group + (alias + (name C) + (obj_name c) + (path C) + (visibility public) + (kind alias) + (impl)) (name C) - (obj_name c) - (visibility public) - (kind alias) - (impl)) + (modules + (module + (name Y) + (obj_name c__Y) + (path Y) + (visibility private) + (impl) + (intf)))) (wrapped true)))) (library (name a.byte_only) @@ -94,14 +106,17 @@ (modes byte) (modules (wrapped - (main_module_name D) - (modules ((name Z) (obj_name d__Z) (visibility public) (impl))) - (alias_module + (group + (alias + (name D) + (obj_name d) + (path D) + (visibility public) + (kind alias) + (impl)) (name D) - (obj_name d) - (visibility public) - (kind alias) - (impl)) + (modules + (module (name Z) (obj_name d__Z) (path Z) (visibility public) (impl)))) (wrapped true)))) Build with "--store-orig-source-dir" profile diff --git a/test/blackbox-tests/test-cases/gh5267.t b/test/blackbox-tests/test-cases/gh5267.t index a6b28a41a19..4d8d8f312bc 100644 --- a/test/blackbox-tests/test-cases/gh5267.t +++ b/test/blackbox-tests/test-cases/gh5267.t @@ -20,6 +20,12 @@ same directory. $ touch foo.mli $ dune build ./bar.exe - File "foo.ml-gen", line 1: - Error: Could not find the .cmi file for interface foo.mli. + File "dune", line 1, characters 0-0: + Error: Module "Foo" is used in several stanzas: + - dune:1 + - dune:4 + To fix this error, you must specify an explicit "modules" field in every + library, executable, and executables stanzas in this dune file. Note that + each module cannot appear in more than one "modules" field - it must belong + to a single library or executable. [1] diff --git a/test/blackbox-tests/test-cases/github1549.t/run.t b/test/blackbox-tests/test-cases/github1549.t/run.t index a666c1578f8..09f55afe5a0 100644 --- a/test/blackbox-tests/test-cases/github1549.t/run.t +++ b/test/blackbox-tests/test-cases/github1549.t/run.t @@ -31,13 +31,15 @@ Reproduction case for #1549: too many parentheses in installed .dune files (modes byte native) (modules (wrapped - (main_module_name Simple_tests) - (alias_module - (name Simple_tests) - (obj_name simple_tests) - (visibility public) - (kind alias) - (impl)) + (group + (alias + (name Simple_tests) + (obj_name simple_tests) + (path Simple_tests) + (visibility public) + (kind alias) + (impl)) + (name Simple_tests)) (wrapped true))) (inline_tests.backend (flags :standard) diff --git a/test/blackbox-tests/test-cases/github1946.t/run.t b/test/blackbox-tests/test-cases/github1946.t/run.t index 895c2b54b80..8faa87f3d36 100644 --- a/test/blackbox-tests/test-cases/github1946.t/run.t +++ b/test/blackbox-tests/test-cases/github1946.t/run.t @@ -19,7 +19,7 @@ in the same dune file, but require different ppx specifications --as-ppx --cookie 'library-name="usesppx1"'")) - (FLG (-open Usesppx1 -w -40))) + (FLG (-w -40))) Usesppx2 ((STDLIB /OCAMLC_WHERE) (EXCLUDE_QUERY_DIR) @@ -33,4 +33,4 @@ in the same dune file, but require different ppx specifications --as-ppx --cookie 'library-name="usesppx2"'")) - (FLG (-open Usesppx2 -w -40))) + (FLG (-w -40))) diff --git a/test/blackbox-tests/test-cases/github759.t/run.t b/test/blackbox-tests/test-cases/github759.t/run.t index 384fd936183..716c2f5ba56 100644 --- a/test/blackbox-tests/test-cases/github759.t/run.t +++ b/test/blackbox-tests/test-cases/github759.t/run.t @@ -10,7 +10,7 @@ $TESTCASE_ROOT/_build/default/.foo.objs/byte) (S $TESTCASE_ROOT) - (FLG (-open Foo -w -40))) + (FLG (-w -40))) $ rm -f .merlin $ dune build foo.cma --profile release @@ -22,7 +22,7 @@ $TESTCASE_ROOT/_build/default/.foo.objs/byte) (S $TESTCASE_ROOT) - (FLG (-open Foo -w -40))) + (FLG (-w -40))) $ echo toto > .merlin $ dune build foo.cma --profile release @@ -34,4 +34,4 @@ $TESTCASE_ROOT/_build/default/.foo.objs/byte) (S $TESTCASE_ROOT) - (FLG (-open Foo -w -40))) + (FLG (-w -40))) diff --git a/test/blackbox-tests/test-cases/include-qualified/basic.t/dune-project b/test/blackbox-tests/test-cases/include-qualified/basic.t/dune-project index 1863cf14648..3c48133ad58 100644 --- a/test/blackbox-tests/test-cases/include-qualified/basic.t/dune-project +++ b/test/blackbox-tests/test-cases/include-qualified/basic.t/dune-project @@ -1 +1 @@ -(lang dune 3.5) +(lang dune 3.7) diff --git a/test/blackbox-tests/test-cases/include-qualified/basic.t/run.t b/test/blackbox-tests/test-cases/include-qualified/basic.t/run.t index cfc667daf76..fb32f0be9ef 100644 --- a/test/blackbox-tests/test-cases/include-qualified/basic.t/run.t +++ b/test/blackbox-tests/test-cases/include-qualified/basic.t/run.t @@ -1,8 +1,27 @@ Basic test showcasing the feature. Every directory creates a new level of aliasing. $ dune build - File "lib/dune", line 1, characters 17-26: - 1 | (include_subdirs qualified) - ^^^^^^^^^ - Error: Unknown value qualified - Hint: did you mean unqualified? - [1] + hello from nested module bar + hello from nested module B + + $ find _build -iname "*.ml-gen" | sort | while read file; do echo "contents of $file"; cat $file; echo "--------"; done; + contents of _build/default/lib/foolib.ml-gen + (* generated by dune *) + + (** @canonical Foolib.Foo *) + module Foo = Foolib__Foo + -------- + contents of _build/default/lib/foolib__Foo.ml-gen + (* generated by dune *) + + (** @canonical Foolib.A *) + module A = Foolib__Foo__A + + (** @canonical Foolib.Bar *) + module Bar = Foolib__Foo__Bar + -------- + contents of _build/default/lib/foolib__Foo__A.ml-gen + (* generated by dune *) + + (** @canonical Foolib.B *) + module B = Foolib__Foo__A__B + -------- diff --git a/test/blackbox-tests/test-cases/include-qualified/exe.t b/test/blackbox-tests/test-cases/include-qualified/exe.t new file mode 100644 index 00000000000..62f19a6e082 --- /dev/null +++ b/test/blackbox-tests/test-cases/include-qualified/exe.t @@ -0,0 +1,19 @@ + + $ cat >dune-project < (lang dune 3.7) + > EOF + + $ cat >dune < (include_subdirs qualified) + > (executable + > (name foo)) + > EOF + + $ mkdir -p bar/baz/ baz/ + $ touch bar/baz/baz.ml baz/bar.ml + $ cat >foo.ml < module X = Baz.Bar + > module Y = Bar.Baz + > EOF + + $ dune exec ./foo.exe diff --git a/test/blackbox-tests/test-cases/include-qualified/group-module-overlap.t b/test/blackbox-tests/test-cases/include-qualified/group-module-overlap.t new file mode 100644 index 00000000000..c564685159c --- /dev/null +++ b/test/blackbox-tests/test-cases/include-qualified/group-module-overlap.t @@ -0,0 +1,26 @@ +Dune should not allow mod.ml and mod/ in the same directory: + + $ cat >dune-project < (lang dune 3.7) + > EOF + $ cat >dune < (include_subdirs qualified) + > (executable + > (name foo)) + > EOF + + $ cat >foo.ml < module X = Mod + > EOF + $ touch mod.ml + $ mkdir mod + $ touch mod/baz.ml + + $ dune build foo.exe + +Another type of overlap: + + $ rm mod/baz.ml + $ touch mod/mod.ml + + $ dune build foo.exe diff --git a/test/blackbox-tests/test-cases/include-qualified/invalid-deps/group-interface-sub-module.t b/test/blackbox-tests/test-cases/include-qualified/invalid-deps/group-interface-sub-module.t new file mode 100644 index 00000000000..ed3617c1ae3 --- /dev/null +++ b/test/blackbox-tests/test-cases/include-qualified/invalid-deps/group-interface-sub-module.t @@ -0,0 +1,31 @@ +We shouldn't allow foo/y/$x.ml to depend on foo/foo.ml + + $ cat > dune-project << EOF + > (lang dune 3.7) + > EOF + + $ cat > dune << EOF + > (include_subdirs qualified) + > (library + > (name foo)) + > EOF + + $ mkdir -p x/y + $ touch x/x.ml + $ cat >x/y/z.ml < let () = X.f + > EOF + + $ dune build + Error: Module Z in directory _build/default depends on X. + This doesn't make sense to me. + + X is the main module of the library and is the only module exposed outside of + the library. Consequently, it should be the one depending on all the other + modules in the library. + -> required by _build/default/.foo.objs/foo__X__Y__Z.impl.all-deps + -> required by _build/default/.foo.objs/byte/foo__X__Y__Z.cmo + -> required by _build/default/foo.cma + -> required by alias all + -> required by alias default + [1] diff --git a/test/blackbox-tests/test-cases/include-qualified/invalid-deps/group-interface.t b/test/blackbox-tests/test-cases/include-qualified/invalid-deps/group-interface.t new file mode 100644 index 00000000000..8682855cf4c --- /dev/null +++ b/test/blackbox-tests/test-cases/include-qualified/invalid-deps/group-interface.t @@ -0,0 +1,31 @@ +We shouldn't allow foo/$x.ml to depend on foo/foo.ml + + $ cat > dune-project << EOF + > (lang dune 3.7) + > EOF + + $ cat > dune << EOF + > (include_subdirs qualified) + > (library + > (name foo)) + > EOF + + $ mkdir baz + $ touch baz/baz.ml + $ cat >baz/bar.ml < let () = Baz.f + > EOF + + $ dune build + Error: Module Bar in directory _build/default depends on Baz. + This doesn't make sense to me. + + Baz is the main module of the library and is the only module exposed outside + of the library. Consequently, it should be the one depending on all the other + modules in the library. + -> required by _build/default/.foo.objs/foo__Baz__Bar.impl.all-deps + -> required by _build/default/.foo.objs/byte/foo__Baz__Bar.cmo + -> required by _build/default/foo.cma + -> required by alias all + -> required by alias default + [1] diff --git a/test/blackbox-tests/test-cases/include-qualified/invalid-deps/toplevel-lib-interface.t b/test/blackbox-tests/test-cases/include-qualified/invalid-deps/toplevel-lib-interface.t new file mode 100644 index 00000000000..35fd7cb4187 --- /dev/null +++ b/test/blackbox-tests/test-cases/include-qualified/invalid-deps/toplevel-lib-interface.t @@ -0,0 +1,24 @@ +We should forbid lib interfaces modules from depending on themselves: + + $ cat > dune-project << EOF + > (lang dune 3.7) + > EOF + + $ cat > dune << EOF + > (include_subdirs qualified) + > (library + > (name foo)) + > EOF + + $ cat > foo.ml < let () = Foo.f () + > EOF + + $ touch bar.ml + + $ dune build @check + File "foo.ml", line 1, characters 9-14: + 1 | let () = Foo.f () + ^^^^^ + Error: Unbound module Foo + [1] diff --git a/test/blackbox-tests/test-cases/include-qualified/nested-lib-interface.t/dune-project b/test/blackbox-tests/test-cases/include-qualified/nested-lib-interface.t/dune-project index 1863cf14648..3c48133ad58 100644 --- a/test/blackbox-tests/test-cases/include-qualified/nested-lib-interface.t/dune-project +++ b/test/blackbox-tests/test-cases/include-qualified/nested-lib-interface.t/dune-project @@ -1 +1 @@ -(lang dune 3.5) +(lang dune 3.7) diff --git a/test/blackbox-tests/test-cases/include-qualified/nested-lib-interface.t/exe/dune b/test/blackbox-tests/test-cases/include-qualified/nested-lib-interface.t/exe/dune new file mode 100644 index 00000000000..3f280db8253 --- /dev/null +++ b/test/blackbox-tests/test-cases/include-qualified/nested-lib-interface.t/exe/dune @@ -0,0 +1,3 @@ +(executable + (libraries foolib) + (name test)) diff --git a/test/blackbox-tests/test-cases/include-qualified/nested-lib-interface.t/run.t b/test/blackbox-tests/test-cases/include-qualified/nested-lib-interface.t/run.t index bc6f4d790ca..c82539b824b 100644 --- a/test/blackbox-tests/test-cases/include-qualified/nested-lib-interface.t/run.t +++ b/test/blackbox-tests/test-cases/include-qualified/nested-lib-interface.t/run.t @@ -1,8 +1,36 @@ We are also allowed to write lib interface files at each level. $ dune build - File "lib/dune", line 1, characters 17-26: - 1 | (include_subdirs qualified) - ^^^^^^^^^ - Error: Unknown value qualified - Hint: did you mean unqualified? - [1] + defined in lib interface file + hello from baz + + $ find _build -iname "*.cmi" | sort + _build/default/exe/.test.eobjs/byte/dune__exe__Test.cmi + _build/default/lib/.foolib.objs/byte/foolib.cmi + _build/default/lib/.foolib.objs/byte/foolib__.cmi + _build/default/lib/.foolib.objs/byte/foolib__Bar.cmi + _build/default/lib/.foolib.objs/byte/foolib__Bar__.cmi + _build/default/lib/.foolib.objs/byte/foolib__Bar__Baz.cmi + _build/default/lib/.foolib.objs/byte/foolib__Private.cmi + + $ find _build -iname "*.ml-gen" | sort | while read file; do echo "contents of $file"; cat $file; echo "--------"; done; + contents of _build/default/lib/foolib__.ml-gen + (* generated by dune *) + + (** @canonical Foolib.Bar *) + module Bar = Foolib__Bar + + (** @canonical Foolib.Private *) + module Private = Foolib__Private + + module Foolib__ = struct end + [@@deprecated "this module is shadowed"] + -------- + contents of _build/default/lib/foolib__Bar__.ml-gen + (* generated by dune *) + + (** @canonical Foolib.Baz *) + module Baz = Foolib__Bar__Baz + + module Foolib__Bar__ = struct end + [@@deprecated "this module is shadowed"] + -------- diff --git a/test/blackbox-tests/test-cases/include-qualified/nested-virtual.t/dune-project b/test/blackbox-tests/test-cases/include-qualified/nested-virtual.t/dune-project index 1863cf14648..3c48133ad58 100644 --- a/test/blackbox-tests/test-cases/include-qualified/nested-virtual.t/dune-project +++ b/test/blackbox-tests/test-cases/include-qualified/nested-virtual.t/dune-project @@ -1 +1 @@ -(lang dune 3.5) +(lang dune 3.7) diff --git a/test/blackbox-tests/test-cases/include-qualified/nested-virtual.t/run.t b/test/blackbox-tests/test-cases/include-qualified/nested-virtual.t/run.t index 8afc3a625d9..a86861acb9e 100644 --- a/test/blackbox-tests/test-cases/include-qualified/nested-virtual.t/run.t +++ b/test/blackbox-tests/test-cases/include-qualified/nested-virtual.t/run.t @@ -1,13 +1,7 @@ We can nested modules virtual $ dune build @all - File "impl/dune", line 1, characters 17-26: - 1 | (include_subdirs qualified) - ^^^^^^^^^ - Error: Unknown value qualified - Hint: did you mean unqualified? - File "vlib/dune", line 1, characters 17-26: - 1 | (include_subdirs qualified) - ^^^^^^^^^ - Error: Unknown value qualified - Hint: did you mean unqualified? + File "vlib/dune", line 5, characters 18-26: + 5 | (virtual_modules bar/virt)) + ^^^^^^^^ + Error: Module Bar/virt doesn't exist. [1] diff --git a/test/blackbox-tests/test-cases/include-qualified/ocamldep-regression.t b/test/blackbox-tests/test-cases/include-qualified/ocamldep-regression.t new file mode 100644 index 00000000000..271272ff603 --- /dev/null +++ b/test/blackbox-tests/test-cases/include-qualified/ocamldep-regression.t @@ -0,0 +1,69 @@ +We should forbid lib interfaces modules from depending on themselves: + + $ cat > dune-project << EOF + > (lang dune 3.7) + > EOF + + $ cat > dune << EOF + > (include_subdirs qualified) + > (library + > (name foo)) + > EOF + + $ cat > foo.ml < let () = Foo.f () + > EOF + + $ touch bar.ml + + $ dune build @check + File "foo.ml", line 1, characters 9-14: + 1 | let () = Foo.f () + ^^^^^ + Error: Unbound module Foo + [1] + +We also forbid submodules from depending on their interface modules: + + $ rm foo.ml bar.ml + $ mkdir baz + $ touch baz/baz.ml + $ cat >baz/bar.ml < let () = Baz.f + > EOF + + $ dune build + Error: Module Bar in directory _build/default depends on Baz. + This doesn't make sense to me. + + Baz is the main module of the library and is the only module exposed outside + of the library. Consequently, it should be the one depending on all the other + modules in the library. + -> required by _build/default/.foo.objs/foo__Baz__Bar.impl.all-deps + -> required by _build/default/.foo.objs/byte/foo__Baz__Bar.cmo + -> required by _build/default/foo.cma + -> required by alias all + -> required by alias default + [1] + +Or their parent interface modules: + + $ rm -rf baz + $ touch baz.ml + $ mkdir -p baz/foo/ + $ cat >baz/foo/z.ml < let () = Baz.f + > EOF + $ dune build + Error: Module Z in directory _build/default depends on Baz. + This doesn't make sense to me. + + Baz is the main module of the library and is the only module exposed outside + of the library. Consequently, it should be the one depending on all the other + modules in the library. + -> required by _build/default/.foo.objs/foo__Baz__Foo__Z.impl.all-deps + -> required by _build/default/.foo.objs/byte/foo__Baz__Foo__Z.cmo + -> required by _build/default/foo.cma + -> required by alias all + -> required by alias default + [1] diff --git a/test/blackbox-tests/test-cases/include-qualified/pp.t/dune-project b/test/blackbox-tests/test-cases/include-qualified/pp.t/dune-project index 1863cf14648..3c48133ad58 100644 --- a/test/blackbox-tests/test-cases/include-qualified/pp.t/dune-project +++ b/test/blackbox-tests/test-cases/include-qualified/pp.t/dune-project @@ -1 +1 @@ -(lang dune 3.5) +(lang dune 3.7) diff --git a/test/blackbox-tests/test-cases/include-qualified/pp.t/run.t b/test/blackbox-tests/test-cases/include-qualified/pp.t/run.t index dc4e4e2d168..2c417822460 100644 --- a/test/blackbox-tests/test-cases/include-qualified/pp.t/run.t +++ b/test/blackbox-tests/test-cases/include-qualified/pp.t/run.t @@ -1,8 +1,10 @@ We can set preprocessing options for nested modules $ dune build @all - File "dune", line 1, characters 17-26: - 1 | (include_subdirs qualified) - ^^^^^^^^^ - Error: Unknown value qualified - Hint: did you mean unqualified? + File "dune", line 8, characters 30-38: + 8 | (run cat %{input-file})) bar/ppme)))) + ^^^^^^^^ + Error: "bar/ppme" is an invalid module name. + Module names must be non-empty and composed only of the following characters: + 'A'..'Z', 'a'..'z', '_', ''' or '0'..'9'. + Hint: barppme would be a correct module name [1] diff --git a/test/blackbox-tests/test-cases/include-qualified/private-modules.t b/test/blackbox-tests/test-cases/include-qualified/private-modules.t new file mode 100644 index 00000000000..4f22d3a697d --- /dev/null +++ b/test/blackbox-tests/test-cases/include-qualified/private-modules.t @@ -0,0 +1,22 @@ +Marking modules as private + + $ cat >dune-project < (lang dune 3.7) + > EOF + + $ cat >dune < (include_subdirs qualified) + > (library + > (name foolib) + > (private_modules baz/foo)) + > EOF + + $ mkdir baz + $ touch baz/foo.ml + + $ dune build + File "dune", line 4, characters 18-25: + 4 | (private_modules baz/foo)) + ^^^^^^^ + Error: Module Baz/foo doesn't exist. + [1] diff --git a/test/blackbox-tests/test-cases/melange/merlin.t b/test/blackbox-tests/test-cases/melange/merlin.t index 292c63e2ed0..8cc8d3853c0 100644 --- a/test/blackbox-tests/test-cases/melange/merlin.t +++ b/test/blackbox-tests/test-cases/melange/merlin.t @@ -21,14 +21,13 @@ $ touch bar.ml $lib.ml $ dune build @check $ dune ocaml merlin dump-config "$PWD" | grep -i "$lib" - Foo $TESTCASE_ROOT/_build/default/.foo.objs/melange) - Foo__ + (FLG (-open Foo__)) + Foo $TESTCASE_ROOT/_build/default/.foo.objs/melange) - Foo__ + (FLG (-open Foo__)) Foo__ $TESTCASE_ROOT/_build/default/.foo.objs/melange) - Foo__ All 3 entries (Foo, Foo__ and Bar) contain a ppx directive diff --git a/test/blackbox-tests/test-cases/merlin/dump-dot-merlin.t/run.t b/test/blackbox-tests/test-cases/merlin/dump-dot-merlin.t/run.t index 3d86102de7f..a9e86970a7b 100644 --- a/test/blackbox-tests/test-cases/merlin/dump-dot-merlin.t/run.t +++ b/test/blackbox-tests/test-cases/merlin/dump-dot-merlin.t/run.t @@ -8,7 +8,7 @@ B $TESTCASE_ROOT/_build/default/src with spaces/.foo.eobjs/byte S $TESTCASE_ROOT/src with spaces # FLG -pp ''\''$TESTCASE_ROOT/_build/default/p p/pp.exe'\''' - # FLG -open Dune__exe -w @1..3@5..28@30..39@43@46..47@49..57@61..62-40 -strict-sequence -strict-formats -short-paths -keep-locs + # FLG -w @1..3@5..28@30..39@43@46..47@49..57@61..62-40 -strict-sequence -strict-formats -short-paths -keep-locs $ dune ocaml dump-dot-merlin "p p" EXCLUDE_QUERY_DIR diff --git a/test/blackbox-tests/test-cases/merlin/merlin-from-subdir.t/dune b/test/blackbox-tests/test-cases/merlin/merlin-from-subdir.t/dune index b719072938a..3f0de254215 100644 --- a/test/blackbox-tests/test-cases/merlin/merlin-from-subdir.t/dune +++ b/test/blackbox-tests/test-cases/merlin/merlin-from-subdir.t/dune @@ -7,4 +7,4 @@ (modules test) (libraries foo)) -(copy_files 411/test.ml) +(copy_files# 411/test.ml) diff --git a/test/blackbox-tests/test-cases/merlin/merlin-tests.t/run.t b/test/blackbox-tests/test-cases/merlin/merlin-tests.t/run.t index e0ebcfc6376..c526857dddf 100644 --- a/test/blackbox-tests/test-cases/merlin/merlin-tests.t/run.t +++ b/test/blackbox-tests/test-cases/merlin/merlin-tests.t/run.t @@ -28,7 +28,7 @@ CRAM sanitization $ dune build ./lib/.merlin-conf/lib-foo ./lib/.merlin-conf/lib-bar --profile release $ dune ocaml merlin dump-config $PWD/lib - File + Bar ((STDLIB /OCAMLC_WHERE) (EXCLUDE_QUERY_DIR) (B @@ -43,8 +43,8 @@ CRAM sanitization --as-ppx --cookie 'library-name="bar"'")) - (FLG (-open Bar -w -40))) - Bar + (FLG (-w -40))) + File ((STDLIB /OCAMLC_WHERE) (EXCLUDE_QUERY_DIR) (B @@ -53,14 +53,15 @@ CRAM sanitization $TESTCASE_ROOT/lib) (S $TESTCASE_ROOT/lib/subdir) + (FLG (-open Bar)) (FLG (-ppx "$TESTCASE_ROOT/_build/default/.ppx/4128e43a9cfb141a37f547484cc9bf46/ppx.exe --as-ppx --cookie 'library-name="bar"'")) - (FLG (-open Bar -w -40))) - Privmod + (FLG (-w -40))) + Foo ((STDLIB /OCAMLC_WHERE) (EXCLUDE_QUERY_DIR) (B lib/findlib) @@ -79,8 +80,8 @@ CRAM sanitization --as-ppx --cookie 'library-name="foo"'")) - (FLG (-open Foo -w -40))) - Foo + (FLG (-w -40))) + Privmod ((STDLIB /OCAMLC_WHERE) (EXCLUDE_QUERY_DIR) (B lib/findlib) @@ -93,13 +94,14 @@ CRAM sanitization $TESTCASE_ROOT/lib) (S $TESTCASE_ROOT/lib/subdir) + (FLG (-open Foo)) (FLG (-ppx "$TESTCASE_ROOT/_build/default/.ppx/4128e43a9cfb141a37f547484cc9bf46/ppx.exe --as-ppx --cookie 'library-name="foo"'")) - (FLG (-open Foo -w -40))) + (FLG (-w -40))) Make sure a ppx directive is generated (if not, the [grep ppx] step fails) $ dune ocaml merlin dump-config $PWD/lib | grep ppx > /dev/null @@ -124,7 +126,7 @@ Make sure pp flag is correct and variables are expanded Check hash of executables names if more than one $ dune build ./exes/.merlin-conf/exe-x-6562915302827c6dce0630390bfa68b7 $ dune ocaml merlin dump-config $PWD/exes - Y + X ((STDLIB /OCAMLC_WHERE) (EXCLUDE_QUERY_DIR) (B @@ -138,7 +140,7 @@ Check hash of executables names if more than one -strict-formats -short-paths -keep-locs))) - X + Y ((STDLIB /OCAMLC_WHERE) (EXCLUDE_QUERY_DIR) (B diff --git a/test/blackbox-tests/test-cases/merlin/per-module-pp.t/run.t b/test/blackbox-tests/test-cases/merlin/per-module-pp.t/run.t index 19156e8bfa0..d1b997d3587 100644 --- a/test/blackbox-tests/test-cases/merlin/per-module-pp.t/run.t +++ b/test/blackbox-tests/test-cases/merlin/per-module-pp.t/run.t @@ -7,16 +7,13 @@ We dump the config for Foo and Bar modules but the pp.exe preprocessor should appear only once since only Foo is using it. $ dune ocaml merlin dump-config $PWD - Foo + Bar ((STDLIB /OCAMLC_WHERE) (EXCLUDE_QUERY_DIR) (B $TESTCASE_ROOT/_build/default/.foo.objs/byte) (S $TESTCASE_ROOT) - (FLG - (-pp - $TESTCASE_ROOT/_build/default/pp/pp.exe)) (FLG (-w @1..3@5..28@30..39@43@46..47@49..57@61..62-40 @@ -24,13 +21,16 @@ should appear only once since only Foo is using it. -strict-formats -short-paths -keep-locs))) - Bar + Foo ((STDLIB /OCAMLC_WHERE) (EXCLUDE_QUERY_DIR) (B $TESTCASE_ROOT/_build/default/.foo.objs/byte) (S $TESTCASE_ROOT) + (FLG + (-pp + $TESTCASE_ROOT/_build/default/pp/pp.exe)) (FLG (-w @1..3@5..28@30..39@43@46..47@49..57@61..62-40 diff --git a/test/blackbox-tests/test-cases/merlin/server.t/dune b/test/blackbox-tests/test-cases/merlin/server.t/dune index 55775d0cd84..94067dcf96b 100644 --- a/test/blackbox-tests/test-cases/merlin/server.t/dune +++ b/test/blackbox-tests/test-cases/merlin/server.t/dune @@ -1,3 +1,5 @@ +(rule (copy# foobar/lib3.foobar.ml lib3.ml)) + (library (name mylib) (modules lib)) diff --git a/test/blackbox-tests/test-cases/merlin/server.t/foobar/lib3.foobar.ml b/test/blackbox-tests/test-cases/merlin/server.t/foobar/lib3.foobar.ml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/blackbox-tests/test-cases/merlin/server.t/lib3.ml b/test/blackbox-tests/test-cases/merlin/server.t/lib3.ml deleted file mode 100644 index bcf417b1934..00000000000 --- a/test/blackbox-tests/test-cases/merlin/server.t/lib3.ml +++ /dev/null @@ -1 +0,0 @@ -let foo ="bar2" diff --git a/test/blackbox-tests/test-cases/merlin/server.t/run.t b/test/blackbox-tests/test-cases/merlin/server.t/run.t index 3a1ae837426..f400876bf0c 100644 --- a/test/blackbox-tests/test-cases/merlin/server.t/run.t +++ b/test/blackbox-tests/test-cases/merlin/server.t/run.t @@ -8,11 +8,11 @@ $ dune build @check $ printf "(4:File%d:%s)" ${#FILE} $FILE | dune ocaml-merlin | sed -E "s/[[:digit:]]+:/\?:/g" - ((?:STDLIB?:/OCAMLC_WHERE)(?:EXCLUDE_QUERY_DIR)(?:B?:$TESTCASE_ROOT/_build/default/.main.eobjs/byte)(?:B?:$TESTCASE_ROOT/_build/default/.mylib.objs/byte)(?:B?:$TESTCASE_ROOT/_build/default/.mylib3.objs/byte)(?:S?:$TESTCASE_ROOT)(?:FLG(?:-open?:Dune__exe?:-w?:@1..3@5..28@30..39@43@46..47@49..57@61..62-?:-strict-sequence?:-strict-formats?:-short-paths?:-keep-locs))) + ((?:STDLIB?:/OCAMLC_WHERE)(?:EXCLUDE_QUERY_DIR)(?:B?:$TESTCASE_ROOT/_build/default/.main.eobjs/byte)(?:B?:$TESTCASE_ROOT/_build/default/.mylib.objs/byte)(?:B?:$TESTCASE_ROOT/_build/default/.mylib3.objs/byte)(?:S?:$TESTCASE_ROOT)(?:FLG(?:-open?:Dune__exe))(?:FLG(?:-w?:@1..3@5..28@30..39@43@46..47@49..57@61..62-?:-strict-sequence?:-strict-formats?:-short-paths?:-keep-locs))) $ FILE=$PWD/lib3.ml $ printf "(4:File%d:%s)" ${#FILE} $FILE | dune ocaml-merlin | sed -E "s/[[:digit:]]+:/\?:/g" - ((?:STDLIB?:/OCAMLC_WHERE)(?:EXCLUDE_QUERY_DIR)(?:B?:$TESTCASE_ROOT/_build/default/.mylib.objs/byte)(?:B?:$TESTCASE_ROOT/_build/default/.mylib3.objs/byte)(?:S?:$TESTCASE_ROOT)(?:FLG(?:-open?:Mylib?:-w?:@1..3@5..28@30..39@43@46..47@49..57@61..62-?:-strict-sequence?:-strict-formats?:-short-paths?:-keep-locs))) + ((?:STDLIB?:/OCAMLC_WHERE)(?:EXCLUDE_QUERY_DIR)(?:B?:$TESTCASE_ROOT/_build/default/.mylib.objs/byte)(?:B?:$TESTCASE_ROOT/_build/default/.mylib3.objs/byte)(?:S?:$TESTCASE_ROOT)(?:FLG(?:-open?:Mylib3))(?:FLG(?:-w?:@1..3@5..28@30..39@43@46..47@49..57@61..62-?:-strict-sequence?:-strict-formats?:-short-paths?:-keep-locs))) If a file has a name of the kind `module_name.xx.xxx.ml/i` we consider it as ``module_name.ml/i` @@ -20,13 +20,13 @@ This can be useful when some build scripts perform custom preprocessing and copy files around. $ FILE=lib3.foobar.ml $ printf "(4:File%d:%s)" ${#FILE} $FILE | dune ocaml-merlin | sed -E "s/[[:digit:]]+:/\?:/g" - ((?:STDLIB?:/OCAMLC_WHERE)(?:EXCLUDE_QUERY_DIR)(?:B?:$TESTCASE_ROOT/_build/default/.mylib.objs/byte)(?:B?:$TESTCASE_ROOT/_build/default/.mylib3.objs/byte)(?:S?:$TESTCASE_ROOT)(?:FLG(?:-open?:Mylib?:-w?:@1..3@5..28@30..39@43@46..47@49..57@61..62-?:-strict-sequence?:-strict-formats?:-short-paths?:-keep-locs))) + ((?:STDLIB?:/OCAMLC_WHERE)(?:EXCLUDE_QUERY_DIR)(?:B?:$TESTCASE_ROOT/_build/default/.mylib.objs/byte)(?:B?:$TESTCASE_ROOT/_build/default/.mylib3.objs/byte)(?:S?:$TESTCASE_ROOT)(?:FLG(?:-open?:Mylib3))(?:FLG(?:-w?:@1..3@5..28@30..39@43@46..47@49..57@61..62-?:-strict-sequence?:-strict-formats?:-short-paths?:-keep-locs))) If a directory has no configuration the configuration of its parent is used This can be useful when some build scripts copy files from subdirectories. - $ FILE=some_sub_dir/lib3.foobar.ml + $ FILE=foobar/lib3.foobar.ml $ printf "(4:File%d:%s)" ${#FILE} $FILE | dune ocaml-merlin | sed -E "s/[[:digit:]]+:/\?:/g" - ((?:STDLIB?:/OCAMLC_WHERE)(?:EXCLUDE_QUERY_DIR)(?:B?:$TESTCASE_ROOT/_build/default/.mylib.objs/byte)(?:B?:$TESTCASE_ROOT/_build/default/.mylib3.objs/byte)(?:S?:$TESTCASE_ROOT)(?:FLG(?:-open?:Mylib?:-w?:@1..3@5..28@30..39@43@46..47@49..57@61..62-?:-strict-sequence?:-strict-formats?:-short-paths?:-keep-locs))) + ((?:STDLIB?:/OCAMLC_WHERE)(?:EXCLUDE_QUERY_DIR)(?:B?:$TESTCASE_ROOT/_build/default/.mylib.objs/byte)(?:B?:$TESTCASE_ROOT/_build/default/.mylib3.objs/byte)(?:S?:$TESTCASE_ROOT)(?:FLG(?:-open?:Mylib3))(?:FLG(?:-w?:@1..3@5..28@30..39@43@46..47@49..57@61..62-?:-strict-sequence?:-strict-formats?:-short-paths?:-keep-locs))) Test of an valid invalid module name $ FILE=not-a-module-name.ml @@ -36,8 +36,8 @@ Test of an valid invalid module name Dune should also provide configuration when the file is in the build folder $ FILE=$PWD/_build/default/lib3.ml $ printf "(4:File%d:%s)" ${#FILE} $FILE | dune ocaml-merlin | sed -E "s/[[:digit:]]+:/\?:/g" - ((?:STDLIB?:/OCAMLC_WHERE)(?:EXCLUDE_QUERY_DIR)(?:B?:$TESTCASE_ROOT/_build/default/.mylib.objs/byte)(?:B?:$TESTCASE_ROOT/_build/default/.mylib3.objs/byte)(?:S?:$TESTCASE_ROOT)(?:FLG(?:-open?:Mylib?:-w?:@1..3@5..28@30..39@43@46..47@49..57@61..62-?:-strict-sequence?:-strict-formats?:-short-paths?:-keep-locs))) + ((?:STDLIB?:/OCAMLC_WHERE)(?:EXCLUDE_QUERY_DIR)(?:B?:$TESTCASE_ROOT/_build/default/.mylib.objs/byte)(?:B?:$TESTCASE_ROOT/_build/default/.mylib3.objs/byte)(?:S?:$TESTCASE_ROOT)(?:FLG(?:-open?:Mylib3))(?:FLG(?:-w?:@1..3@5..28@30..39@43@46..47@49..57@61..62-?:-strict-sequence?:-strict-formats?:-short-paths?:-keep-locs))) $ FILE=_build/default/lib3.ml $ printf "(4:File%d:%s)" ${#FILE} $FILE | dune ocaml-merlin | sed -E "s/[[:digit:]]+:/\?:/g" - ((?:STDLIB?:/OCAMLC_WHERE)(?:EXCLUDE_QUERY_DIR)(?:B?:$TESTCASE_ROOT/_build/default/.mylib.objs/byte)(?:B?:$TESTCASE_ROOT/_build/default/.mylib3.objs/byte)(?:S?:$TESTCASE_ROOT)(?:FLG(?:-open?:Mylib?:-w?:@1..3@5..28@30..39@43@46..47@49..57@61..62-?:-strict-sequence?:-strict-formats?:-short-paths?:-keep-locs))) + ((?:STDLIB?:/OCAMLC_WHERE)(?:EXCLUDE_QUERY_DIR)(?:B?:$TESTCASE_ROOT/_build/default/.mylib.objs/byte)(?:B?:$TESTCASE_ROOT/_build/default/.mylib3.objs/byte)(?:S?:$TESTCASE_ROOT)(?:FLG(?:-open?:Mylib3))(?:FLG(?:-w?:@1..3@5..28@30..39@43@46..47@49..57@61..62-?:-strict-sequence?:-strict-formats?:-short-paths?:-keep-locs))) diff --git a/test/blackbox-tests/test-cases/merlin/src-dirs-of-deps.t b/test/blackbox-tests/test-cases/merlin/src-dirs-of-deps.t index 4f26b1c4081..48595727cfd 100644 --- a/test/blackbox-tests/test-cases/merlin/src-dirs-of-deps.t +++ b/test/blackbox-tests/test-cases/merlin/src-dirs-of-deps.t @@ -11,7 +11,10 @@ library also has more than one src dir. $ touch lib1/bar.ml $ mkdir lib2 $ cat >lib2/dune < (library (name lib2) (libraries lib1) (modules ())) + > (library + > (name lib2) + > (libraries lib1) + > (modules ())) > EOF $ opam_prefix="$(ocamlc -where)" @@ -33,9 +36,7 @@ library also has more than one src dir. (S $TESTCASE_ROOT/lib2) (FLG - (-open - Lib2 - -w + (-w @1..3@5..28@30..39@43@46..47@49..57@61..62-40 -strict-sequence -strict-formats diff --git a/test/blackbox-tests/test-cases/merlin/unit-names-merlin-gh1233.t/run.t b/test/blackbox-tests/test-cases/merlin/unit-names-merlin-gh1233.t/run.t index 9701b21ed39..9f60c40b67e 100644 --- a/test/blackbox-tests/test-cases/merlin/unit-names-merlin-gh1233.t/run.t +++ b/test/blackbox-tests/test-cases/merlin/unit-names-merlin-gh1233.t/run.t @@ -32,10 +32,9 @@ $TESTCASE_ROOT/_build/default/foo/.foo.objs/byte) (S $TESTCASE_ROOT/foo) + (FLG (-open Foo)) (FLG - (-open - Foo - -w + (-w @1..3@5..28@30..39@43@46..47@49..57@61..62-40 -strict-sequence -strict-formats @@ -49,9 +48,7 @@ (S $TESTCASE_ROOT/foo) (FLG - (-open - Foo - -w + (-w @1..3@5..28@30..39@43@46..47@49..57@61..62-40 -strict-sequence -strict-formats diff --git a/test/blackbox-tests/test-cases/re-exported-deps/transitive.t/run.t b/test/blackbox-tests/test-cases/re-exported-deps/transitive.t/run.t index 4c9580f072f..c0b5fc7228c 100644 --- a/test/blackbox-tests/test-cases/re-exported-deps/transitive.t/run.t +++ b/test/blackbox-tests/test-cases/re-exported-deps/transitive.t/run.t @@ -43,7 +43,8 @@ transitive deps expressed in the dune-package (requires pkg.ccc (re_export pkg.bbb)) (main_module_name Aaa) (modes byte native) - (modules (singleton (name Aaa) (obj_name aaa) (visibility public) (impl)))) + (modules + (singleton (name Aaa) (obj_name aaa) (path Aaa) (visibility public) (impl)))) (library (name pkg.bbb) (kind normal) @@ -53,7 +54,8 @@ transitive deps expressed in the dune-package (requires (re_export pkg.ccc)) (main_module_name Bbb) (modes byte native) - (modules (singleton (name Bbb) (obj_name bbb) (visibility public) (impl)))) + (modules + (singleton (name Bbb) (obj_name bbb) (path Bbb) (visibility public) (impl)))) (library (name pkg.ccc) (kind normal) @@ -62,4 +64,5 @@ transitive deps expressed in the dune-package (native_archives ccc/ccc$ext_lib) (main_module_name Ccc) (modes byte native) - (modules (singleton (name Ccc) (obj_name ccc) (visibility public) (impl)))) + (modules + (singleton (name Ccc) (obj_name ccc) (path Ccc) (visibility public) (impl)))) diff --git a/test/blackbox-tests/test-cases/virtual-libraries/dune-package-info.t/run.t b/test/blackbox-tests/test-cases/virtual-libraries/dune-package-info.t/run.t index 8db3c8fe018..aad98d6c0f3 100644 --- a/test/blackbox-tests/test-cases/virtual-libraries/dune-package-info.t/run.t +++ b/test/blackbox-tests/test-cases/virtual-libraries/dune-package-info.t/run.t @@ -43,19 +43,23 @@ Include variants and implementation information in dune-package (modes byte native) (modules (wrapped - (main_module_name Vlib) - (modules - ((name Vmod) - (obj_name vlib__Vmod) + (group + (alias + (name Vlib__impl__) + (obj_name vlib__impl__) + (path Vlib__impl__) (visibility public) - (kind impl_vmodule) - (impl))) - (alias_module - (name Vlib__impl__) - (obj_name vlib__impl__) - (visibility public) - (kind alias) - (impl)) + (kind alias) + (impl)) + (name Vlib) + (modules + (module + (name Vmod) + (obj_name vlib__Vmod) + (path Vmod) + (visibility public) + (kind impl_vmodule) + (impl)))) (wrapped true)))) (library (name foo.vlib) @@ -65,17 +69,21 @@ Include variants and implementation information in dune-package (modes byte native) (modules (wrapped - (main_module_name Vlib) - (modules - ((name Vmod) - (obj_name vlib__Vmod) + (group + (alias + (name Vlib) + (obj_name vlib) + (path Vlib) (visibility public) - (kind virtual) - (intf))) - (alias_module + (kind alias) + (impl)) (name Vlib) - (obj_name vlib) - (visibility public) - (kind alias) - (impl)) + (modules + (module + (name Vmod) + (obj_name vlib__Vmod) + (path Vmod) + (visibility public) + (kind virtual) + (intf)))) (wrapped true)))) diff --git a/test/blackbox-tests/test-cases/virtual-libraries/impl-private-modules.t/run.t b/test/blackbox-tests/test-cases/virtual-libraries/impl-private-modules.t/run.t index 861ca1cf1ac..b295e2f6d6d 100644 --- a/test/blackbox-tests/test-cases/virtual-libraries/impl-private-modules.t/run.t +++ b/test/blackbox-tests/test-cases/virtual-libraries/impl-private-modules.t/run.t @@ -14,7 +14,7 @@ that they never collide with modules present in the virtual library. module Bar = Foo__Bar (** @canonical Foo.Priv *) - module Priv = Foo__foo_impl____Priv + module Priv = Foo__foo_impl__Priv Here we look at the raw artifacts for our implementation and verify it matches the alias: @@ -22,4 +22,4 @@ the alias: $ ls _build/default/impl/.foo_impl.objs/byte/*.cmi _build/default/impl/.foo_impl.objs/byte/foo__Bar.cmi _build/default/impl/.foo_impl.objs/byte/foo__foo_impl__.cmi - _build/default/impl/.foo_impl.objs/byte/foo__foo_impl____Priv.cmi + _build/default/impl/.foo_impl.objs/byte/foo__foo_impl__Priv.cmi diff --git a/test/blackbox-tests/test-cases/virtual-libraries/vlib-default-impl/dune-package.t/run.t b/test/blackbox-tests/test-cases/virtual-libraries/vlib-default-impl/dune-package.t/run.t index 1a59c7e6f7b..6b2ad9e139f 100644 --- a/test/blackbox-tests/test-cases/virtual-libraries/vlib-default-impl/dune-package.t/run.t +++ b/test/blackbox-tests/test-cases/virtual-libraries/vlib-default-impl/dune-package.t/run.t @@ -42,15 +42,23 @@ Check that default implementation data is installed in the dune package file. (modes byte native) (modules (wrapped - (main_module_name A) - (modules - ((name X) (obj_name a__X) (visibility public) (kind virtual) (intf))) - (alias_module + (group + (alias + (name A) + (obj_name a) + (path A) + (visibility public) + (kind alias) + (impl)) (name A) - (obj_name a) - (visibility public) - (kind alias) - (impl)) + (modules + (module + (name X) + (obj_name a__X) + (path X) + (visibility public) + (kind virtual) + (intf)))) (wrapped true)))) (library (name a.default-impl) @@ -68,13 +76,21 @@ Check that default implementation data is installed in the dune package file. (modes byte native) (modules (wrapped - (main_module_name A) - (modules - ((name X) (obj_name a__X) (visibility public) (kind impl_vmodule) (impl))) - (alias_module - (name A__a_default__) - (obj_name a__a_default__) - (visibility public) - (kind alias) - (impl)) + (group + (alias + (name A__a_default__) + (obj_name a__a_default__) + (path A__a_default__) + (visibility public) + (kind alias) + (impl)) + (name A) + (modules + (module + (name X) + (obj_name a__X) + (path X) + (visibility public) + (kind impl_vmodule) + (impl)))) (wrapped true))))