249 lines
9.2 KiB
OCaml
249 lines
9.2 KiB
OCaml
|
|
open Import
|
||
|
|
|
||
|
|
let doc =
|
||
|
|
"Print a list of toplevel directives for including directories and loading cma files."
|
||
|
|
;;
|
||
|
|
|
||
|
|
let man =
|
||
|
|
[ `S "DESCRIPTION"
|
||
|
|
; `P
|
||
|
|
{|Print a list of toplevel directives for including directories and loading cma files.|}
|
||
|
|
; `P
|
||
|
|
{|The output of $(b,dune top) should be evaluated in a toplevel
|
||
|
|
to make a library available there.|}
|
||
|
|
; `Blocks Common.help_secs
|
||
|
|
]
|
||
|
|
;;
|
||
|
|
|
||
|
|
let info = Cmd.info "top" ~doc ~man
|
||
|
|
|
||
|
|
let link_deps sctx link =
|
||
|
|
let open Memo.O in
|
||
|
|
let* lib_config =
|
||
|
|
let+ ocaml = Super_context.context sctx |> Context.ocaml in
|
||
|
|
ocaml.lib_config
|
||
|
|
in
|
||
|
|
Memo.parallel_map link ~f:(fun t ->
|
||
|
|
Dune_rules.Lib_flags.link_deps sctx t Dune_rules.Link_mode.Byte lib_config)
|
||
|
|
>>| List.concat
|
||
|
|
;;
|
||
|
|
|
||
|
|
let files_to_load_of_requires sctx requires =
|
||
|
|
let open Memo.O in
|
||
|
|
let* files = link_deps sctx requires in
|
||
|
|
let+ () = Memo.parallel_iter files ~f:Build_system.build_file in
|
||
|
|
List.filter files ~f:(fun p ->
|
||
|
|
let ext = Path.extension p in
|
||
|
|
ext = Ocaml.Mode.compiled_lib_ext Byte || ext = Ocaml.Cm_kind.ext Cmo)
|
||
|
|
;;
|
||
|
|
|
||
|
|
let term =
|
||
|
|
let+ builder = Common.Builder.term
|
||
|
|
and+ dir = Arg.(value & pos 0 string "" & Arg.info [] ~docv:"DIR")
|
||
|
|
and+ ctx_name = Common.context_arg ~doc:{|Select context where to build/run utop.|} in
|
||
|
|
let common, config = Common.init builder in
|
||
|
|
Scheduler.go_with_rpc_server ~common ~config (fun () ->
|
||
|
|
let open Fiber.O in
|
||
|
|
let* setup = Import.Main.setup () in
|
||
|
|
build_exn (fun () ->
|
||
|
|
let open Memo.O in
|
||
|
|
let* setup = setup in
|
||
|
|
let sctx =
|
||
|
|
Dune_engine.Context_name.Map.find setup.scontexts ctx_name |> Option.value_exn
|
||
|
|
in
|
||
|
|
let context = Super_context.context sctx in
|
||
|
|
let* libs =
|
||
|
|
let dir =
|
||
|
|
let build_dir = Context.build_dir context in
|
||
|
|
Path.Build.relative build_dir (Common.prefix_target common dir)
|
||
|
|
in
|
||
|
|
let* db =
|
||
|
|
let+ scope = Dune_rules.Scope.DB.find_by_dir dir in
|
||
|
|
Dune_rules.Scope.libs scope
|
||
|
|
in
|
||
|
|
(* TODO why don't we read ppx as well?*)
|
||
|
|
Dune_rules.Utop.libs_under_dir sctx ~db ~dir:(Path.build dir)
|
||
|
|
in
|
||
|
|
let* requires =
|
||
|
|
Dune_rules.Resolve.Memo.read_memo (Dune_rules.Lib.closure ~linking:true libs)
|
||
|
|
in
|
||
|
|
let* lib_config =
|
||
|
|
let+ ocaml = Context.ocaml context in
|
||
|
|
ocaml.lib_config
|
||
|
|
in
|
||
|
|
let include_paths =
|
||
|
|
Dune_rules.Lib_flags.L.toplevel_include_paths requires lib_config
|
||
|
|
in
|
||
|
|
let+ files_to_load = files_to_load_of_requires sctx requires in
|
||
|
|
Dune_rules.Toplevel.print_toplevel_init_file
|
||
|
|
{ include_paths; files_to_load; uses = []; pp = None; ppx = None; code = [] }))
|
||
|
|
;;
|
||
|
|
|
||
|
|
let command = Cmd.v info term
|
||
|
|
|
||
|
|
module Module = struct
|
||
|
|
let doc = "Print a list of toplevel directives for loading a module into the topevel."
|
||
|
|
|
||
|
|
let man =
|
||
|
|
[ `S "DESCRIPTION"
|
||
|
|
; `P doc
|
||
|
|
; `P
|
||
|
|
"The module's source is evaluated in the toplevel without being sealed by the \
|
||
|
|
mli."
|
||
|
|
; `P
|
||
|
|
{|The output of $(b,dune top) should be evaluated in a toplevel
|
||
|
|
to make the module available there.|}
|
||
|
|
; `Blocks Common.help_secs
|
||
|
|
]
|
||
|
|
;;
|
||
|
|
|
||
|
|
let info = Cmd.info "top-module" ~doc ~man
|
||
|
|
|
||
|
|
let module_directives sctx mod_ =
|
||
|
|
let ctx = Super_context.context sctx in
|
||
|
|
let src = Path.Build.append_source (Context.build_dir ctx) mod_ in
|
||
|
|
let dir = Path.Build.parent_exn src in
|
||
|
|
let filename = Path.Build.basename src in
|
||
|
|
if Filename.extension filename = ""
|
||
|
|
then User_error.raise [ Pp.text "file is missing an extension" ];
|
||
|
|
let open Memo.O in
|
||
|
|
let module_name =
|
||
|
|
let name = Filename.remove_extension filename in
|
||
|
|
Dune_rules.Module_name.of_string_user_error (Loc.none, name) |> User_error.ok_exn
|
||
|
|
in
|
||
|
|
let* expander = Super_context.expander sctx ~dir in
|
||
|
|
let* top_module_info = Dune_rules.Top_module.find_module sctx mod_ in
|
||
|
|
match top_module_info with
|
||
|
|
| None -> User_error.raise [ Pp.text "no module found" ]
|
||
|
|
| Some (_, _, _, Melange _) ->
|
||
|
|
User_error.raise [ Pp.text "Modules belonging to `melange.emit' are not supported" ]
|
||
|
|
| Some (module_, cctx, merlin, _) ->
|
||
|
|
let module Compilation_context = Dune_rules.Compilation_context in
|
||
|
|
let module Obj_dir = Dune_rules.Obj_dir in
|
||
|
|
let module Top_module = Dune_rules.Top_module in
|
||
|
|
let* requires =
|
||
|
|
let* requires = Compilation_context.requires_link cctx in
|
||
|
|
Dune_rules.Resolve.read_memo requires
|
||
|
|
in
|
||
|
|
let private_obj_dir = Top_module.private_obj_dir ctx mod_ in
|
||
|
|
let include_paths =
|
||
|
|
let libs =
|
||
|
|
let lib_config = (Compilation_context.ocaml cctx).lib_config in
|
||
|
|
Dune_rules.Lib_flags.L.toplevel_include_paths requires lib_config
|
||
|
|
in
|
||
|
|
Path.Set.add libs (Path.build (Obj_dir.byte_dir private_obj_dir))
|
||
|
|
in
|
||
|
|
let files_to_load () =
|
||
|
|
let+ libs, modules =
|
||
|
|
Memo.fork_and_join
|
||
|
|
(fun () -> files_to_load_of_requires sctx requires)
|
||
|
|
(fun () ->
|
||
|
|
let cmis () =
|
||
|
|
let glob =
|
||
|
|
Dune_engine.File_selector.of_glob
|
||
|
|
~dir:(Path.build (Obj_dir.byte_dir private_obj_dir))
|
||
|
|
(Dune_lang.Glob.of_string_exn Loc.none "*.cmi")
|
||
|
|
in
|
||
|
|
let* files = Build_system.eval_pred glob in
|
||
|
|
Memo.parallel_iter
|
||
|
|
(Filename_set.to_list files)
|
||
|
|
~f:Build_system.build_file
|
||
|
|
in
|
||
|
|
let cmos () =
|
||
|
|
let obj_dir = Compilation_context.obj_dir cctx in
|
||
|
|
let dep_graph = (Compilation_context.dep_graphs cctx).impl in
|
||
|
|
let* modules =
|
||
|
|
let graph =
|
||
|
|
Dune_rules.Dep_graph.top_closed_implementations dep_graph [ module_ ]
|
||
|
|
in
|
||
|
|
let+ modules, _ = Action_builder.evaluate_and_collect_facts graph in
|
||
|
|
modules
|
||
|
|
in
|
||
|
|
let cmos =
|
||
|
|
let module Module = Dune_rules.Module in
|
||
|
|
let module Module_name = Dune_rules.Module_name in
|
||
|
|
let module_obj_name = Module.obj_name module_ in
|
||
|
|
List.filter_map modules ~f:(fun m ->
|
||
|
|
let obj_dir =
|
||
|
|
if Module_name.Unique.equal module_obj_name (Module.obj_name m)
|
||
|
|
then private_obj_dir
|
||
|
|
else obj_dir
|
||
|
|
in
|
||
|
|
Obj_dir.Module.cm_file obj_dir m ~kind:(Ocaml Cmo)
|
||
|
|
|> Option.map ~f:Path.build)
|
||
|
|
in
|
||
|
|
let+ (_ : Dep.Facts.t) =
|
||
|
|
Build_system.build_deps (Dep.Set.of_files cmos)
|
||
|
|
in
|
||
|
|
cmos
|
||
|
|
in
|
||
|
|
Memo.fork_and_join_unit cmis cmos)
|
||
|
|
in
|
||
|
|
libs @ modules
|
||
|
|
in
|
||
|
|
let pps () =
|
||
|
|
let module Merlin = Dune_rules.Merlin in
|
||
|
|
let pps = Merlin.pp_config merlin ctx ~expander in
|
||
|
|
let+ pps, _ = Action_builder.evaluate_and_collect_facts pps in
|
||
|
|
let pp = Dune_rules.Module_name.Per_item.get pps module_name in
|
||
|
|
match pp with
|
||
|
|
| None -> None, None
|
||
|
|
| Some pp_flags ->
|
||
|
|
let args = Merlin.Processed.pp_args pp_flags in
|
||
|
|
(match Merlin.Processed.pp_kind pp_flags with
|
||
|
|
| Pp -> Some args, None
|
||
|
|
| Ppx -> None, Some args)
|
||
|
|
in
|
||
|
|
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.Modules.With_vlib.local_open modules module_ in
|
||
|
|
List.map opens_ ~f:(fun name ->
|
||
|
|
sprintf "open %s" (Dune_rules.Module_name.to_string name))
|
||
|
|
in
|
||
|
|
{ Dune_rules.Toplevel.files_to_load; pp; ppx; include_paths; uses = []; code }
|
||
|
|
;;
|
||
|
|
|
||
|
|
let term =
|
||
|
|
let+ builder = Common.Builder.term
|
||
|
|
and+ module_path =
|
||
|
|
Arg.(
|
||
|
|
required
|
||
|
|
& pos 0 (some string) None
|
||
|
|
& Arg.info [] ~docv:"MODULE" ~doc:"Path to an OCaml module.")
|
||
|
|
and+ ctx_name = Common.context_arg ~doc:{|Select context where to build/run utop.|} in
|
||
|
|
let common, config = Common.init builder in
|
||
|
|
Scheduler.go_with_rpc_server ~common ~config (fun () ->
|
||
|
|
let open Fiber.O in
|
||
|
|
let* setup = Import.Main.setup () in
|
||
|
|
build_exn (fun () ->
|
||
|
|
let open Memo.O in
|
||
|
|
let* setup = setup in
|
||
|
|
let sctx =
|
||
|
|
Dune_engine.Context_name.Map.find setup.scontexts ctx_name |> Option.value_exn
|
||
|
|
in
|
||
|
|
let+ directives =
|
||
|
|
let module_path =
|
||
|
|
if Filename.is_relative module_path
|
||
|
|
then Path.Local.of_string module_path
|
||
|
|
else (
|
||
|
|
let root =
|
||
|
|
(Common.root common).dir
|
||
|
|
|> Path.of_string
|
||
|
|
|> Path.to_absolute_filename
|
||
|
|
|> Path.of_string
|
||
|
|
in
|
||
|
|
match Path.drop_prefix ~prefix:root (Path.of_string module_path) with
|
||
|
|
| Some module_path -> module_path
|
||
|
|
| None ->
|
||
|
|
User_error.raise
|
||
|
|
[ Pp.text "Module path not a descendent of workspace root." ])
|
||
|
|
in
|
||
|
|
module_directives sctx (Path.Source.of_local module_path)
|
||
|
|
in
|
||
|
|
Dune_rules.Toplevel.print_toplevel_init_file directives))
|
||
|
|
;;
|
||
|
|
end
|
||
|
|
|
||
|
|
let module_command = Cmd.v Module.info Module.term
|