···1+module Store = Git_unix.Store
2+module Search = Git.Search.Make (Digestif.SHA1) (Store)
3+open Lwt.Infix
4+5+type rejection =
6+ | UserConstraint of OpamFormula.atom
7+ | Unavailable
8+9+type t = {
10+ env : string -> OpamVariable.variable_contents option;
11+ packages : OpamFile.OPAM.t OpamPackage.Version.Map.t OpamPackage.Name.Map.t;
12+ pins : (OpamPackage.Version.t * OpamFile.OPAM.t) OpamPackage.Name.Map.t;
13+ constraints : OpamFormula.version_constraint OpamTypes.name_map;
14+ (* User-provided constraints *)
15+ test : OpamPackage.Name.Set.t;
16+}
17+18+let user_restrictions t name = OpamPackage.Name.Map.find_opt name t.constraints
19+let dev = OpamPackage.Version.of_string "dev"
20+21+let env t pkg v =
22+ if List.mem v OpamPackageVar.predefined_depends_variables then None
23+ else
24+ match OpamVariable.Full.to_string v with
25+ | "version" -> Some (OpamTypes.S (OpamPackage.version_to_string pkg))
26+ | x -> t.env x
27+28+let filter_deps t pkg f =
29+ let dev = OpamPackage.Version.compare (OpamPackage.version pkg) dev = 0 in
30+ let test = OpamPackage.Name.Set.mem (OpamPackage.name pkg) t.test in
31+ f |> OpamFilter.partial_filter_formula (env t pkg) |> OpamFilter.filter_deps ~build:true ~post:true ~test ~doc:false ~dev ~dev_setup:false ~default:false
32+33+let candidates t name =
34+ match OpamPackage.Name.Map.find_opt name t.pins with
35+ | Some (version, opam) -> [ (version, Ok opam) ]
36+ | None -> (
37+ match OpamPackage.Name.Map.find_opt name t.packages with
38+ | None ->
39+ OpamConsole.log "opam-0install" "Package %S not found!" (OpamPackage.Name.to_string name);
40+ []
41+ | Some versions ->
42+ let user_constraints = user_restrictions t name in
43+ OpamPackage.Version.Map.bindings versions
44+ |> List.rev_map (fun (v, opam) ->
45+ match user_constraints with
46+ | Some test when not (OpamFormula.check_version_formula (OpamFormula.Atom test) v) -> (v, Error (UserConstraint (name, Some test)))
47+ | _ -> (
48+ let pkg = OpamPackage.create name v in
49+ let available = OpamFile.OPAM.available opam in
50+ match OpamFilter.eval ~default:(B false) (env t pkg) available with
51+ | B true -> (v, Ok opam)
52+ | B false -> (v, Error Unavailable)
53+ | _ ->
54+ OpamConsole.error "Available expression not a boolean: %s" (OpamFilter.to_string available);
55+ (v, Error Unavailable))))
56+57+let pp_rejection f = function
58+ | UserConstraint x -> Fmt.pf f "Rejected by user-specified constraint %s" (OpamFormula.string_of_atom x)
59+ | Unavailable -> Fmt.string f "Availability condition not satisfied"
60+61+let read_dir store hash =
62+ Store.read store hash >|= function
63+ | Error e -> Fmt.failwith "Failed to read tree: %a" Store.pp_error e
64+ | Ok (Git.Value.Tree tree) -> Some tree
65+ | Ok _ -> None
66+67+let read_package store pkg hash =
68+ Search.find store hash (`Path [ "opam" ]) >>= function
69+ | None -> Fmt.failwith "opam file not found for %s" (OpamPackage.to_string pkg)
70+ | Some hash -> (
71+ Store.read store hash >|= function
72+ | Ok (Git.Value.Blob blob) -> OpamFile.OPAM.read_from_string (Store.Value.Blob.to_string blob)
73+ | _ -> Fmt.failwith "Bad Git object type for %s!" (OpamPackage.to_string pkg))
74+75+(* Get a map of the versions inside [entry] (an entry under "packages") *)
76+let read_versions store (entry : Store.Value.Tree.entry) =
77+ read_dir store entry.node >>= function
78+ | None -> Lwt.return_none
79+ | Some tree ->
80+ Store.Value.Tree.to_list tree
81+ |> Lwt_list.fold_left_s
82+ (fun acc (entry : Store.Value.Tree.entry) ->
83+ match OpamPackage.of_string_opt entry.name with
84+ | Some pkg -> read_package store pkg entry.node >|= fun opam -> OpamPackage.Version.Map.add pkg.version opam acc
85+ | None ->
86+ OpamConsole.log "opam-0install" "Invalid package name %S" entry.name;
87+ Lwt.return acc)
88+ OpamPackage.Version.Map.empty
89+ >|= fun versions -> Some versions
90+91+let read_packages store commit =
92+ Search.find store commit (`Commit (`Path [ "packages" ])) >>= function
93+ | None -> Fmt.failwith "Failed to find packages directory!"
94+ | Some tree_hash -> (
95+ read_dir store tree_hash >>= function
96+ | None -> Fmt.failwith "'packages' is not a directory!"
97+ | Some tree ->
98+ Store.Value.Tree.to_list tree
99+ |> Lwt_list.fold_left_s
100+ (fun acc (entry : Store.Value.Tree.entry) ->
101+ match OpamPackage.Name.of_string entry.name with
102+ | exception ex ->
103+ OpamConsole.log "opam-0install" "Invalid package name %S: %s" entry.name (Printexc.to_string ex);
104+ Lwt.return acc
105+ | name -> (
106+ read_versions store entry >|= function
107+ | None -> acc
108+ | Some versions -> OpamPackage.Name.Map.add name versions acc))
109+ OpamPackage.Name.Map.empty)
110+111+let create ?(test = OpamPackage.Name.Set.empty) ?(pins = OpamPackage.Name.Map.empty) ~constraints ~env ~packages () = { env; packages; pins; constraints; test }
+13
bin/git_context.mli
···0000000000000
···1+include Opam_0install.S.CONTEXT
2+3+val read_packages : Git_unix.Store.t -> Git_unix.Store.Hash.t -> OpamFile.OPAM.t OpamPackage.Version.Map.t OpamPackage.Name.Map.t Lwt.t
4+(** [read_packages store commit] is an index of the opam files in [store] at [commit]. *)
5+6+val create :
7+ ?test:OpamPackage.Name.Set.t ->
8+ ?pins:(OpamPackage.Version.t * OpamFile.OPAM.t) OpamPackage.Name.Map.t ->
9+ constraints:OpamFormula.version_constraint OpamPackage.Name.Map.t ->
10+ env:(string -> OpamVariable.variable_contents option) ->
11+ packages:OpamFile.OPAM.t OpamPackage.Version.Map.t OpamPackage.Name.Map.t ->
12+ unit ->
13+ t
+21
bin/main.ml
···000000000000000000000
···1+module Solver = Opam_0install.Solver.Make (Opam_0install.Switch_context)
2+3+let constraints = OpamPackage.Name.Map.of_list [ (OpamPackage.Name.of_string "ocaml", (`Eq, OpamPackage.Version.of_string "5.3.0")) ]
4+let pp_pkg = Fmt.of_to_string OpamPackage.to_string
5+6+let _ =
7+ let root = OpamStateConfig.opamroot () in
8+ OpamFormatConfig.init ();
9+ ignore (OpamStateConfig.load_defaults root);
10+ OpamCoreConfig.init ();
11+ OpamStateConfig.init ();
12+ OpamGlobalState.with_ `Lock_none @@ fun gt ->
13+ OpamSwitchState.with_ `Lock_none gt @@ fun st ->
14+ let context = Opam_0install.Switch_context.create ~constraints st in
15+ let r = Solver.solve context [ OpamPackage.Name.of_string "obuilder" ] in
16+ match r with
17+ | Ok sels -> Fmt.pr "%a@." Fmt.(list ~sep:(any " ") pp_pkg) (Solver.packages_of_result sels)
18+ | Error problem ->
19+ OpamConsole.error "No solution";
20+ print_endline (Solver.diagnostics problem);
21+ ()
+31
day10.opam
···0000000000000000000000000000000
···1+# This file is generated by dune, edit dune-project instead
2+opam-version: "2.0"
3+synopsis: "A short synopsis"
4+description: "A longer description"
5+maintainer: ["Maintainer Name <maintainer@example.com>"]
6+authors: ["Author Name <author@example.com>"]
7+license: "LICENSE"
8+tags: ["add topics" "to describe" "your" "project"]
9+homepage: "https://github.com/username/reponame"
10+doc: "https://url/to/documentation"
11+bug-reports: "https://github.com/username/reponame/issues"
12+depends: [
13+ "dune" {>= "3.17"}
14+ "ocaml"
15+ "odoc" {with-doc}
16+]
17+build: [
18+ ["dune" "subst"] {dev}
19+ [
20+ "dune"
21+ "build"
22+ "-p"
23+ name
24+ "-j"
25+ jobs
26+ "@install"
27+ "@runtest" {with-test}
28+ "@doc" {with-doc}
29+ ]
30+]
31+dev-repo: "git+https://github.com/username/reponame.git"
+26
dune-project
···00000000000000000000000000
···1+(lang dune 3.17)
2+3+(name day10)
4+5+(generate_opam_files true)
6+7+(source
8+ (github username/reponame))
9+10+(authors "Author Name <author@example.com>")
11+12+(maintainers "Maintainer Name <maintainer@example.com>")
13+14+(license LICENSE)
15+16+(documentation https://url/to/documentation)
17+18+(package
19+ (name day10)
20+ (synopsis "A short synopsis")
21+ (description "A longer description")
22+ (depends ocaml)
23+ (tags
24+ ("add topics" "to describe" your project)))
25+26+; See the complete stanza docs at https://dune.readthedocs.io/en/stable/reference/dune-project/index.html