Skip to content

Commit 8d802cc

Browse files
authored
pkg: utop dev tool (ocaml#12027)
This adds utop as a dev tool. Unlike most dev tools there is no "dune tools exec utop" command, but rather the existing "dune utop" command has been modified such that if a lockdir exists for the utop dev tool, then dune will install the utop library (downloading it if necessary) within the _build directory and use that library to create the custom utop toplevel for the project. It's possible to install utop as a dev tool without running it by running the "dune tools install utop" command. Signed-off-by: Stephen Sherratt <[email protected]>
1 parent 37d6024 commit 8d802cc

File tree

18 files changed

+203
-45
lines changed

18 files changed

+203
-45
lines changed

bin/lock_dev_tool.ml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -247,12 +247,8 @@ let lock_ocamlformat () =
247247
lock_dev_tool_at_version Ocamlformat version
248248
;;
249249

250-
let lock_odoc () = lock_dev_tool_at_version Odoc None
251-
let lock_ocamllsp () = lock_dev_tool_at_version Ocamllsp None
252-
253250
let lock_dev_tool dev_tool =
254251
match (dev_tool : Dune_pkg.Dev_tool.t) with
255252
| Ocamlformat -> lock_ocamlformat ()
256-
| Odoc -> lock_odoc ()
257-
| Ocamllsp -> lock_ocamllsp ()
253+
| other -> lock_dev_tool_at_version other None
258254
;;

bin/ocaml/utop.ml

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ let man =
1212

1313
let info = Cmd.info "utop" ~doc ~man
1414

15+
let lock_utop_if_dev_tool_enabled () =
16+
match Lazy.force Lock_dev_tool.is_enabled with
17+
| false -> Memo.return ()
18+
| true -> Lock_dev_tool.lock_dev_tool Utop
19+
;;
20+
1521
let term =
1622
let+ builder = Common.Builder.term
1723
and+ dir = Arg.(value & pos 0 string "" & Arg.info [] ~docv:"DIR")
@@ -21,25 +27,41 @@ let term =
2127
let dir = Common.prefix_target common dir in
2228
if not (Path.is_directory (Path.of_string dir))
2329
then User_error.raise [ Pp.textf "cannot find directory: %s" (String.maybe_quoted dir) ];
24-
let env, lib_config, utop_path, requires =
30+
let env, utop_path =
2531
Scheduler.go_with_rpc_server ~common ~config (fun () ->
2632
let open Fiber.O in
2733
let* setup = Import.Main.setup () in
2834
build_exn (fun () ->
2935
let open Memo.O in
3036
let* setup = setup in
3137
let context = Import.Main.find_context_exn setup ~name:ctx_name in
32-
let utop_target =
33-
let utop_target = Filename.concat dir Utop.utop_exe in
34-
Path.build (Path.Build.relative (Context.build_dir context) utop_target)
38+
let utop_target_path filename =
39+
Path.build
40+
(Path.Build.relative
41+
(Context.build_dir context)
42+
(Filename.concat dir filename))
3543
in
36-
Build_system.file_exists utop_target
44+
let utop_exe = utop_target_path Utop.utop_exe in
45+
let utop_findlib_conf = utop_target_path Utop.utop_findlib_conf in
46+
Build_system.file_exists utop_exe
3747
>>= function
3848
| false ->
3949
User_error.raise
4050
[ Pp.textf "no library is defined in %s" (String.maybe_quoted dir) ]
4151
| true ->
42-
let* () = Build_system.build_file utop_target in
52+
let* () = Build_system.build_file utop_exe in
53+
let* () = lock_utop_if_dev_tool_enabled () in
54+
let* utop_dev_tool_lock_dir_exists =
55+
Memo.Lazy.force Utop.utop_dev_tool_lock_dir_exists
56+
in
57+
let* () =
58+
if utop_dev_tool_lock_dir_exists
59+
then
60+
(* Generate the custom findlib.conf file needed when utop is run
61+
as a dev tool. *)
62+
Build_system.build_file utop_findlib_conf
63+
else Memo.return ()
64+
in
4365
let sctx = Import.Main.find_scontext_exn setup ~name:ctx_name in
4466
let* requires =
4567
let dir = Path.Build.relative (Context.build_dir context) dir in
@@ -50,15 +72,29 @@ let term =
5072
let+ ocaml = Context.ocaml context in
5173
ocaml.lib_config
5274
and+ env = Super_context.context_env sctx in
53-
env, lib_config, Path.to_string utop_target, requires))
75+
let env =
76+
Dune_rules.Lib_flags.L.toplevel_ld_paths requires lib_config
77+
|> Path.Set.fold
78+
~f:(fun dir env ->
79+
Env_path.cons ~var:Ocaml.Env.caml_ld_library_path env ~dir)
80+
~init:env
81+
in
82+
let env =
83+
if utop_dev_tool_lock_dir_exists
84+
then
85+
(* If there's a utop lockdir then dune will have built utop as a
86+
dev tool. In order for it to run correctly dune needed to
87+
generate a custom findlib.conf that contains the locations of
88+
all of utop's dependencies within the project's _build
89+
directory. Setting this environment variable causes the custom
90+
findlib.conf file to be used instead of the default
91+
findlib.conf. *)
92+
Env.add env ~var:"OCAMLFIND_CONF" ~value:(Path.to_string utop_findlib_conf)
93+
else env
94+
in
95+
env, Path.to_string utop_exe))
5496
in
5597
Hooks.End_of_build.run ();
56-
let env =
57-
Dune_rules.Lib_flags.L.toplevel_ld_paths requires lib_config
58-
|> Path.Set.fold
59-
~f:(fun dir env -> Env_path.cons ~var:Ocaml.Env.caml_ld_library_path env ~dir)
60-
~init:env
61-
in
6298
restore_cwd_and_execve (Common.root common) utop_path args env
6399
;;
64100

bin/tools/tools.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ module Install = struct
1111
let info = Cmd.info ~doc "install"
1212

1313
let group =
14-
Cmd.group info (List.map [ Ocamlformat; Ocamllsp ] ~f:Tools_common.install_command)
14+
Cmd.group info (List.map Dune_pkg.Dev_tool.all ~f:Tools_common.install_command)
1515
;;
1616
end
1717

@@ -20,7 +20,7 @@ module Which = struct
2020
let info = Cmd.info ~doc "which"
2121

2222
let group =
23-
Cmd.group info (List.map [ Ocamlformat; Ocamllsp ] ~f:Tools_common.which_command)
23+
Cmd.group info (List.map Dune_pkg.Dev_tool.all ~f:Tools_common.which_command)
2424
;;
2525
end
2626

src/dune_pkg/dev_tool.ml

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,60 @@ type t =
44
| Ocamlformat
55
| Odoc
66
| Ocamllsp
7+
| Utop
78

8-
let all = [ Ocamlformat; Odoc; Ocamllsp ]
9+
let to_dyn = function
10+
| Ocamlformat -> Dyn.variant "Ocamlformat" []
11+
| Odoc -> Dyn.variant "Odoc" []
12+
| Ocamllsp -> Dyn.variant "Ocamllsp" []
13+
| Utop -> Dyn.variant "Utop" []
14+
;;
15+
16+
let all = [ Ocamlformat; Odoc; Ocamllsp; Utop ]
917

1018
let equal a b =
1119
match a, b with
1220
| Ocamlformat, Ocamlformat -> true
1321
| Odoc, Odoc -> true
1422
| Ocamllsp, Ocamllsp -> true
15-
| _ -> false
23+
| Utop, Utop -> true
24+
| (Ocamlformat | Odoc | Ocamllsp | Utop), _ -> false
1625
;;
1726

1827
let package_name = function
1928
| Ocamlformat -> Package_name.of_string "ocamlformat"
2029
| Odoc -> Package_name.of_string "odoc"
2130
| Ocamllsp -> Package_name.of_string "ocaml-lsp-server"
31+
| Utop -> Package_name.of_string "utop"
2232
;;
2333

2434
let of_package_name package_name =
2535
match Package_name.to_string package_name with
2636
| "ocamlformat" -> Ocamlformat
2737
| "odoc" -> Odoc
2838
| "ocaml-lsp-server" -> Ocamllsp
39+
| "utop" -> Utop
2940
| other -> User_error.raise [ Pp.textf "No such dev tool: %s" other ]
3041
;;
3142

3243
let exe_name = function
3344
| Ocamlformat -> "ocamlformat"
3445
| Odoc -> "odoc"
3546
| Ocamllsp -> "ocamllsp"
47+
| Utop -> "utop"
3648
;;
3749

3850
let exe_path_components_within_package t =
3951
match t with
4052
| Ocamlformat -> [ "bin"; exe_name t ]
4153
| Odoc -> [ "bin"; exe_name t ]
4254
| Ocamllsp -> [ "bin"; exe_name t ]
55+
| Utop -> [ "bin"; exe_name t ]
4356
;;
4457

4558
let needs_to_build_with_same_compiler_as_project = function
4659
| Ocamlformat -> false
4760
| Odoc -> true
4861
| Ocamllsp -> true
62+
| Utop -> false
4963
;;

src/dune_pkg/dev_tool.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ type t =
44
| Ocamlformat
55
| Odoc
66
| Ocamllsp
7+
| Utop
78

9+
val to_dyn : t -> Dyn.t
810
val all : t list
911
val equal : t -> t -> bool
1012
val package_name : t -> Package_name.t

src/dune_pkg/lock_dir.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module Pkg_info : sig
1212
; extra_sources : (Path.Local.t * Source.t) list
1313
}
1414

15+
val to_dyn : t -> Dyn.t
1516
val default_version : Package_version.t
1617
val variables : t -> OpamVariable.variable_contents Package_variable_name.Map.t
1718
end

src/dune_rules/context.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ let create (builder : Builder.t) ~(kind : Kind.t) =
438438
Pp.textf "loading OCAMLPATH for context %S" (Context_name.to_string builder.name))
439439
(fun () ->
440440
match kind with
441-
| Lock _ -> Pkg_rules.ocamlpath builder.name
441+
| Lock _ -> Pkg_rules.project_ocamlpath builder.name
442442
| Default | Opam _ ->
443443
let+ ocamlpath = builder.env >>| Findlib_config.ocamlpath_of_env in
444444
Kind.ocamlpath kind ~ocamlpath ~findlib_toolchain:builder.findlib_toolchain)

src/dune_rules/findlib.ml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -569,12 +569,11 @@ end
569569

570570
type t = DB.t
571571

572-
let create =
572+
let create_with_paths ~paths =
573573
Per_context.create_by_name ~name:"findlib" (fun context ->
574574
Memo.lazy_ (fun () ->
575575
let* context = Context.DB.get context in
576-
let* paths = Context.findlib_paths context
577-
and* lib_config =
576+
let* lib_config =
578577
let+ ocaml = Context.ocaml context in
579578
ocaml.lib_config
580579
in
@@ -583,4 +582,10 @@ let create =
583582
|> Staged.unstage
584583
;;
585584

585+
let create context_name =
586+
let* context = Context.DB.get context_name in
587+
let* paths = Context.findlib_paths context in
588+
create_with_paths context_name ~paths
589+
;;
590+
586591
include Public

src/dune_rules/findlib.mli

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ val all_broken_packages : t -> (Package.Name.t * User_message.t) list Memo.t
3232

3333
val create : Context_name.t -> t Memo.t
3434

35+
(** Create a findlib database with a given list of paths corresponding to the
36+
"path" field in the findlib.conf file. *)
37+
val create_with_paths : paths:Path.t list -> Context_name.t -> t Memo.t
38+
3539
module For_tests : sig
3640
val create : paths:Path.t list -> lib_config:Lib_config.t -> t Memo.t
3741
end

src/dune_rules/lib.ml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1954,6 +1954,18 @@ module DB = struct
19541954
Findlib.all_packages findlib >>| List.map ~f:Dune_package.Entry.name)
19551955
;;
19561956

1957+
let with_parent t ~parent = { t with parent }
1958+
1959+
let of_paths context ~paths =
1960+
let open Memo.O in
1961+
let+ ocaml = Context.ocaml context
1962+
and+ findlib = Findlib.create_with_paths (Context.name context) ~paths in
1963+
create_from_findlib
1964+
findlib
1965+
~has_bigarray_library:(Ocaml.Version.has_bigarray_library ocaml.version)
1966+
~instrument_with:(Context.instrument_with context)
1967+
;;
1968+
19571969
let installed (context : Context.t) =
19581970
let open Memo.O in
19591971
let+ ocaml = Context.ocaml context

0 commit comments

Comments
 (0)