Parent

DepSelector::Selector

Attributes

dep_graph[RW]
error_reporter[RW]

Public Class Methods

new(dep_graph, error_reporter = DEFAULT_ERROR_REPORTER) click to toggle source
# File lib/dep_selector/selector.rb, line 36
def initialize(dep_graph, error_reporter = DEFAULT_ERROR_REPORTER)
  @dep_graph = dep_graph
  @error_reporter = error_reporter
end

Public Instance Methods

find_solution(solution_constraints, valid_packages = nil) click to toggle source

Based on solution_constraints, this method tries to find an assignment of PackageVersions that is compatible with the DependencyGraph. If one cannot be found, the constraints are added one at a time until the first unsatisfiable constraint is detected. Once the unsatisfiable solution constraint is identified, required non-existent packages and the most constrained packages are identified and thrown in a NoSolutionExists exception.

If a solution constraint refers to a package that doesn't exist or the constraint matches no versions, it is considered invalid. All invalid solution constraints are collected and raised in an InvalidSolutionConstraints exception. If valid_packages is non-nil, it is considered the authoritative list of extant Packages; otherwise, Package#valid? is used. This is useful if the dependency graph represents an already filtered set of packages such that a Package actually exists in your domain but is added to the dependency graph with no versions, in which case Package#valid? would return false even though we don't want to report that the package is non-existent.

# File lib/dep_selector/selector.rb, line 61
def find_solution(solution_constraints, valid_packages = nil)
  # this is a performance optimization so that packages that are
  # completely unreachable by the solution constraints don't get
  # added to the CSP
  packages_to_include_in_solve = trim_unreachable_packages(dep_graph, solution_constraints)

  begin
    # first, try to solve the whole set of constraints
    solve(dep_graph.clone, solution_constraints, valid_packages, packages_to_include_in_solve)
  rescue Exceptions::NoSolutionFound
    # since we're here, solving the whole system failed, so add
    # the solution_constraints one-by-one and try to solve in
    # order to find the constraint that breaks the system in order
    # to give helpful debugging info
    #
    # TODO [cw,2010/11/28]: for an efficiency gain, instead of
    # continually re-building the problem and looking for a
    # solution, turn solution_constraints into a Generator and
    # iteratively add and solve in order to re-use
    # propagations. This will require separating setting up the
    # constraints from searching for the solution.
    solution_constraints.each_index do |idx|
      workspace = dep_graph.clone
      begin
        solve(workspace, solution_constraints[0..idx], valid_packages, packages_to_include_in_solve)
      rescue Exceptions::NoSolutionFound => nsf
        disabled_packages =
          packages_to_include_in_solve.inject([]) do |acc, elt|
            pkg = workspace.package(elt.name)
            acc << pkg if nsf.unsatisfiable_problem.is_package_disabled?(pkg.gecode_package_id)
            acc
          end
        # disambiguate between packages disabled becuase they
        # don't exist and those that have otherwise problematic
        # constraints
        disabled_non_existent_packages = []
        disabled_most_constrained_packages = []
        disabled_packages.each do |disabled_pkg|
          disabled_collection =
            if disabled_pkg.valid? || (valid_packages && valid_packages.include?(disabled_pkg))
              disabled_most_constrained_packages
            else
              disabled_non_existent_packages
            end
          disabled_collection << disabled_pkg
        end

        # Pick the first non-existent or most-constrained package
        # that was required or the package whose constraints had
        # to be disabled in order to find a solution and generate
        # feedback for it. We only report feedback for one
        # package, because it is in fact actionable and dispalying
        # feedback for every disabled package would probably be
        # too long. The full set of disabled packages is
        # accessible in the NoSolutionExists exception.
        disabled_package_to_report_on = disabled_non_existent_packages.first ||
                                        disabled_most_constrained_packages.first
        feedback = error_reporter.give_feedback(dep_graph, solution_constraints, idx,
                                                disabled_package_to_report_on)

        raise Exceptions::NoSolutionExists.new(feedback, solution_constraints[idx],
                                               disabled_non_existent_packages,
                                               disabled_most_constrained_packages)
      end
    end
  end
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.