Skip to content

Commit f3c70f0

Browse files
authored
Deduplicate opam parse errors (#12670)
Generating portable lockdirs requires running the solver several times, possibly in parallel. Errors parsing opam files would be printed multiple times. This change threads result types through some of the solver logic so errors can be collected and deduplicated before printing. Signed-off-by: Stephen Sherratt <[email protected]>
1 parent 2515784 commit f3c70f0

File tree

11 files changed

+363
-175
lines changed

11 files changed

+363
-175
lines changed

bin/pkg/lock.ml

Lines changed: 57 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,28 @@ let resolve_project_pins project_pins =
8585
;;
8686

8787
module Platforms_by_message = struct
88-
module Message_map = Map.Make (struct
89-
type t = User_message.Style.t Pp.t
88+
module Message = struct
89+
type t =
90+
| Solve_error of User_message.Style.t Pp.t
91+
| Manifest_error of User_message.t
92+
93+
let to_dyn = function
94+
| Solve_error message ->
95+
Dyn.variant "Solve_error" [ Pp.to_dyn User_message.Style.to_dyn message ]
96+
| Manifest_error message ->
97+
Dyn.variant "Manifest_error" [ User_message.to_dyn message ]
98+
;;
9099

91-
let to_dyn = Pp.to_dyn User_message.Style.to_dyn
92-
let compare = Pp.compare ~compare:User_message.Style.compare
93-
end)
100+
let compare a b =
101+
match a, b with
102+
| Solve_error a, Solve_error b -> Pp.compare ~compare:User_message.Style.compare a b
103+
| Solve_error _, _ -> Lt
104+
| _, Solve_error _ -> Gt
105+
| Manifest_error a, Manifest_error b -> User_message.compare a b
106+
;;
107+
end
108+
109+
module Message_map = Map.Make (Message)
94110

95111
(* Map messages to the list of platforms for which those messages are
96112
relevant. If a dependency problem has no solution on any platform, it's
@@ -101,14 +117,20 @@ module Platforms_by_message = struct
101117
type t = Solver_env.t list Message_map.t
102118

103119
let singleton message platform : t = Message_map.singleton message [ platform ]
120+
let to_list (t : t) : (Message.t * Solver_env.t list) list = Message_map.to_list t
121+
let union_all ts : t = Message_map.union_all ts ~f:(fun _ a b -> Some (a @ b))
104122

105-
let to_list (t : t) : (User_message.Style.t Pp.t * Solver_env.t list) list =
106-
Message_map.to_list t
123+
let all_solver_errors_raising_if_any_manifest_errors t =
124+
let solver_errors, manifest_errors =
125+
List.partition_map (to_list t) ~f:(fun (message, platforms) ->
126+
match message with
127+
| Solve_error message -> Left (message, platforms)
128+
| Manifest_error message -> Right message)
129+
in
130+
match manifest_errors with
131+
| [] -> solver_errors
132+
| message :: _ -> raise (User_error.E message)
107133
;;
108-
109-
let union_all ts : t = Message_map.union_all ts ~f:(fun _ a b -> Some (a @ b))
110-
let all_platforms (t : t) = Message_map.values t |> List.concat
111-
let all_messages (t : t) = Message_map.keys t
112134
end
113135

114136
let solve_multiple_platforms
@@ -143,7 +165,12 @@ let solve_multiple_platforms
143165
Fiber.parallel_map solve_for_platforms ~f:(fun platform_env ->
144166
let solver_env = Solver_env.extend portable_solver_env platform_env in
145167
let+ solver_result = solve_for_env solver_env in
146-
Result.map_error solver_result ~f:(fun (`Diagnostic_message message) ->
168+
Result.map_error solver_result ~f:(fun message ->
169+
let message : Platforms_by_message.Message.t =
170+
match message with
171+
| `Solve_error m -> Solve_error m
172+
| `Manifest_error m -> Manifest_error m
173+
in
147174
Platforms_by_message.singleton message platform_env))
148175
in
149176
let solver_results, errors =
@@ -153,14 +180,21 @@ let solve_multiple_platforms
153180
in
154181
match solver_results, errors with
155182
| [], [] -> Code_error.raise "Solver did not run for any platforms." []
156-
| [], errors -> `All_error (Platforms_by_message.union_all errors)
183+
| [], errors ->
184+
`All_error
185+
(Platforms_by_message.union_all errors
186+
|> Platforms_by_message.all_solver_errors_raising_if_any_manifest_errors)
157187
| x :: xs, errors ->
158188
let merged_solver_result =
159189
List.fold_left xs ~init:x ~f:Dune_pkg.Opam_solver.Solver_result.merge
160190
in
161191
if List.is_empty errors
162192
then `All_ok merged_solver_result
163-
else `Partial (merged_solver_result, Platforms_by_message.union_all errors)
193+
else
194+
`Partial
195+
( merged_solver_result
196+
, Platforms_by_message.union_all errors
197+
|> Platforms_by_message.all_solver_errors_raising_if_any_manifest_errors )
164198
;;
165199

166200
let summary_message
@@ -235,8 +269,7 @@ let summary_message
235269
;;
236270

237271
let pp_solve_errors_by_platforms platforms_by_message =
238-
Platforms_by_message.to_list platforms_by_message
239-
|> List.map ~f:(fun (solver_error, platforms) ->
272+
List.map platforms_by_message ~f:(fun (message, platforms) ->
240273
Pp.concat
241274
~sep:Pp.cut
242275
[ Pp.nop
@@ -246,7 +279,7 @@ let pp_solve_errors_by_platforms platforms_by_message =
246279
platforms:"
247280
; Pp.enumerate platforms ~f:Solver_env.pp_oneline
248281
; Pp.box @@ Pp.text "...with this error:"
249-
; solver_error
282+
; message
250283
]
251284
|> Pp.vbox)
252285
;;
@@ -330,19 +363,21 @@ let solve_lock_dir
330363
| `All_ok solver_result -> Ok (solver_result, [])
331364
| `Partial (solver_result, errors) ->
332365
Log.info @@ pp_solve_errors_by_platforms errors;
366+
let all_platforms =
367+
List.concat_map errors ~f:snd |> List.sort_uniq ~compare:Solver_env.compare
368+
in
333369
Ok
334370
( solver_result
335371
, [ Pp.nop
336372
; Pp.tag User_message.Style.Warning
373+
@@ Pp.vbox
337374
@@ Pp.concat
338-
~sep:Pp.newline
375+
~sep:Pp.cut
339376
[ Pp.box
340377
@@ Pp.text "No package solution was found for some requsted platforms."
341378
; Pp.nop
342379
; Pp.box @@ Pp.text "Platforms with no solution:"
343-
; Pp.enumerate
344-
(Platforms_by_message.all_platforms errors)
345-
~f:Solver_env.pp_oneline
380+
; Pp.box @@ Pp.enumerate all_platforms ~f:Solver_env.pp_oneline
346381
; Pp.nop
347382
; Pp.box
348383
@@ Pp.text
@@ -447,7 +482,7 @@ let solve
447482
User_error.raise
448483
([ Pp.text "Unable to solve dependencies for the following lock directories:" ]
449484
@ List.concat_map errors ~f:(fun (path, errors) ->
450-
let messages = Platforms_by_message.all_messages errors in
485+
let messages = List.map errors ~f:fst in
451486
[ Pp.textf "Lock directory %s:" (Path.to_string_maybe_quoted path)
452487
; Pp.vbox (Pp.concat ~sep:Pp.cut messages)
453488
]))

otherlibs/stdune/src/result.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ module O = struct
7979
let ( >>| ) t f = map t ~f
8080
let ( let* ) = ( >>= )
8181
let ( let+ ) = ( >>| )
82+
let ( and* ) = both
8283
let ( and+ ) = both
8384
end
8485

otherlibs/stdune/src/result.mli

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ module O : sig
2424
val ( let* ) : ('a, 'error) t -> ('a -> ('b, 'error) t) -> ('b, 'error) t
2525
val ( and+ ) : ('a, 'error) t -> ('b, 'error) t -> ('a * 'b, 'error) t
2626
val ( let+ ) : ('a, 'error) t -> ('a -> 'b) -> ('b, 'error) t
27+
val ( and* ) : ('a, 'error) t -> ('b, 'error) t -> ('a * 'b, 'error) t
2728
end
2829

2930
val map : ('a, 'error) t -> f:('a -> 'b) -> ('b, 'error) t
@@ -36,6 +37,8 @@ val to_dyn : 'a Dyn.builder -> 'error Dyn.builder -> ('a, 'error) t Dyn.builder
3637
(** Produce [Error <message>] *)
3738
val errorf : ('a, unit, string, (_, string) t) format4 -> 'a
3839

40+
val both : ('a, 'e) t -> ('b, 'e) t -> ('a * 'b, 'e) t
41+
3942
(** For compatibility with some other code *)
4043
type ('a, 'error) result = ('a, 'error) t
4144

0 commit comments

Comments
 (0)