forked from ocaml/opam
-
Notifications
You must be signed in to change notification settings - Fork 0
/
couverture.ml
executable file
·146 lines (136 loc) · 5.49 KB
/
couverture.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#!/usr/bin/env opam-admin.top
#directory "+../opam-lib";;
(**************************************************************************)
(* *)
(* Copyright 2015 OCamlPro *)
(* *)
(* All rights reserved.This file is distributed under the terms of the *)
(* GNU Lesser General Public License version 3.0 with linking *)
(* exception. *)
(* *)
(* OPAM is distributed in the hope that it will be useful, but WITHOUT *)
(* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *)
(* or FITNESS FOR A PARTICULAR PURPOSE.See the GNU General Public *)
(* License for more details. *)
(* *)
(**************************************************************************)
(** This script gives scenarios to install all named packages in a given set.
This may require several steps, in case of conflicts.
Consistent installation steps are printed one per line to stdout. Stderr
gives more detail.
Relies on the current opam root for the list of available packages, i.e.
depends on configured remotes, OS and OCaml version, but not on the set of
currently installed packages.
*)
open OpamTypes
let max_install t inst_packages =
let universe = OpamState.universe t Depends in
let wish_field = "wished" in
let base = OpamState.base_packages t in
let universe =
{ universe with u_installed = base;
u_installed_roots = base;
u_attrs = [wish_field, inst_packages]; }
in
if not (OpamCudf.external_solver_available ()) then
failwith "No external solver found";
let preferences =
let preferences = OpamSolverConfig.criteria `Default in
Some (lazy (Printf.sprintf "+sum(solution,%s),%s" wish_field preferences))
in
OpamSolverConfig.update ~solver_preferences_default:preferences ();
let version_map =
OpamSolver.cudf_versions_map universe universe.u_available
in
let request = {
wish_install = [];
wish_remove = [];
wish_upgrade = [];
extra_attributes = [wish_field];
criteria = `Default;
} in
let cudf_universe =
OpamSolver.load_cudf_universe ~build:true universe ~version_map
universe.u_available
in
match OpamCudf.resolve ~extern:true ~version_map cudf_universe request with
| Conflicts _ -> failwith "Solver error (unexpected conflicts)"
| Success u ->
OpamPackage.Set.diff
(OpamPackage.Set.of_list
(List.map OpamCudf.cudf2opam (OpamCudf.packages u)))
base
module P = OpamPackage
open P.Set.Op
let rec couverture acc t pkgs =
Printf.eprintf "# %d packages remaining...\n%!"
(P.Name.Set.cardinal (P.names_of_packages pkgs));
let step = max_install t pkgs in
let added =
P.Name.Set.inter (P.names_of_packages step) (P.names_of_packages pkgs)
in
if P.Name.Set.is_empty added then
let () =
Printf.eprintf "# -> %d uninstallable packages remaining.\n%!"
(P.Name.Set.cardinal (P.names_of_packages pkgs))
in
List.rev acc, pkgs
else
let n = P.Name.Set.cardinal added in
Printf.eprintf "# -> Step %d: covering %d/%d packages%s.\n%!"
(List.length acc + 1) n (P.Name.Set.cardinal (P.names_of_packages pkgs))
(if n > 5 then "" else
OpamStd.List.concat_map ~left:" (" ~right:")" " " P.Name.to_string
(OpamPackage.Name.Set.elements added));
let pkgs =
P.Set.filter
(fun nv -> not (P.has_name step (P.name nv))) pkgs
in
couverture (step::acc) t pkgs
let () =
let root = OpamStateConfig.opamroot () in
OpamFormatConfig.init ();
if not (OpamStateConfig.load_defaults root) then
failwith "Opam root not found";
OpamStd.Config.init ();
OpamSolverConfig.init ();
OpamStateConfig.init ();
let t =
OpamState.load_state ~save_cache:false "couverture"
OpamStateConfig.(!r.current_switch)
in
let avail = Lazy.force t.OpamState.Types.available_packages in
let wanted = match Array.to_list Sys.argv with
| [] | _::[] ->
avail -- P.packages_of_names avail (OpamState.base_package_names t)
| _::l ->
List.fold_left (fun wanted name ->
let nvs =
if String.contains name '.' then
P.Set.singleton (P.of_string name)
else
P.packages_of_name avail
(P.Name.of_string name)
in
if P.Set.is_empty (nvs %% avail) then
failwith (Printf.sprintf "Package %s not found" name)
else
wanted ++ nvs
) P.Set.empty l
in
let couv,remaining = couverture [] t wanted in
let avail_names = P.names_of_packages avail in
let remaining_names = P.names_of_packages remaining in
Printf.eprintf "# Found a couverture for %d over %d packages in %d steps:\n%!"
(P.Name.Set.cardinal (P.Name.Set.diff avail_names remaining_names))
(P.Name.Set.cardinal avail_names)
(List.length couv);
List.iter (fun s ->
print_endline
(OpamStd.List.concat_map " " OpamPackage.to_string
(P.Set.elements s)))
couv;
Printf.eprintf "# %d uninstallable packages remain: %s\n%!"
(P.Name.Set.cardinal remaining_names)
(OpamStd.List.concat_map " " OpamPackage.Name.to_string
(P.Name.Set.elements remaining_names))