Module rlx_depsolver

This is a dependency constraint solver.

Authors: Eric Merritt (ericbmerritt@gmail.com).

Description

This is a dependency constraint solver. You add your 'world' to the solver. That is the packages that exist, their versions and their dependencies. Then give the system a set of targets and ask it to solve.

Lets say our world looks as follows

app1 that has versions "0.1" depends on app3 any version greater then "0.2" "0.2" with no dependencies "0.3" with no dependencies

app2 that has versions "0.1" with no dependencies "0.2" that depends on app3 exactly "0.3" "0.3" with no dependencies

app3 that has versions "0.1", "0.2" and "0.3" all with no dependencies

we can add this world to the system all at once as follows

Graph0 = rlx_depsolver:new_graph(), Graph1 = rlx_depsolver:add_packages( [{app1, [{"0.1", [{app2, "0.2"}, {app3, "0.2", '>='}]}, {"0.2", []}, {"0.3", []}]}, {app2, [{"0.1", []}, {"0.2",[{app3, "0.3"}]}, {"0.3", []}]}, {app3, [{"0.1", []}, {"0.2", []}, {"0.3", []}]}]).

We can also build it up incrementally using the other add_package and add_package_version functions.

Finally, once we have built up the graph we can ask rlx_depsolver to solve the dependency constraints. That is to give us a list of valid dependencies by using the solve function. Lets say we want the app3 version "0.3" and all of its resolved dependencies. We could call solve as follows.

rlx_depsolver:solve(Graph1, [{app3, "0.3"}]).

That will give us the completely resolved dependencies including app3 itself. Lets be a little more flexible. Lets ask for a graph that is rooted in anything greater then or equal to app3 "0.3". We could do that by

rlx_depsolver:solve(Graph1, [{app3, "0.3", '>='}]).

Of course, you can specify any number of goals at the top level.

Data Types

constraint()

constraint() = pkg_name() | {pkg_name(), vsn()} | {pkg_name(), vsn(), constraint_op()} | {pkg_name(), vsn(), vsn(), between}

constraint_op()

constraint_op() = '=' | gte | '>=' | lte | '<=' | gt | '>' | lt | '<' | pes | '~>' | between

constraints()

constraints() = [constraint()]

dep_graph()

dep_graph() = gb_trees:tree()

dependency_set()

dependency_set() = {pkg_name(), [vsn_constraint()]}

fail_detail()

fail_detail() = {fail, [fail_info()]}

fail_info()

fail_info() = {[pkg()], ordered_constraints()}

ordered_constraints()

ordered_constraints() = [{pkg_name(), constraints()}]

pkg()

pkg() = {pkg_name(), vsn()}

pkg_name()

pkg_name() = string() | binary() | atom()

raw_constraint()

raw_constraint() = pkg_name() | {pkg_name(), raw_vsn()} | {pkg_name(), raw_vsn(), constraint_op()} | {pkg_name(), raw_vsn(), vsn(), between}

raw_vsn()

raw_vsn() = ec_semver:any_version()

t()

abstract datatype: t()

vsn()

vsn() = 'NO_VSN' | ec_semver:semver()

vsn_constraint()

vsn_constraint() = {raw_vsn(), [raw_constraint()]}

Function Index

add_package/3add a single package to the graph, where it consists of a package name and its versions and thier dependencies.
add_package_version/3add a package and version to the dependency graph with no dependency constraints, dependency constraints can always be added after the fact.
add_package_version/4add a set of dependencies to a specific package and version.
add_packages/2add a complete set of list of packages to the graph.
dep_pkg/1 given a Pkg | {Pkg, Vsn} | {Pkg, Vsn, Constraint} return Pkg.
filter_package/2
filter_packages/2given a list of package name version pairs, and a list of constraints return every member of that list that matches all constraints.
format_constraint/1A formatted constraint tuple.
format_culprits/1Return a formatted list of the culprit depenedencies which led to the dependencies not being satisfied.
format_error/1Produce a full error message for a given error condition.
format_roots/1Return a formatted list of roots of the dependency trees which could not be satisified.
format_version/1A formatted version tuple.
is_valid_constraint/1check that a specified constraint is a valid constraint.
new_graph/0create a new empty dependency graph.
parse_version/1Parse a string version into a tuple based version.
primitive_solve/3Given a dep graph and a set of goals this either solves the problem or fails.
solve/2Given a set of goals (in the form of constrains) find a set of packages and versions that satisfy all constraints.

Function Details

add_package/3

add_package(State::t(), Pkg::pkg_name(), Versions::[vsn_constraint()]) -> t()

add a single package to the graph, where it consists of a package name and its versions and thier dependencies.

      rlx_depsolver:add_package(Graph, app1, [{"0.1", [{app2, "0.2"},
                                               {app3, "0.2", '>='}]},
                                               {"0.2", []},
                                               {"0.3", []}]}]).

add_package_version/3

add_package_version(State::t(), Pkg::pkg_name(), Vsn::raw_vsn()) -> t()

add a package and version to the dependency graph with no dependency constraints, dependency constraints can always be added after the fact.

     rlx_depsolver:add_package_version(Graph, app1, "0.1").

add_package_version/4

add_package_version(X1::t(), RawPkg::pkg_name(), RawVsn::raw_vsn(), RawPkgConstraints::[raw_constraint()]) -> t()

add a set of dependencies to a specific package and version. and its versions and thier dependencies.

      rlx_depsolver:add_package(Graph, app1, "0.1", [{app2, "0.2"},
                                               {app3, "0.2", '>='}]},
                                               {"0.2", []},
                                               {"0.3", []}]).

add_packages/2

add_packages(Dom0::t(), Info::[dependency_set()]) -> t()

add a complete set of list of packages to the graph. Where the package consists of the name and a list of versions and dependencies.

      rlx_depsolver:add_packages(Graph,
                [{app1, [{"0.1", [{app2, "0.2"},
                                  {app3, "0.2", '>='}]},
                                  {"0.2", []},
                                  {"0.3", []}]},
                  {app2, [{"0.1", []},
                          {"0.2",[{app3, "0.3"}]},
                          {"0.3", []}]},
                  {app3, [{"0.1", []},
                          {"0.2", []},
                          {"0.3", []}]}])

dep_pkg/1

dep_pkg(Pkg::constraint()) -> pkg_name()

given a Pkg | {Pkg, Vsn} | {Pkg, Vsn, Constraint} return Pkg

filter_package/2

filter_package(X1::{pkg_name(), vsn()}, C::constraint()) -> boolean()

filter_packages/2

filter_packages(PVPairs::[{pkg_name(), raw_vsn()}], RawConstraints::[raw_constraint()]) -> {ok, [{pkg_name(), raw_vsn()}]} | {error, Reason::term()}

given a list of package name version pairs, and a list of constraints return every member of that list that matches all constraints.

format_constraint/1

format_constraint(Constraint::constraint()) -> iolist()

A formatted constraint tuple

format_culprits/1

format_culprits(Culprits::[{[constraint()], [constraint()]}]) -> iolist()

Return a formatted list of the culprit depenedencies which led to the dependencies not being satisfied. Example:

         (foo = 1.2.0) -> (bar > 2.0.0)

format_error/1

format_error(Error::{error, {unreachable_package, list()} | {invalid_constraints, [constraint()]} | list()}) -> iolist()

Produce a full error message for a given error condition. This includes details of the paths taken to resolve the dependencies and shows which dependencies could not be satisfied

format_roots/1

format_roots(Roots::[constraints()]) -> iolist()

Return a formatted list of roots of the dependency trees which could not be satisified. These may also have versions attached. Example:

        (foo = 1.2.0), bar

format_version/1

format_version(Version::vsn()) -> iolist()

A formatted version tuple

is_valid_constraint/1

is_valid_constraint(Pkg::constraint()) -> boolean()

check that a specified constraint is a valid constraint.

new_graph/0

new_graph() -> t()

create a new empty dependency graph

parse_version/1

parse_version(RawVsn::raw_vsn() | vsn()) -> vsn()

Parse a string version into a tuple based version

primitive_solve/3

primitive_solve(State::dep_graph(), PackageList::[constraint()], PathInd::term()) -> [pkg()] | fail_detail()

Given a dep graph and a set of goals this either solves the problem or fails. This is basically the root solver of the system, the main difference from the exported solve/2 function is the fact that this does not do the culprit search.

solve/2

solve(X1::t(), RawGoals::[constraint()]) -> {ok, [pkg()]} | {error, term()}

Given a set of goals (in the form of constrains) find a set of packages and versions that satisfy all constraints. If no solution can be found then an exception is thrown.

      rlx_depsolver:solve(State, [{app1, "0.1", '>='}]).


Generated by EDoc, Nov 11 2022, 09:11:41.