Skip to content

Commit be54d96

Browse files
authored
pkg: print portable solutions grouped by platform (#12620)
The message that prints after solving lists each package in the solution. When a solution is portable, it's possible (though hopefully rare) that the version of a package included in a solution will be different depending on the platform. This led to multiple versions of the same package appearing in the message for non-obvious reasons, which some found confusing. This commit changes the message when portable lockdirs are enabled, such that the packages common to all platforms are printed together, and each set of platform-specific dependencies is printetd separately from the common dependencies. Signed-off-by: Stephen Sherratt <[email protected]>
1 parent dcb6800 commit be54d96

15 files changed

+285
-30
lines changed

bin/pkg/lock.ml

Lines changed: 106 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,14 @@ let solve_multiple_platforms
114114
let+ results =
115115
Fiber.parallel_map solve_for_platforms ~f:(fun platform_env ->
116116
let solver_env = Dune_pkg.Solver_env.extend portable_solver_env platform_env in
117-
solve_for_env solver_env)
117+
let+ solver_result = solve_for_env solver_env in
118+
Result.map_error solver_result ~f:(fun (`Diagnostic_message message) ->
119+
platform_env, message))
118120
in
119121
let solver_results, errors =
120122
List.partition_map results ~f:(function
121123
| Ok result -> Left result
122-
| Error (`Diagnostic_message message) -> Right message)
124+
| Error e -> Right e)
123125
in
124126
match solver_results, errors with
125127
| [], [] -> Code_error.raise "Solver did not run for any platforms." []
@@ -133,6 +135,77 @@ let solve_multiple_platforms
133135
else `Partial (merged_solver_result, errors)
134136
;;
135137

138+
let summary_message
139+
~portable_lock_dir
140+
~lock_dir_path
141+
~(lock_dir : Lock_dir.t)
142+
~maybe_perf_stats
143+
~maybe_unsolved_platforms_message
144+
=
145+
if portable_lock_dir
146+
then (
147+
let pkgs_by_platform = Lock_dir.Packages.pkgs_by_platform lock_dir.packages in
148+
let opam_package_sets_by_platform =
149+
Dune_pkg.Solver_env.Map.map pkgs_by_platform ~f:(fun pkgs ->
150+
List.map pkgs ~f:(fun (pkg : Dune_pkg.Lock_dir.Pkg.t) ->
151+
OpamPackage.create
152+
(Dune_pkg.Package_name.to_opam_package_name pkg.info.name)
153+
(Dune_pkg.Package_version.to_opam_package_version pkg.info.version))
154+
|> OpamPackage.Set.of_list)
155+
in
156+
let common_packages =
157+
Dune_pkg.Solver_env.Map.values opam_package_sets_by_platform
158+
|> List.reduce ~f:OpamPackage.Set.inter
159+
|> Option.value ~default:OpamPackage.Set.empty
160+
in
161+
let pp_package_set package_set =
162+
if OpamPackage.Set.is_empty package_set
163+
then Pp.tag User_message.Style.Warning @@ Pp.text "(none)"
164+
else
165+
Pp.enumerate (OpamPackage.Set.elements package_set) ~f:(fun opam_package ->
166+
Pp.text (OpamPackage.to_string opam_package))
167+
in
168+
let uncommon_packages_by_platform =
169+
Dune_pkg.Solver_env.Map.map opam_package_sets_by_platform ~f:(fun package_set ->
170+
OpamPackage.Set.diff package_set common_packages)
171+
|> Dune_pkg.Solver_env.Map.filteri ~f:(fun _ package_set ->
172+
not (OpamPackage.Set.is_empty package_set))
173+
in
174+
let maybe_uncommon_packages =
175+
if Dune_pkg.Solver_env.Map.is_empty uncommon_packages_by_platform
176+
then []
177+
else
178+
Pp.nop
179+
:: Pp.text "Additionally, some packages will only be built on specific platforms."
180+
:: (Dune_pkg.Solver_env.Map.to_list uncommon_packages_by_platform
181+
|> List.concat_map ~f:(fun (platform, packages) ->
182+
[ Pp.nop
183+
; Pp.concat [ Dune_pkg.Solver_env.pp_oneline platform; Pp.text ":" ]
184+
; pp_package_set packages
185+
]))
186+
in
187+
(Pp.tag
188+
User_message.Style.Success
189+
(Pp.textf "Solution for %s" (Path.to_string_maybe_quoted lock_dir_path))
190+
:: Pp.nop
191+
:: Pp.text "This solution supports the following platforms:"
192+
:: Pp.enumerate (snd lock_dir.solved_for_platforms) ~f:Dune_pkg.Solver_env.pp_oneline
193+
:: Pp.nop
194+
:: Pp.text "Dependencies on all supported platforms:"
195+
:: pp_package_set common_packages
196+
:: (maybe_uncommon_packages @ maybe_perf_stats))
197+
@ maybe_unsolved_platforms_message)
198+
else
199+
(Pp.tag
200+
User_message.Style.Success
201+
(Pp.textf "Solution for %s:" (Path.to_string_maybe_quoted lock_dir_path))
202+
:: (match Lock_dir.Packages.to_pkg_list lock_dir.packages with
203+
| [] -> Pp.tag User_message.Style.Warning @@ Pp.text "(no dependencies to lock)"
204+
| packages -> pp_packages packages)
205+
:: maybe_perf_stats)
206+
@ maybe_unsolved_platforms_message
207+
;;
208+
136209
let solve_lock_dir
137210
workspace
138211
~local_packages
@@ -211,14 +284,32 @@ let solve_lock_dir
211284
| `All_error messages -> Error messages
212285
| `All_ok solver_result -> Ok (solver_result, [])
213286
| `Partial (solver_result, errors) ->
214-
Log.info errors;
287+
Log.info
288+
@@ List.map errors ~f:(fun (platform, solver_error) ->
289+
Pp.concat
290+
~sep:Pp.newline
291+
[ Pp.box @@ Pp.text "Failed to find package solution for platform:"
292+
; Dune_pkg.Solver_env.pp platform
293+
; solver_error
294+
]);
215295
Ok
216296
( solver_result
217297
, [ Pp.nop
218-
; Pp.text
219-
"No solution was found for some platforms. See the log or run with \
220-
--verbose for more details."
221-
|> Pp.tag User_message.Style.Warning
298+
; Pp.tag User_message.Style.Warning
299+
@@ Pp.concat
300+
~sep:Pp.newline
301+
[ Pp.box
302+
@@ Pp.text "No package solution was found for some requsted platforms."
303+
; Pp.nop
304+
; Pp.box @@ Pp.text "Platforms with no solution:"
305+
; Pp.enumerate errors ~f:(fun (platform, _) ->
306+
Dune_pkg.Solver_env.pp_oneline platform)
307+
; Pp.nop
308+
; Pp.box
309+
@@ Pp.text
310+
"See the log or run with --verbose for more details. Configure \
311+
platforms to solve for in the dune-workspace file."
312+
]
222313
] )
223314
in
224315
match solver_result with
@@ -245,15 +336,12 @@ let solve_lock_dir
245336
in
246337
let summary_message =
247338
User_message.make
248-
((Pp.tag
249-
User_message.Style.Success
250-
(Pp.textf "Solution for %s:" (Path.to_string_maybe_quoted lock_dir_path))
251-
:: (match Lock_dir.Packages.to_pkg_list lock_dir.packages with
252-
| [] ->
253-
Pp.tag User_message.Style.Warning @@ Pp.text "(no dependencies to lock)"
254-
| packages -> pp_packages packages)
255-
:: maybe_perf_stats)
256-
@ maybe_unsolved_platforms_message)
339+
(summary_message
340+
~portable_lock_dir
341+
~lock_dir_path
342+
~lock_dir
343+
~maybe_perf_stats
344+
~maybe_unsolved_platforms_message)
257345
in
258346
progress_state := None;
259347
let+ lock_dir = Lock_dir.compute_missing_checksums ~pinned_packages lock_dir in
@@ -308,7 +396,8 @@ let solve
308396
| Error errors ->
309397
User_error.raise
310398
([ Pp.text "Unable to solve dependencies for the following lock directories:" ]
311-
@ List.concat_map errors ~f:(fun (path, messages) ->
399+
@ List.concat_map errors ~f:(fun (path, messages_by_platform) ->
400+
let messages = List.map messages_by_platform ~f:snd in
312401
[ Pp.textf "Lock directory %s:" (Path.to_string_maybe_quoted path)
313402
; Pp.hovbox (Pp.concat ~sep:Pp.newline messages)
314403
]))

src/dune_pkg/lock_dir.ml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,13 @@ module Packages = struct
10221022
|> List.find ~f:(Pkg.is_enabled_on_platform ~platform))
10231023
;;
10241024

1025+
let pkgs_by_platform t =
1026+
to_pkg_list t
1027+
|> List.fold_left ~init:Solver_env.Map.empty ~f:(fun acc (pkg : Pkg.t) ->
1028+
List.fold_left pkg.enabled_on_platforms ~init:acc ~f:(fun acc platform ->
1029+
Solver_env.Map.add_multi acc platform pkg))
1030+
;;
1031+
10251032
let merge a b =
10261033
Package_name.Map.merge a b ~f:(fun _ a b ->
10271034
match a, b with

src/dune_pkg/lock_dir.mli

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ module Packages : sig
8989

9090
val to_pkg_list : t -> Pkg.t list
9191
val pkgs_on_platform_by_name : t -> platform:Solver_env.t -> Pkg.t Package_name.Map.t
92+
93+
(** All the packages grouped by the platforms where they are enabled. *)
94+
val pkgs_by_platform : t -> Pkg.t list Solver_env.Map.t
9295
end
9396

9497
type t = private

src/dune_pkg/solver_env.ml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,19 @@ let pp t =
104104
(String.maybe_quoted (Variable_value.to_string value)))
105105
;;
106106

107+
let pp_oneline t =
108+
if Package_variable_name.Map.is_empty t
109+
then Pp.text "(empty)"
110+
else
111+
Pp.concat
112+
~sep:(Pp.text "; ")
113+
(List.map (Package_variable_name.Map.to_list t) ~f:(fun (variable, value) ->
114+
Pp.textf
115+
"%s = %s"
116+
(Package_variable_name.to_string variable)
117+
(String.maybe_quoted (Variable_value.to_string value))))
118+
;;
119+
107120
let unset = Package_variable_name.Map.remove
108121

109122
let unset_multi t variable_names =

src/dune_pkg/solver_env.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ val extend : t -> t -> t
2929
val with_defaults : t
3030

3131
val pp : t -> 'a Pp.t
32+
val pp_oneline : t -> 'a Pp.t
3233
val unset_multi : t -> Package_variable_name.Set.t -> t
3334

3435
(** [remove_all_except t names] returns an environment with the same bindings

test/blackbox-tests/test-cases/pkg/portable-lockdirs/portable-lockdirs-basic.t

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,17 @@ Create a package that writes a different value to some files depending on the os
3434
> EOF
3535

3636
$ DUNE_CONFIG__PORTABLE_LOCK_DIR=enabled dune pkg lock
37-
Solution for dune.lock:
37+
Solution for dune.lock
38+
39+
This solution supports the following platforms:
40+
- arch = x86_64; os = linux
41+
- arch = arm64; os = linux
42+
- arch = x86_64; os = macos
43+
- arch = arm64; os = macos
44+
- arch = x86_64; os = win32
45+
- arch = arm64; os = win32
46+
47+
Dependencies on all supported platforms:
3848
- foo.0.0.1
3949

4050
$ cat ${default_lock_dir}/lock.dune

test/blackbox-tests/test-cases/pkg/portable-lockdirs/portable-lockdirs-custom-platforms.t

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,12 @@ Create a custom dune-workspace to solve for openbsd.
4646
> EOF
4747

4848
$ DUNE_CONFIG__PORTABLE_LOCK_DIR=enabled dune pkg lock
49-
Solution for dune.lock:
49+
Solution for dune.lock
50+
51+
This solution supports the following platforms:
52+
- arch = x86_64; os = openbsd
53+
54+
Dependencies on all supported platforms:
5055
- foo.0.0.1
5156

5257
$ cat ${default_lock_dir}/lock.dune

test/blackbox-tests/test-cases/pkg/portable-lockdirs/portable-lockdirs-custom-solver-env.t

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,23 @@ Set up a project that depends on the package:
4949

5050
Solve the project:
5151
$ DUNE_CONFIG__PORTABLE_LOCK_DIR=enabled dune pkg lock
52-
Solution for dune.lock:
52+
Solution for dune.lock
53+
54+
This solution supports the following platforms:
55+
- arch = x86_64; os = linux; sys-ocaml-version =
56+
5.4.0+solver-env-version-override
57+
- arch = arm64; os = linux; sys-ocaml-version =
58+
5.4.0+solver-env-version-override
59+
- arch = x86_64; os = macos; sys-ocaml-version =
60+
5.4.0+solver-env-version-override
61+
- arch = arm64; os = macos; sys-ocaml-version =
62+
5.4.0+solver-env-version-override
63+
- arch = x86_64; os = win32; sys-ocaml-version =
64+
5.4.0+solver-env-version-override
65+
- arch = arm64; os = win32; sys-ocaml-version =
66+
5.4.0+solver-env-version-override
67+
68+
Dependencies on all supported platforms:
5369
- foo.0.0.1
5470

5571
Confirming that the build action creates the conditional file:

test/blackbox-tests/test-cases/pkg/portable-lockdirs/portable-lockdirs-depexts-basic.t

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,17 @@ Demonstrate various cases representing depexts in lockfiles.
2222
> EOF
2323

2424
$ DUNE_CONFIG__PORTABLE_LOCK_DIR=enabled dune pkg lock
25-
Solution for dune.lock:
25+
Solution for dune.lock
26+
27+
This solution supports the following platforms:
28+
- arch = x86_64; os = linux
29+
- arch = arm64; os = linux
30+
- arch = x86_64; os = macos
31+
- arch = arm64; os = macos
32+
- arch = x86_64; os = win32
33+
- arch = arm64; os = win32
34+
35+
Dependencies on all supported platforms:
2636
- foo.0.0.1
2737

2838
$ cat ${default_lock_dir}/foo.0.0.1.pkg

test/blackbox-tests/test-cases/pkg/portable-lockdirs/portable-lockdirs-depexts-pkg-config.t

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,14 @@ correct depext names can be chosen for the current distro at build time.
5252
> EOF
5353

5454
$ DUNE_CONFIG__PORTABLE_LOCK_DIR=enabled dune pkg lock
55-
Solution for dune.lock:
55+
Solution for dune.lock
56+
57+
This solution supports the following platforms:
58+
- arch = x86_64; os = macos; os-distribution = homebrew; os-family = homebrew
59+
- arch = x86_64; os = linux
60+
- arch = x86_64; os = win32; os-distribution = cygwin; os-family = windows
61+
62+
Dependencies on all supported platforms:
5663
- conf-pkg-config.0.0.1
5764

5865
Print the name of the depext on a variety of os/distro/versions:

0 commit comments

Comments
 (0)