package opam-solver

  1. Overview
  2. Docs

doc/src/opam-solver/opamCudfSolver.ml.html

Source file opamCudfSolver.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
(**************************************************************************)
(*                                                                        *)
(*    Copyright 2017 OCamlPro                                             *)
(*                                                                        *)
(*  All rights reserved. This file is distributed under the terms of the  *)
(*  GNU Lesser General Public License version 2.1, with the special       *)
(*  exception on linking described in the file LICENSE.                   *)
(*                                                                        *)
(**************************************************************************)

open OpamTypes

include OpamCudfSolverSig

let default_compat_criteria = {
  crit_default = "-removed,-notuptodate,-changed";
  crit_upgrade = "-removed,-notuptodate,-changed";
  crit_fixup = "-changed,-notuptodate";
  crit_best_effort_prefix = None;
}

module type ExternalArg = sig
  val name: string
  val is_present: bool Lazy.t
  val command_name: string
  val command_args: OpamTypes.arg list
  val default_criteria: criteria_def
end

let call_external_solver command ~criteria ?timeout (_, universe,_ as cudf) =
  let solver_in =
    OpamFilename.of_string (OpamSystem.temp_file "solver-in") in
  let solver_out =
    OpamFilename.of_string (OpamSystem.temp_file "solver-out") in
  try
    let _ =
      let oc = OpamFilename.open_out solver_in in
      Cudf_printer.pp_cudf oc cudf;
      close_out oc
    in
    let () =
      let cmd =
        OpamFilter.single_command (fun v ->
            if not (OpamVariable.Full.is_global v) then None else
            match OpamVariable.to_string (OpamVariable.Full.variable v) with
            | "input" -> Some (S (OpamFilename.to_string solver_in))
            | "output" -> Some (S (OpamFilename.to_string solver_out))
            | "criteria" -> Some (S criteria)
            | "timeout" ->
              Some (S (string_of_float (OpamStd.Option.default 0. timeout)))
            | _ -> None)
          command
      in
      OpamSystem.command
        ~verbose:(OpamCoreConfig.(!r.debug_level >= 2))
        cmd
    in
    OpamFilename.remove solver_in;
    if not (OpamFilename.exists solver_out) then
      raise (Common.CudfSolver.Error "no output")
    else if
      (let ic = OpamFilename.open_in solver_out in
       try
         let i = input_line ic in close_in ic;
         i = "FAIL"
       with End_of_file -> close_in ic; false)
    then
      raise Common.CudfSolver.Unsat
    else
    let r =
      Cudf_parser.load_solution_from_file
        (OpamFilename.to_string solver_out) universe in
    OpamFilename.remove solver_out;
    r
  with e ->
    OpamStd.Exn.finalise e @@ fun () ->
    OpamFilename.remove solver_in;
    OpamFilename.remove solver_out

module External (E: ExternalArg) : S = struct
  let name = E.name

  let ext = ref None

  let is_present () = Lazy.force E.is_present

  let command_name = Some E.command_name

  let default_criteria = E.default_criteria

  let call =
    call_external_solver ((CString E.command_name, None) :: E.command_args)
end

module Aspcud_def = struct
  let name = "aspcud"

  let command_name = "aspcud"

  let is_present = lazy (
    match OpamSystem.resolve_command command_name with
    | None -> false
    | Some cmd ->
    try
      match
        OpamSystem.read_command_output ~verbose:false ~allow_stdin:false
          [cmd; "-v"]
      with
      | [] -> false
      | s::_ ->
        match OpamStd.String.split s ' ' with
        | "aspcud"::_::v::_ when OpamVersionCompare.compare v "1.9" >= 0 ->
          OpamConsole.log "SOLVER"
            "Solver is aspcud >= 1.9: using latest version criteria";
          true
        | _ -> false
    with OpamSystem.Process_error _ -> false
  )

  let command_args = [
    CIdent "input", None; CIdent "output", None;
    CIdent "criteria", None
  ]

  let default_criteria =
    {
      crit_default = "-count(removed),\
                      -sum(request,version-lag),\
                      -count(down),\
                      -sum(solution,version-lag),\
                      -count(changed)";
      crit_upgrade = "-count(down),\
                      -count(removed),\
                      -sum(solution,version-lag),\
                      -count(new)";
      crit_fixup = "-count(changed),\
                    -notuptodate(solution),-sum(solution,version-lag)";
      crit_best_effort_prefix = Some "+sum(solution,opam-query),";
    }
end

module Aspcud = External(Aspcud_def)

module Aspcud_old_def = struct
  let name = "aspcud-old"

  let command_name = Aspcud_def.command_name

  let is_present = lazy (OpamSystem.resolve_command command_name <> None)

  let command_args = Aspcud_def.command_args

  let default_criteria = default_compat_criteria
end

module Aspcud_old = External(Aspcud_old_def)

module Mccs_def = struct
  let name = "mccs"

  let command_name = "mccs"

  let is_present = lazy (OpamSystem.resolve_command command_name <> None)

  let command_args = [
    CString "-i", None; CIdent "input", None;
    CString "-o", None; CIdent "output", None;
    CString "-lexagregate[%{criteria}%]", None;
  ]

  let default_criteria =  {
    crit_default = "-removed,\
                    -count[version-lag:,true],\
                    -changed,\
                    -count[version-lag:,false],\
                    -new";
    crit_upgrade = "-removed,\
                    -count[version-lag:,false],\
                    -new";
    crit_fixup = "-changed,-count[version-lag:,false]";
    crit_best_effort_prefix = Some "+count[opam-query:,false],";
  }
end

module Mccs = External(Mccs_def)

module Packup_def = struct
  let name = "packup"

  let command_name = "packup"

  let is_present = lazy (OpamSystem.resolve_command command_name <> None)

  let command_args = [
    CIdent "input", None; CIdent "output", None;
    CString "-u", None; CIdent "criteria", None;
  ]

  let default_criteria = default_compat_criteria
end

module Packup = External(Packup_def)

let make_custom_solver name args criteria =
  (module
    (External (struct
       let command_name = name
       let name = name ^ "-custom"
       let is_present = lazy true
       let command_args = args
       let default_criteria = criteria
     end))
    : S)


let default_solver_selection =
  OpamBuiltinMccs.all_backends @ [
    (module Aspcud: S);
    (module Mccs: S);
    (module Aspcud_old: S);
    (module Packup: S);
  ]

let extract_solver_param name =
  if OpamStd.String.ends_with ~suffix:")" name then
    match OpamStd.String.cut_at name '(' with
    | Some (xname, ext2) ->
      xname, Some (OpamStd.String.remove_suffix ~suffix:")" ext2)
    | None -> name, None
  else name, None

let custom_solver cmd = match cmd with
  | [ CIdent name, _ ] | [ CString name, _ ] ->
    (try
       let xname, ext = extract_solver_param name in
       List.find (fun (module S: S) ->
           let n, _ = extract_solver_param S.name in
           (n = xname || n = Filename.basename xname ||
            S.command_name = Some name) &&
           (if ext <> None then S.ext := ext;
            S.is_present ()))
         default_solver_selection
     with Not_found ->
       OpamConsole.error_and_exit `Configuration_error
         "No installed solver matching the selected '%s' found"
         name)
  | ((CIdent name | CString name), _) :: args ->
    let criteria =
      try
        let corresponding_module =
          List.find (fun (module S: S) ->
              S.command_name =
              Some (Filename.basename name) && S.is_present ())
            default_solver_selection
        in
        let module S = (val corresponding_module) in
        S.default_criteria
      with Not_found -> default_compat_criteria
    in
    make_custom_solver name args criteria
  | _ ->
    OpamConsole.error_and_exit `Configuration_error
      "Invalid custom solver command specified."

let solver_of_string s =
  let args = OpamStd.String.split s ' ' in
  (custom_solver
     (List.map (fun a -> OpamTypes.CString a, None) args))

let has_builtin_solver () =
  List.exists
    (fun (module S: S) -> S.command_name = None && S.is_present ())
    default_solver_selection

let get_solver ?internal l =
  try
    List.find
      (fun (module S: S) ->
         (internal = None || internal = Some (S.command_name = None)) &&
         S.is_present ())
      l
  with Not_found ->
    OpamConsole.error_and_exit `Configuration_error
      "No available solver found. Make sure your solver configuration is \
       correct. %s"
      (if has_builtin_solver () then
         "You can enforce use of the built-in solver with \
          `--use-internal-solver'."
       else
         "This opam has been compiled without a built-in solver, so you need \
          to install and configure an external one. See \
          http://opam.ocaml.org/doc/Install.html#ExternalSolvers for details.")

let get_name (module S: S) =
  let name, ext0 = extract_solver_param S.name in
  match !S.ext, ext0 with
  | Some e, _ | None, Some e -> Printf.sprintf "%s(%s)" name e
  | None, None -> name
OCaml

Innovation. Community. Security.