···11+module Store = Git_unix.Store
22+module Search = Git.Search.Make (Digestif.SHA1) (Store)
33+open Lwt.Infix
44+55+type rejection =
66+ | UserConstraint of OpamFormula.atom
77+ | Unavailable
88+99+type t = {
1010+ env : string -> OpamVariable.variable_contents option;
1111+ packages : OpamFile.OPAM.t OpamPackage.Version.Map.t OpamPackage.Name.Map.t;
1212+ pins : (OpamPackage.Version.t * OpamFile.OPAM.t) OpamPackage.Name.Map.t;
1313+ constraints : OpamFormula.version_constraint OpamTypes.name_map;
1414+ (* User-provided constraints *)
1515+ test : OpamPackage.Name.Set.t;
1616+}
1717+1818+let user_restrictions t name = OpamPackage.Name.Map.find_opt name t.constraints
1919+let dev = OpamPackage.Version.of_string "dev"
2020+2121+let env t pkg v =
2222+ if List.mem v OpamPackageVar.predefined_depends_variables then None
2323+ else
2424+ match OpamVariable.Full.to_string v with
2525+ | "version" -> Some (OpamTypes.S (OpamPackage.version_to_string pkg))
2626+ | x -> t.env x
2727+2828+let filter_deps t pkg f =
2929+ let dev = OpamPackage.Version.compare (OpamPackage.version pkg) dev = 0 in
3030+ let test = OpamPackage.Name.Set.mem (OpamPackage.name pkg) t.test in
3131+ f |> OpamFilter.partial_filter_formula (env t pkg) |> OpamFilter.filter_deps ~build:true ~post:true ~test ~doc:false ~dev ~dev_setup:false ~default:false
3232+3333+let candidates t name =
3434+ match OpamPackage.Name.Map.find_opt name t.pins with
3535+ | Some (version, opam) -> [ (version, Ok opam) ]
3636+ | None -> (
3737+ match OpamPackage.Name.Map.find_opt name t.packages with
3838+ | None ->
3939+ OpamConsole.log "opam-0install" "Package %S not found!" (OpamPackage.Name.to_string name);
4040+ []
4141+ | Some versions ->
4242+ let user_constraints = user_restrictions t name in
4343+ OpamPackage.Version.Map.bindings versions
4444+ |> List.rev_map (fun (v, opam) ->
4545+ match user_constraints with
4646+ | Some test when not (OpamFormula.check_version_formula (OpamFormula.Atom test) v) -> (v, Error (UserConstraint (name, Some test)))
4747+ | _ -> (
4848+ let pkg = OpamPackage.create name v in
4949+ let available = OpamFile.OPAM.available opam in
5050+ match OpamFilter.eval ~default:(B false) (env t pkg) available with
5151+ | B true -> (v, Ok opam)
5252+ | B false -> (v, Error Unavailable)
5353+ | _ ->
5454+ OpamConsole.error "Available expression not a boolean: %s" (OpamFilter.to_string available);
5555+ (v, Error Unavailable))))
5656+5757+let pp_rejection f = function
5858+ | UserConstraint x -> Fmt.pf f "Rejected by user-specified constraint %s" (OpamFormula.string_of_atom x)
5959+ | Unavailable -> Fmt.string f "Availability condition not satisfied"
6060+6161+let read_dir store hash =
6262+ Store.read store hash >|= function
6363+ | Error e -> Fmt.failwith "Failed to read tree: %a" Store.pp_error e
6464+ | Ok (Git.Value.Tree tree) -> Some tree
6565+ | Ok _ -> None
6666+6767+let read_package store pkg hash =
6868+ Search.find store hash (`Path [ "opam" ]) >>= function
6969+ | None -> Fmt.failwith "opam file not found for %s" (OpamPackage.to_string pkg)
7070+ | Some hash -> (
7171+ Store.read store hash >|= function
7272+ | Ok (Git.Value.Blob blob) -> OpamFile.OPAM.read_from_string (Store.Value.Blob.to_string blob)
7373+ | _ -> Fmt.failwith "Bad Git object type for %s!" (OpamPackage.to_string pkg))
7474+7575+(* Get a map of the versions inside [entry] (an entry under "packages") *)
7676+let read_versions store (entry : Store.Value.Tree.entry) =
7777+ read_dir store entry.node >>= function
7878+ | None -> Lwt.return_none
7979+ | Some tree ->
8080+ Store.Value.Tree.to_list tree
8181+ |> Lwt_list.fold_left_s
8282+ (fun acc (entry : Store.Value.Tree.entry) ->
8383+ match OpamPackage.of_string_opt entry.name with
8484+ | Some pkg -> read_package store pkg entry.node >|= fun opam -> OpamPackage.Version.Map.add pkg.version opam acc
8585+ | None ->
8686+ OpamConsole.log "opam-0install" "Invalid package name %S" entry.name;
8787+ Lwt.return acc)
8888+ OpamPackage.Version.Map.empty
8989+ >|= fun versions -> Some versions
9090+9191+let read_packages store commit =
9292+ Search.find store commit (`Commit (`Path [ "packages" ])) >>= function
9393+ | None -> Fmt.failwith "Failed to find packages directory!"
9494+ | Some tree_hash -> (
9595+ read_dir store tree_hash >>= function
9696+ | None -> Fmt.failwith "'packages' is not a directory!"
9797+ | Some tree ->
9898+ Store.Value.Tree.to_list tree
9999+ |> Lwt_list.fold_left_s
100100+ (fun acc (entry : Store.Value.Tree.entry) ->
101101+ match OpamPackage.Name.of_string entry.name with
102102+ | exception ex ->
103103+ OpamConsole.log "opam-0install" "Invalid package name %S: %s" entry.name (Printexc.to_string ex);
104104+ Lwt.return acc
105105+ | name -> (
106106+ read_versions store entry >|= function
107107+ | None -> acc
108108+ | Some versions -> OpamPackage.Name.Map.add name versions acc))
109109+ OpamPackage.Name.Map.empty)
110110+111111+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
···11+include Opam_0install.S.CONTEXT
22+33+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
44+(** [read_packages store commit] is an index of the opam files in [store] at [commit]. *)
55+66+val create :
77+ ?test:OpamPackage.Name.Set.t ->
88+ ?pins:(OpamPackage.Version.t * OpamFile.OPAM.t) OpamPackage.Name.Map.t ->
99+ constraints:OpamFormula.version_constraint OpamPackage.Name.Map.t ->
1010+ env:(string -> OpamVariable.variable_contents option) ->
1111+ packages:OpamFile.OPAM.t OpamPackage.Version.Map.t OpamPackage.Name.Map.t ->
1212+ unit ->
1313+ t
+21
bin/main.ml
···11+module Solver = Opam_0install.Solver.Make (Opam_0install.Switch_context)
22+33+let constraints = OpamPackage.Name.Map.of_list [ (OpamPackage.Name.of_string "ocaml", (`Eq, OpamPackage.Version.of_string "5.3.0")) ]
44+let pp_pkg = Fmt.of_to_string OpamPackage.to_string
55+66+let _ =
77+ let root = OpamStateConfig.opamroot () in
88+ OpamFormatConfig.init ();
99+ ignore (OpamStateConfig.load_defaults root);
1010+ OpamCoreConfig.init ();
1111+ OpamStateConfig.init ();
1212+ OpamGlobalState.with_ `Lock_none @@ fun gt ->
1313+ OpamSwitchState.with_ `Lock_none gt @@ fun st ->
1414+ let context = Opam_0install.Switch_context.create ~constraints st in
1515+ let r = Solver.solve context [ OpamPackage.Name.of_string "obuilder" ] in
1616+ match r with
1717+ | Ok sels -> Fmt.pr "%a@." Fmt.(list ~sep:(any " ") pp_pkg) (Solver.packages_of_result sels)
1818+ | Error problem ->
1919+ OpamConsole.error "No solution";
2020+ print_endline (Solver.diagnostics problem);
2121+ ()
+31
day10.opam
···11+# This file is generated by dune, edit dune-project instead
22+opam-version: "2.0"
33+synopsis: "A short synopsis"
44+description: "A longer description"
55+maintainer: ["Maintainer Name <maintainer@example.com>"]
66+authors: ["Author Name <author@example.com>"]
77+license: "LICENSE"
88+tags: ["add topics" "to describe" "your" "project"]
99+homepage: "https://github.com/username/reponame"
1010+doc: "https://url/to/documentation"
1111+bug-reports: "https://github.com/username/reponame/issues"
1212+depends: [
1313+ "dune" {>= "3.17"}
1414+ "ocaml"
1515+ "odoc" {with-doc}
1616+]
1717+build: [
1818+ ["dune" "subst"] {dev}
1919+ [
2020+ "dune"
2121+ "build"
2222+ "-p"
2323+ name
2424+ "-j"
2525+ jobs
2626+ "@install"
2727+ "@runtest" {with-test}
2828+ "@doc" {with-doc}
2929+ ]
3030+]
3131+dev-repo: "git+https://github.com/username/reponame.git"
+26
dune-project
···11+(lang dune 3.17)
22+33+(name day10)
44+55+(generate_opam_files true)
66+77+(source
88+ (github username/reponame))
99+1010+(authors "Author Name <author@example.com>")
1111+1212+(maintainers "Maintainer Name <maintainer@example.com>")
1313+1414+(license LICENSE)
1515+1616+(documentation https://url/to/documentation)
1717+1818+(package
1919+ (name day10)
2020+ (synopsis "A short synopsis")
2121+ (description "A longer description")
2222+ (depends ocaml)
2323+ (tags
2424+ ("add topics" "to describe" your project)))
2525+2626+; See the complete stanza docs at https://dune.readthedocs.io/en/stable/reference/dune-project/index.html