opam-0install-cudf-0.5.0/0000755000175000017500000000000014657442125016525 5ustar kit_ty_katekit_ty_kateopam-0install-cudf-0.5.0/.gitignore0000644000175000017500000000004014657442125020507 0ustar kit_ty_katekit_ty_kate/_build .merlin *.install .*.~* opam-0install-cudf-0.5.0/CHANGES.md0000644000175000017500000000665614657442125020134 0ustar kit_ty_katekit_ty_katev0.5.0 (15/08/2024) ------------------- - Remove opam-0install (the project has been split off from opam-0install-cudf) - Avoid global mutable state. This allows the solver to be potentially called multiple times without causing differences in output. (@kit-ty-kate #4) - Add support for the avoid-version flag. This is enabled by default and can be disabled by setting the new `?handle_avoid_version` parameter to `false`. (@kit-ty-kate #2 #3) - Add a new `?prefer_installed` parameter to encourage keeping installed packages if possible. (@kit-ty-kate #3) - Add some tests (@kit-ty-kate #2 #3) v0.4.3 (28/04/2022) ------------------- - Add `?opam_version` to `Dir_context.std_env` (@emillon #36). It defaults to the version of opam libraries, but in some cases (e.g. ocaml-ci) it is useful to inject a value that comes from an external opam process. - Sort `Reject` after `RealImpl` (@emillon #33). This improves error messages by displaying Rejects first. - Expose diagnostics rolemap in Solver (@NathanReb #31). Allows library users to provide extra help on error. - Cmdliner 1.1.0 compatibility (@dra27 #40) - Fix compiler warnings from new fmt (@talex5 #32). v0.4.2 (16/06/2021) ------------------- - opam-0install: Upgrade to opam 2.1.0~rc (@kit-ty-kate #29) - Upgrade to (lang dune 2.7) (@kit-ty-kate #29) v0.4.1 (22/04/2021) ------------------- - opam-0install-cudf: Remove unused (`cmdliner`) and unnecessary (`fmt`) dependencies for easier integration with opam. (@kit-ty-kate #28) - opam-0install: Be explicit that `Ok` values are not passed to `Term.exit` (@talex5 #24) v0.4 (09/10/2020) ---------------- - opam-0install-cudf: Fix conflict detection (@kit-ty-kate #20) v0.3 (17/09/2020) ---------------- - opam-0install-cudf: Allow to tag packages as recommended when giving them to the solver (@kit-ty-kate #16) Recommanded packages might or might not be chosen by the solver depending on whether the most up-to-date Essential packages available are compatible with them. - Add an option to get the least up-to-date version of each packages (@kit-ty-kate #18) Option available in both opam-0install and opam-0install-cudf libraries as well as a new --prefer-oldest option to the opam-0install binary. - opam-0install-cudf: Remove the unnecessary dependency towards the opam library (@kit-ty-kate #15) - Documentation: Add a link to API docs in the README (@talex5 #14 #17) v0.2 (17/06/2020) ---------------- - Add a new `opam-0install-cudf` package (@kit-ty-kate #11). This uses opam's CUDF API, allowing the solver to be used directly from within opam. - `Dir_context.std_env` now has some optional arguments, and also responds for `opam-version` (@talex5 #12). You will need to add an extra `()` argument to it to upgrade. - Evaluate a package's `available` expression in `Dir_context` (@talex5 #12). This isn't needed for `Switch_context` because the switch does it for us, but `Dir_context` could return packages with `available: false`. - Simplify the `CONTEXT` API (@talex5 #12). `candidates` now returns either `Ok opam` or `Error pkg` for each package. This is clearer than using an option type and avoids the need for a separate `load` function. It also makes it possible to filter packages based on the content of the opam file without having to load it twice. We also no longer bother loading the opam file for rejects (all we need is the name). v0.1 (26/05/2020) ---------------- Initial release. opam-0install-cudf-0.5.0/LICENSE.md0000644000175000017500000000135414657442125020134 0ustar kit_ty_katekit_ty_kateCopyright (c) 2020 Thomas Leonard Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. opam-0install-cudf-0.5.0/Makefile0000644000175000017500000000006514657442125020166 0ustar kit_ty_katekit_ty_kate.PHONY: all test all: dune build test: dune test opam-0install-cudf-0.5.0/README.md0000644000175000017500000000215214657442125020004 0ustar kit_ty_katekit_ty_kate* [API docs](https://ocaml-opam.github.io/opam-0install-cudf) **NOTE: opam-0install-cudf previously resided at together with opam-0install at [ocaml-opam/opam-0install-solver](https://github.com/ocaml-opam/opam-0install-solver) but has now been split into its own repository at [ocaml-opam/opam-0install-cudf](https://github.com/ocaml-opam/opam-0install-cudf). The git history for both are the same, up until [d19d6fe7](https://github.com/kit-ty-kate/opam-0install-cudf/commit/d19d6fe797d565f4df38823d8bb3df616adb6411)** # Introduction Opam's default solver is designed to maintain a set of packages over time, minimising disruption when installing new programs and finding a compromise solution across all packages. In many situations (e.g. CI, local roots or duniverse builds) this is not necessary, and we can get a solution much faster by usin a different algorithm. This package provides a generic solver library which uses 0install's solver library. The library uses the [CUDF](https://gitlab.com/irill/cudf) library in order to interface with opam as it is the format common used to talk to all the supported solvers. opam-0install-cudf-0.5.0/dune-project0000644000175000017500000000015514657442125021050 0ustar kit_ty_katekit_ty_kate(lang dune 2.7) (name opam-0install-cudf) (version 0.5.0) (formatting disabled) (generate_opam_files false) opam-0install-cudf-0.5.0/opam-0install-cudf.opam0000644000175000017500000000237114657442125023005 0ustar kit_ty_katekit_ty_kateopam-version: "2.0" version: "0.5.0" synopsis: "Opam solver using 0install backend using the CUDF interface" description: """ Opam's default solver is designed to maintain a set of packages over time, minimising disruption when installing new programs and finding a compromise solution across all packages. In many situations (e.g. CI, local roots or duniverse builds) this is not necessary, and we can get a solution much faster by using a different algorithm. This package provides a generic solver library which uses 0install's solver library. The library uses the CUDF library in order to interface with opam as it is the format common used to talk to all the supported solvers. """ maintainer: "Kate " authors: "Kate " license: "ISC" homepage: "https://github.com/ocaml-opam/opam-0install-cudf" doc: "https://ocaml-opam.github.io/opam-0install-cudf/" bug-reports: "https://github.com/ocaml-opam/opam-0install-cudf/issues" depends: [ "dune" {>= "2.7"} "cudf" {>= "0.10"} "ocaml" {>= "4.08.0"} "0install-solver" {>= "2.18"} "alcotest" {with-test} ] build: ["dune" "build" "-p" name "-j" jobs] run-test: ["dune" "test" "-p" name "-j" jobs] dev-repo: "git+https://github.com/ocaml-opam/opam-0install-cudf.git" opam-0install-cudf-0.5.0/src/0000755000175000017500000000000014657442125017314 5ustar kit_ty_katekit_ty_kateopam-0install-cudf-0.5.0/src/dune0000644000175000017500000000015114657442125020167 0ustar kit_ty_katekit_ty_kate(library (name opam_0install_cudf) (public_name opam-0install-cudf) (libraries cudf 0install-solver)) opam-0install-cudf-0.5.0/src/model.ml0000644000175000017500000002053114657442125020747 0ustar kit_ty_katekit_ty_kate(* Note: changes to this file may require similar changes to lib/model.ml *) let fop : Cudf_types.relop -> int -> int -> bool = function | `Eq -> (=) | `Neq -> (<>) | `Geq -> (>=) | `Gt -> (>) | `Leq -> (<=) | `Lt -> (<) module Make (Context : S.CONTEXT) = struct type restriction = { kind : [ `Ensure | `Prevent ]; expr : (Cudf_types.relop * Cudf_types.version) list; (* TODO: might not be a list *) (* NOTE: each list is a raw or the list is an OR case (see Cudf_types.vpkgforula) *) } type real_role = { context : Context.t; name : Cudf_types.pkgname; } type role = | Real of real_role (* A role is usually an opam package name *) | Virtual of int * impl list (* (int just for sorting) *) and real_impl = { pkg : Cudf.package; requires : dependency list; } and dependency = { drole : role; importance : [ `Essential | `Recommended | `Restricts ]; restrictions : restriction list; } and impl = | RealImpl of real_impl (* An implementation is usually an opam package *) | VirtualImpl of int * dependency list (* (int just for sorting) *) | Reject of (Cudf_types.pkgname * Cudf_types.version) | Dummy (* Used for diagnostics *) let rec pp_version f = function | RealImpl impl -> Format.pp_print_int f impl.pkg.Cudf.version | Reject pkg -> Format.pp_print_int f (snd pkg) | VirtualImpl (_i, deps) -> Format.pp_print_string f (String.concat "&" (List.map (fun d -> Format.asprintf "%a" pp_role d.drole) deps)) | Dummy -> Format.pp_print_string f "(no version)" and pp_impl f = function | RealImpl impl -> Format.fprintf f "%s.%d" impl.pkg.Cudf.package impl.pkg.Cudf.version | Reject pkg -> Format.fprintf f "%s.%d" (fst pkg) (snd pkg) | VirtualImpl _ as x -> pp_version f x | Dummy -> Format.pp_print_string f "(no solution found)" and pp_role f = function | Real t -> Format.pp_print_string f t.name | Virtual (_, impls) -> Format.pp_print_string f (String.concat "|" (List.map (Format.asprintf "%a" pp_impl) impls)) let pp_impl_long = pp_impl module Role = struct type t = role let pp = pp_role let compare a b = match a, b with | Real a, Real b -> String.compare a.name b.name | Virtual (a, _), Virtual (b, _) -> compare (a : int) b | Real _, Virtual _ -> -1 | Virtual _, Real _ -> 1 end let role context name = Real { context; name } let virtual_impl ~context ~depends () = let depends = depends |> List.map (fun (name, importance) -> let drole = role context name in let importance = (importance :> [ `Essential | `Recommended | `Restricts ]) in { drole; importance; restrictions = []} ) in VirtualImpl (Context.fresh_id context, depends) let virtual_role ~context impls = Virtual (Context.fresh_id context, impls) type command = | (* We don't use 0install commands anywhere *) type command_name = private string let pp_command _ = function (_:command) -> . let command_requires _role = function (_:command) -> . let get_command _impl _command_name = None type dep_info = { dep_role : Role.t; dep_importance : [ `Essential | `Recommended | `Restricts ]; dep_required_commands : command_name list; } type requirements = { role : Role.t; command : command_name option; } let dummy_impl = Dummy (* Turn an CUDF formula into a 0install list of dependencies. *) let list_deps ~context ~importance ~kind ~pname ~pver deps = let rec aux = function | [] -> [] | [[(name, _)]] when String.equal name pname -> [] | [[(name, c)]] -> let drole = role context name in let restrictions = match kind, c with | `Prevent, None -> [{ kind; expr = [] }] | `Ensure, None -> [] | kind, Some c -> [{ kind; expr = [c] }] in [{ drole; restrictions; importance }] | x::(_::_ as y) -> aux [x] @ aux y | [o] -> let impls = group_ors o in let drole = virtual_role ~context impls in (* Essential because we must apply a restriction, even if its components are only restrictions. *) [{ drole; restrictions = []; importance = `Essential }] and group_ors = function | x::(_::_ as y) -> group_ors [x] @ group_ors y | [expr] -> [VirtualImpl (Context.fresh_id context, aux [[expr]])] | [] -> [Reject (pname, pver)] in aux deps let requires _ = function | Dummy | Reject _ -> [], [] | VirtualImpl (_, deps) -> deps, [] | RealImpl impl -> impl.requires, [] let dep_info { drole; importance; restrictions = _ } = { dep_role = drole; dep_importance = importance; dep_required_commands = [] } type role_information = { replacement : Role.t option; impls : impl list; } type machine_group = private string (* We don't use machine groups because opam is source-only. *) let machine_group _impl = None type conflict_class = string let conflict_class _impl = [] let prevent f = List.map (fun x -> [x]) f let ensure = Fun.id (* Get all the candidates for a role. *) let implementations = function | Virtual (_, impls) -> { impls; replacement = None } | Real role -> let context = role.context in let impls = Context.candidates context role.name |> List.filter_map (function | _, Error _rejection -> None | version, Ok pkg -> let requires = let make_deps importance kind xform deps = xform deps |> list_deps ~context ~importance ~kind ~pname:role.name ~pver:version in make_deps `Essential `Ensure ensure pkg.Cudf.depends @ make_deps `Restricts `Prevent prevent pkg.Cudf.conflicts in Some (RealImpl { pkg; requires }) ) in { impls; replacement = None } let restrictions dependency = dependency.restrictions let meets_restriction impl { kind; expr } = match impl with | Dummy -> true | VirtualImpl _ -> assert false (* Can't constrain version of a virtual impl! *) | Reject _ -> false | RealImpl impl -> let result = match expr with [] -> true | _ -> List.exists (fun (c, v) -> fop c impl.pkg.Cudf.version v) expr in match kind with | `Ensure -> result | `Prevent -> not result type rejection = Context.rejection let rejects role = match role with | Virtual _ -> [], [] | Real role -> let context = role.context in let rejects = Context.candidates context role.name |> List.filter_map (function | _, Ok _ -> None | version, Error reason -> let pkg = (role.name, version) in Some (Reject pkg, reason) ) in let notes = [] in rejects, notes let compare_version a b = match a, b with | RealImpl a, RealImpl b -> compare (a.pkg.Cudf.version : int) b.pkg.Cudf.version | VirtualImpl (ia, _), VirtualImpl (ib, _) -> compare (ia : int) ib | Reject a, Reject b -> compare (snd a : int) (snd b) | (RealImpl _ | Reject _ | VirtualImpl _ | Dummy), (RealImpl _ | Reject _ | VirtualImpl _ | Dummy) -> compare b a let user_restrictions = function | Virtual _ -> None | Real role -> match Context.user_restrictions role.context role.name with | [] -> None | f -> Some { kind = `Ensure; expr = f } let format_machine _impl = "(src)" let string_of_op = function | `Eq -> "=" | `Geq -> ">=" | `Gt -> ">" | `Leq -> "<=" | `Lt -> "<" | `Neq -> "<>" let string_of_version_formula f = String.concat " & " (List.map (fun (rel, v) -> Printf.sprintf "%s %s" (string_of_op rel) (string_of_int v) ) f) let string_of_restriction = function | { kind = `Prevent; expr = [] } -> "conflict with all versions" | { kind = `Prevent; expr } -> Format.asprintf "not(%s)" (string_of_version_formula expr) | { kind = `Ensure; expr } -> string_of_version_formula expr let describe_problem _impl = Format.asprintf "%a" Context.pp_rejection let version = function | RealImpl impl -> Some (impl.pkg.Cudf.package, impl.pkg.Cudf.version) | Reject pkg -> Some pkg | VirtualImpl _ -> None | Dummy -> None end opam-0install-cudf-0.5.0/src/model.mli0000644000175000017500000000330114657442125021114 0ustar kit_ty_katekit_ty_kate(** This module maps between the opam and 0install concepts. Roughly: - An opam package name is a 0install role. - An opam package is a 0install implementation. - An opam version formula is a 0install restriction. For dependencies: - depends become "essential" dependencies - depopts are ignored (the opam solver ignores them too; they don't have constraints) - conflicts become "restricts" (with the test reversed) Dependencies on alternatives (e.g. "ocaml-base-compiler | ocaml-variants") become a dependency on a virtual package which has each choice as an implementation. *) val fop : Cudf_types.relop -> int -> int -> bool module Make (Context : S.CONTEXT) : sig include Zeroinstall_solver.S.SOLVER_INPUT val role : Context.t -> Cudf_types.pkgname -> Role.t val version : impl -> (Cudf_types.pkgname * Cudf_types.version) option (** [version impl] is the Opam package for [impl], if any. Virtual and dummy implementations return [None]. *) val virtual_role : context:Context.t -> impl list -> Role.t (** [virtual_role impls] is a virtual package name with candidates [impls]. This is used if the user requests multiple packages on the command line (the single [impl] will also be virtual). *) val virtual_impl : context:Context.t -> depends:(Cudf_types.pkgname * [`Essential | `Recommended]) list -> unit -> impl (** [virtual_impl ~context ~depends] is a virtual package which just depends on [depends]. This is used if the user requests multiple packages on the command line - each requested package becomes a dependency of the virtual implementation according to their associated requirement tag. *) end opam-0install-cudf-0.5.0/src/opam_0install_cudf.ml0000644000175000017500000001005114657442125023406 0ustar kit_ty_katekit_ty_katelet tagged_with_avoid_version pkg = List.exists (function | "avoid-version", (`Int 1 | `Bool true) -> true | _ -> false ) pkg.Cudf.pkg_extra let version_rev_compare ~prefer_oldest ~handle_avoid_version ~prefer_installed = (* cmp ordered from least important to most important setting *) let cmp = if prefer_oldest then fun pkg1 pkg2 -> Int.compare pkg1.Cudf.version pkg2.Cudf.version else fun pkg1 pkg2 -> Int.compare pkg2.Cudf.version pkg1.Cudf.version in let cmp = if handle_avoid_version then fun pkg1 pkg2 -> match tagged_with_avoid_version pkg1, tagged_with_avoid_version pkg2 with | true, true | false, false -> cmp pkg1 pkg2 | true, false when pkg1.Cudf.installed -> cmp pkg1 pkg2 | false, true when pkg2.Cudf.installed -> cmp pkg1 pkg2 | true, false -> 1 | false, true -> -1 else cmp in let cmp = if prefer_installed then fun pkg1 pkg2 -> match pkg1.Cudf.installed, pkg2.Cudf.installed with | true, true | false, false -> cmp pkg1 pkg2 | true, false -> -1 | false, true -> 1 else cmp in cmp module Context = struct type rejection = UserConstraint of Cudf_types.vpkg type t = { universe : Cudf.universe; constraints : (Cudf_types.pkgname * (Cudf_types.relop * Cudf_types.version)) list; fresh_id : int ref; version_rev_compare : Cudf.package -> Cudf.package -> int; } let user_restrictions t name = List.fold_left (fun acc (name', c) -> if String.equal name name' then c :: acc else acc ) [] t.constraints let candidates t name = let user_constraints = user_restrictions t name in match Cudf.lookup_packages t.universe name with | [] -> [] (* Package not found *) | versions -> List.fast_sort t.version_rev_compare versions (* Higher versions are preferred. *) |> List.map (fun pkg -> let rec check_constr = function | [] -> (pkg.Cudf.version, Ok pkg) | ((op, v)::c) -> if Model.fop op pkg.Cudf.version v then check_constr c else (pkg.Cudf.version, Error (UserConstraint (name, Some (op, v)))) (* Reject *) in check_constr user_constraints ) let print_constr = function | None -> "" | Some (`Eq, v) -> "="^string_of_int v | Some (`Neq, v) -> "!="^string_of_int v | Some (`Geq, v) -> ">="^string_of_int v | Some (`Gt, v) -> ">"^string_of_int v | Some (`Leq, v) -> "<="^string_of_int v | Some (`Lt, v) -> "<"^string_of_int v let pp_rejection f = function | UserConstraint (name, c) -> Format.fprintf f "Rejected by user-specified constraint %s%s" name (print_constr c) let fresh_id {fresh_id; _} = incr fresh_id; !fresh_id end module Input = Model.Make(Context) let requirements ~context pkgs = let role = let impl = Input.virtual_impl ~context ~depends:pkgs () in Input.virtual_role ~context [impl] in { Input.role; command = None } module Solver = Zeroinstall_solver.Make(Input) module Diagnostics = Zeroinstall_solver.Diagnostics(Solver.Output) type t = Context.t type selections = Solver.Output.t type diagnostics = Input.requirements (* So we can run another solve *) let create ?(prefer_oldest=false) ?(handle_avoid_version=true) ?(prefer_installed=false) ~constraints universe = { Context.universe; constraints; fresh_id = ref 0; version_rev_compare = version_rev_compare ~prefer_oldest ~handle_avoid_version ~prefer_installed; } let solve context pkgs = let req = requirements ~context pkgs in match Solver.do_solve ~closest_match:false req with | Some sels -> Ok sels | None -> Error req let diagnostics ?verbose req = Solver.do_solve req ~closest_match:true |> Option.get |> Diagnostics.get_failure_reason ?verbose let packages_of_result sels = sels |> Solver.Output.to_map |> Solver.Output.RoleMap.to_seq |> List.of_seq |> List.filter_map (fun (_role, sel) -> Input.version (Solver.Output.unwrap sel)) opam-0install-cudf-0.5.0/src/opam_0install_cudf.mli0000644000175000017500000000361614657442125023570 0ustar kit_ty_katekit_ty_katetype t type selections type diagnostics val create : ?prefer_oldest:bool -> ?handle_avoid_version:bool -> ?prefer_installed:bool -> constraints:(Cudf_types.pkgname * (Cudf_types.relop * Cudf_types.version)) list -> Cudf.universe -> t (** [create ~constraints universe] is a solver that gets candidates from [universe], filtering them using [constraints]. @param prefer_oldest if [true] the solver is set to return the least up-to-date version of each package, if a solution exists. This is [false] by default. @before 0.4 the [prefer_oldest] parameter did not exist. @param handle_avoid_version if [true] the solver will try to avoid packages containing the [("avoid-version", `Int 1)] or [("avoid-version", `Bool true)] extra property (see {!Cudf.pkg_extra}). However, if a package both has this property and is installed, the solver will do as if the package didn't have the [avoid-version] property. This is [true] by default. @before 0.5 the [handle_avoid_version] parameter did not exist. @param prefer_installed if [true] the solver will try to prioritize keeping the versions of packages installed at their current version instead of the latest possible version. This is [false] by default. @before 0.5 the [prefer_installed] parameter did not exist. *) val solve : t -> (Cudf_types.pkgname * [`Essential | `Recommended]) list -> (selections, diagnostics) result (** [solve t packages] finds a compatible set of package versions that includes all packages in [packages] according to their requirement tag, and their required dependencies if needed. *) val packages_of_result : selections -> (Cudf_types.pkgname * Cudf_types.version) list val diagnostics : ?verbose:bool -> diagnostics -> string (** [diagnostics d] is a message explaining why [d] failed, generated by performing another solve which doesn't abort on failure. *) opam-0install-cudf-0.5.0/src/s.ml0000644000175000017500000000174414657442125020116 0ustar kit_ty_katekit_ty_katemodule type CONTEXT = sig type t type rejection (** A reason why a package can't be used as input to the solver. e.g. it is for a different platform, or conflicts with a user-provided constraint. *) val pp_rejection : Format.formatter -> rejection -> unit val candidates : t -> Cudf_types.pkgname -> (Cudf_types.version * (Cudf.package, rejection) result) list (** [candidates t name] is the list of available versions of [name], in order of decreasing preference. If the user or environment provides additional constraints that mean a version should be rejected, include that here too. Rejects are only used for generating diagnostics reports. *) val user_restrictions : t -> Cudf_types.pkgname -> (Cudf_types.relop * Cudf_types.version) list (** [user_restrictions t pkg] is the user's constraint on [pkg], if any. This is just used for diagnostics; you still have to filter them out yourself in [candidates]. *) val fresh_id : t -> int end opam-0install-cudf-0.5.0/test/0000755000175000017500000000000014657442125017504 5ustar kit_ty_katekit_ty_kateopam-0install-cudf-0.5.0/test/dune0000644000175000017500000000013314657442125020357 0ustar kit_ty_katekit_ty_kate(test (name test) (package opam-0install-cudf) (libraries alcotest opam-0install-cudf)) opam-0install-cudf-0.5.0/test/test.ml0000644000175000017500000001416614657442125021025 0ustar kit_ty_katekit_ty_katelet universe = Cudf.load_universe [ {Cudf.default_package with package = "a"; version = 1}; {Cudf.default_package with package = "a"; version = 2}; {Cudf.default_package with package = "a"; version = 3}; {Cudf.default_package with package = "a"; version = 4}; {Cudf.default_package with package = "b"; version = 1}; {Cudf.default_package with package = "b"; version = 2; pkg_extra = [("avoid-version", `Int 1)]}; {Cudf.default_package with package = "b"; version = 3; pkg_extra = [("avoid-version", `Int 0)]}; {Cudf.default_package with package = "b"; version = 4}; {Cudf.default_package with package = "c"; version = 1; pkg_extra = [("avoid-version", `Int 1)]}; {Cudf.default_package with package = "c"; version = 2}; {Cudf.default_package with package = "c"; version = 3}; {Cudf.default_package with package = "c"; version = 4; pkg_extra = [("avoid-version", `Int 0)]}; {Cudf.default_package with package = "d"; version = 1; pkg_extra = [("avoid-version", `Int 0)]}; {Cudf.default_package with package = "d"; version = 2}; {Cudf.default_package with package = "d"; version = 3}; {Cudf.default_package with package = "d"; version = 4; pkg_extra = [("avoid-version", `Int 1)]}; {Cudf.default_package with package = "e"; version = 1}; {Cudf.default_package with package = "e"; version = 2; installed = true}; {Cudf.default_package with package = "e"; version = 3}; {Cudf.default_package with package = "e"; version = 4}; {Cudf.default_package with package = "f"; version = 1}; {Cudf.default_package with package = "f"; version = 2}; {Cudf.default_package with package = "f"; version = 3}; {Cudf.default_package with package = "f"; version = 4; installed = true; pkg_extra = [("avoid-version", `Int 1)]}; {Cudf.default_package with package = "g"; version = 1}; {Cudf.default_package with package = "g"; version = 2}; {Cudf.default_package with package = "g"; version = 3; installed = true; pkg_extra = [("avoid-version", `Int 1)]}; {Cudf.default_package with package = "g"; version = 4}; ] let solve ?prefer_oldest ?prefer_installed req = let x = Opam_0install_cudf.create ?prefer_oldest ?prefer_installed ~constraints:[] universe in match Opam_0install_cudf.solve x req with | Ok sel -> Ok (Opam_0install_cudf.packages_of_result sel) | Error diag -> Error (Opam_0install_cudf.diagnostics ~verbose:true diag) let simple_solve () = Alcotest.(check (result (list (pair string int)) string)) "equal" (Ok [("a", 4)]) (solve [("a", `Essential)]) let simple_oldest () = Alcotest.(check (result (list (pair string int)) string)) "equal" (Ok [("a", 1)]) (solve ~prefer_oldest:true [("a", `Essential)]) let simple_avoid_1 () = Alcotest.(check (result (list (pair string int)) string)) "equal" (Ok [("b", 4)]) (solve [("b", `Essential)]) let oldest_avoid_1 () = Alcotest.(check (result (list (pair string int)) string)) "equal" (Ok [("b", 1)]) (solve ~prefer_oldest:true [("b", `Essential)]) let simple_avoid_2 () = Alcotest.(check (result (list (pair string int)) string)) "equal" (Ok [("c", 4)]) (solve [("c", `Essential)]) let oldest_avoid_2 () = Alcotest.(check (result (list (pair string int)) string)) "equal" (Ok [("c", 2)]) (solve ~prefer_oldest:true [("c", `Essential)]) let simple_avoid_3 () = Alcotest.(check (result (list (pair string int)) string)) "equal" (Ok [("d", 3)]) (solve [("d", `Essential)]) let oldest_avoid_3 () = Alcotest.(check (result (list (pair string int)) string)) "equal" (Ok [("d", 1)]) (solve ~prefer_oldest:true [("d", `Essential)]) let prefer_installed_1 () = Alcotest.(check (result (list (pair string int)) string)) "equal" (Ok [("e", 2)]) (solve ~prefer_installed:true [("e", `Essential)]) let prefer_installed_2 () = Alcotest.(check (result (list (pair string int)) string)) "equal" (Ok [("e", 2)]) (solve ~prefer_installed:true [("e", `Recommended)]) let prefer_installed_3 () = Alcotest.(check (result (list (pair string int)) string)) "equal" (Ok [("e", 4)]) (solve [("e", `Essential)]) let prefer_installed_4 () = Alcotest.(check (result (list (pair string int)) string)) "equal" (Ok [("e", 4)]) (solve [("e", `Recommended)]) let prefer_installed_5 () = Alcotest.(check (result (list (pair string int)) string)) "equal" (Ok [("f", 4)]) (solve ~prefer_installed:true [("f", `Essential)]) let prefer_installed_6 () = Alcotest.(check (result (list (pair string int)) string)) "equal" (Ok [("f", 4)]) (solve [("f", `Essential)]) let prefer_installed_7 () = Alcotest.(check (result (list (pair string int)) string)) "equal" (Ok [("g", 3)]) (solve ~prefer_installed:true [("g", `Essential)]) let prefer_installed_8 () = Alcotest.(check (result (list (pair string int)) string)) "equal" (Ok [("g", 4)]) (solve [("g", `Essential)]) let () = Alcotest.run "cudf" [ ( "simple solve", [ Alcotest.test_case "normal" `Quick simple_solve; Alcotest.test_case "oldest" `Quick simple_oldest; ] ); ( "avoid-version", [ Alcotest.test_case "normal 1" `Quick simple_avoid_1; Alcotest.test_case "oldest 1" `Quick oldest_avoid_2; Alcotest.test_case "normal 2" `Quick simple_avoid_2; Alcotest.test_case "oldest 2" `Quick oldest_avoid_2; Alcotest.test_case "normal 3" `Quick simple_avoid_3; Alcotest.test_case "oldest 3" `Quick oldest_avoid_3; ] ); ( "keep-installed", [ Alcotest.test_case "normal 1" `Quick prefer_installed_1; Alcotest.test_case "normal 2" `Quick prefer_installed_2; Alcotest.test_case "normal 3" `Quick prefer_installed_3; Alcotest.test_case "normal 4" `Quick prefer_installed_4; Alcotest.test_case "latest avoid-version=1" `Quick prefer_installed_5; Alcotest.test_case "latest keep-installed=0, avoid-version=1" `Quick prefer_installed_6; Alcotest.test_case "avoid-version=1" `Quick prefer_installed_7; Alcotest.test_case "keep-installed=0, avoid-version=1" `Quick prefer_installed_8; ] ); ]