solve 3.1.1 → 4.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/lib/solve.rb +10 -11
- data/lib/solve/artifact.rb +10 -10
- data/lib/solve/constraint.rb +47 -47
- data/lib/solve/demand.rb +2 -2
- data/lib/solve/dependency.rb +4 -2
- data/lib/solve/errors.rb +3 -3
- data/lib/solve/gecode_solver.rb +98 -95
- data/lib/solve/graph.rb +3 -3
- data/lib/solve/ruby_solver.rb +128 -63
- data/lib/solve/solver/serializer.rb +53 -53
- data/lib/solve/version.rb +1 -1
- metadata +14 -52
- data/.gitignore +0 -20
- data/.travis.yml +0 -22
- data/Gemfile +0 -33
- data/Guardfile +0 -17
- data/NoGecode.gemfile +0 -4
- data/README.md +0 -85
- data/Rakefile +0 -1
- data/Thorfile +0 -36
- data/solve.gemspec +0 -26
- data/spec/acceptance/benchmark.rb +0 -59
- data/spec/acceptance/large_graph_no_solution.rb +0 -18730
- data/spec/acceptance/opscode_ci_graph.rb +0 -18600
- data/spec/acceptance/ruby_solver_solutions_spec.rb +0 -307
- data/spec/acceptance/solutions_spec.rb +0 -317
- data/spec/spec_helper.rb +0 -25
- data/spec/unit/solve/artifact_spec.rb +0 -137
- data/spec/unit/solve/demand_spec.rb +0 -53
- data/spec/unit/solve/dependency_spec.rb +0 -25
- data/spec/unit/solve/gecode_solver_spec.rb +0 -206
- data/spec/unit/solve/graph_spec.rb +0 -130
- data/spec/unit/solve/ruby_solver_spec.rb +0 -166
- data/spec/unit/solve/solver/serializer_spec.rb +0 -33
- data/spec/unit/solve_spec.rb +0 -19
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 | 
            -
             | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 2 | 
            +
            SHA256:
         | 
| 3 | 
            +
              metadata.gz: 5d0ae05eff693dfa5424fa6d2c92f96839d997da2d585505992e70f91e6ab863
         | 
| 4 | 
            +
              data.tar.gz: 4ddae0850f4f234d98208ed8e42f6a2073b43e6f1d6696935c09127cd30c0bf8
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 8c4b234445b2b64082b1cdfecad5f4b49b3c13b6757237033c4b23d359a28d9899f24eb2545cf686f238a037504f1151c6924f0c376e7819b142335e6d5255a3
         | 
| 7 | 
            +
              data.tar.gz: 67dc423bc183e3c7b680ba9078340179556561837ef0eb14b0f3f650bbc3e4bd0430137d20a1e1998dca4e7c17f1890f7379147bf0e258a1fa1fad0bbbf3278e
         | 
    
        data/lib/solve.rb
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 | 
            -
            require  | 
| 1 | 
            +
            require "semverse"
         | 
| 2 2 |  | 
| 3 3 | 
             
            module Solve
         | 
| 4 | 
            -
              require_relative  | 
| 5 | 
            -
              require_relative  | 
| 6 | 
            -
              require_relative  | 
| 7 | 
            -
              require_relative  | 
| 8 | 
            -
              require_relative  | 
| 9 | 
            -
              require_relative  | 
| 10 | 
            -
              require_relative  | 
| 11 | 
            -
              require_relative  | 
| 4 | 
            +
              require_relative "solve/artifact"
         | 
| 5 | 
            +
              require_relative "solve/demand"
         | 
| 6 | 
            +
              require_relative "solve/dependency"
         | 
| 7 | 
            +
              require_relative "solve/version"
         | 
| 8 | 
            +
              require_relative "solve/errors"
         | 
| 9 | 
            +
              require_relative "solve/graph"
         | 
| 10 | 
            +
              require_relative "solve/ruby_solver"
         | 
| 11 | 
            +
              require_relative "solve/gecode_solver"
         | 
| 12 12 |  | 
| 13 13 | 
             
              # We have to set the default engine here, it gets set on the wrong object if
         | 
| 14 14 | 
             
              # we put this in the metaclass context below.
         | 
| @@ -23,7 +23,6 @@ module Solve | |
| 23 23 | 
             
                # @return [Symbol]
         | 
| 24 24 | 
             
                attr_reader :engine
         | 
| 25 25 |  | 
| 26 | 
            -
             | 
| 27 26 | 
             
                # Sets the solving backend engine. Solve supports 2 engines:
         | 
| 28 27 | 
             
                # * `:ruby` - Molinillo, a pure ruby solver
         | 
| 29 28 | 
             
                # * `:gecode` - dep-selector, a wrapper around the Gecode CSP solver library
         | 
| @@ -42,6 +41,7 @@ module Solve | |
| 42 41 | 
             
                  else
         | 
| 43 42 | 
             
                    engine_class.activate
         | 
| 44 43 | 
             
                  end
         | 
| 44 | 
            +
             | 
| 45 45 | 
             
                  @engine = selected_engine
         | 
| 46 46 | 
             
                end
         | 
| 47 47 |  | 
| @@ -77,4 +77,3 @@ module Solve | |
| 77 77 | 
             
              end
         | 
| 78 78 |  | 
| 79 79 | 
             
            end
         | 
| 80 | 
            -
             | 
    
        data/lib/solve/artifact.rb
    CHANGED
    
    | @@ -74,7 +74,7 @@ module Solve | |
| 74 74 | 
             
                #     .depends('ntp', '~> 1.3')
         | 
| 75 75 | 
             
                #
         | 
| 76 76 | 
             
                # @return [Solve::Artifact]
         | 
| 77 | 
            -
                def depends(name, constraint =  | 
| 77 | 
            +
                def depends(name, constraint = ">= 0.0.0")
         | 
| 78 78 | 
             
                  unless dependency?(name, constraint)
         | 
| 79 79 | 
             
                    set_dependency(name, constraint)
         | 
| 80 80 | 
             
                  end
         | 
| @@ -91,8 +91,8 @@ module Solve | |
| 91 91 | 
             
                # @return [Boolean]
         | 
| 92 92 | 
             
                def ==(other)
         | 
| 93 93 | 
             
                  other.is_a?(self.class) &&
         | 
| 94 | 
            -
             | 
| 95 | 
            -
             | 
| 94 | 
            +
                    name == other.name &&
         | 
| 95 | 
            +
                    version == other.version
         | 
| 96 96 | 
             
                end
         | 
| 97 97 | 
             
                alias_method :eql?, :==
         | 
| 98 98 |  | 
| @@ -100,17 +100,17 @@ module Solve | |
| 100 100 | 
             
                #
         | 
| 101 101 | 
             
                # @return [Integer]
         | 
| 102 102 | 
             
                def <=>(other)
         | 
| 103 | 
            -
                   | 
| 103 | 
            +
                  version <=> other.version
         | 
| 104 104 | 
             
                end
         | 
| 105 105 |  | 
| 106 106 | 
             
                private
         | 
| 107 107 |  | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 108 | 
            +
                def get_dependency(name, constraint)
         | 
| 109 | 
            +
                  @dependencies["#{name}-#{constraint}"]
         | 
| 110 | 
            +
                end
         | 
| 111 111 |  | 
| 112 | 
            -
             | 
| 113 | 
            -
             | 
| 114 | 
            -
             | 
| 112 | 
            +
                def set_dependency(name, constraint)
         | 
| 113 | 
            +
                  @dependencies["#{name}-#{constraint}"] = Dependency.new(self, name, constraint)
         | 
| 114 | 
            +
                end
         | 
| 115 115 | 
             
              end
         | 
| 116 116 | 
             
            end
         | 
    
        data/lib/solve/constraint.rb
    CHANGED
    
    | @@ -48,17 +48,17 @@ module Solve | |
| 48 48 | 
             
                    end
         | 
| 49 49 |  | 
| 50 50 | 
             
                    split_version = case version.to_s
         | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 51 | 
            +
                                    when /^(\d+)\.(\d+)\.(\d+)(-([0-9a-z\-\.]+))?(\+([0-9a-z\-\.]+))?$/i
         | 
| 52 | 
            +
                                      [ $1.to_i, $2.to_i, $3.to_i, $5, $7 ]
         | 
| 53 | 
            +
                                    when /^(\d+)\.(\d+)\.(\d+)?$/
         | 
| 54 | 
            +
                                      [ $1.to_i, $2.to_i, $3.to_i, nil, nil ]
         | 
| 55 | 
            +
                                    when /^(\d+)\.(\d+)?$/
         | 
| 56 | 
            +
                                      [ $1.to_i, $2.to_i, nil, nil, nil ]
         | 
| 57 | 
            +
                                    when /^(\d+)$/
         | 
| 58 | 
            +
                                      [ $1.to_i, nil, nil, nil, nil ]
         | 
| 59 | 
            +
                                    else
         | 
| 60 | 
            +
                                      raise Errors::InvalidConstraintFormat.new(constraint)
         | 
| 61 | 
            +
                                    end
         | 
| 62 62 |  | 
| 63 63 | 
             
                    [ operator, split_version ].flatten
         | 
| 64 64 | 
             
                  end
         | 
| @@ -110,30 +110,30 @@ module Solve | |
| 110 110 | 
             
                  def compare_approx(constraint, target_version)
         | 
| 111 111 | 
             
                    min = constraint.version
         | 
| 112 112 | 
             
                    max = if constraint.patch.nil?
         | 
| 113 | 
            -
             | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 117 | 
            -
             | 
| 118 | 
            -
             | 
| 119 | 
            -
             | 
| 120 | 
            -
             | 
| 121 | 
            -
             | 
| 122 | 
            -
             | 
| 123 | 
            -
             | 
| 124 | 
            -
             | 
| 113 | 
            +
                            Semverse::Version.new([min.major + 1, 0, 0, 0])
         | 
| 114 | 
            +
                          elsif constraint.build
         | 
| 115 | 
            +
                            identifiers = constraint.version.identifiers(:build)
         | 
| 116 | 
            +
                            replace = identifiers.last.to_i.to_s == identifiers.last.to_s ? "-" : nil
         | 
| 117 | 
            +
                            Semverse::Version.new([min.major, min.minor, min.patch, min.pre_release, identifiers.fill(replace, -1).join(".")])
         | 
| 118 | 
            +
                          elsif constraint.pre_release
         | 
| 119 | 
            +
                            identifiers = constraint.version.identifiers(:pre_release)
         | 
| 120 | 
            +
                            replace = identifiers.last.to_i.to_s == identifiers.last.to_s ? "-" : nil
         | 
| 121 | 
            +
                            Semverse::Version.new([min.major, min.minor, min.patch, identifiers.fill(replace, -1).join(".")])
         | 
| 122 | 
            +
                          else
         | 
| 123 | 
            +
                            Semverse::Version.new([min.major, min.minor + 1, 0, 0])
         | 
| 124 | 
            +
                          end
         | 
| 125 125 | 
             
                    min <= target_version && target_version < max
         | 
| 126 126 | 
             
                  end
         | 
| 127 127 | 
             
                end
         | 
| 128 128 |  | 
| 129 129 | 
             
                OPERATOR_TYPES = {
         | 
| 130 130 | 
             
                  "~>" => :approx,
         | 
| 131 | 
            -
                  "~" | 
| 131 | 
            +
                  "~" => :approx,
         | 
| 132 132 | 
             
                  ">=" => :greater_than_equal,
         | 
| 133 133 | 
             
                  "<=" => :less_than_equal,
         | 
| 134 | 
            -
                  "=" | 
| 135 | 
            -
                  ">" | 
| 136 | 
            -
                  "<" | 
| 134 | 
            +
                  "=" => :equal,
         | 
| 135 | 
            +
                  ">" => :greater_than,
         | 
| 136 | 
            +
                  "<" => :less_than,
         | 
| 137 137 | 
             
                }.freeze
         | 
| 138 138 |  | 
| 139 139 | 
             
                COMPARE_FUNS = {
         | 
| @@ -142,10 +142,10 @@ module Solve | |
| 142 142 | 
             
                  greater_than: method(:compare_gt),
         | 
| 143 143 | 
             
                  less_than_equal: method(:compare_lte),
         | 
| 144 144 | 
             
                  less_than: method(:compare_lt),
         | 
| 145 | 
            -
                  equal: method(:compare_equal)
         | 
| 145 | 
            +
                  equal: method(:compare_equal),
         | 
| 146 146 | 
             
                }.freeze
         | 
| 147 147 |  | 
| 148 | 
            -
                REGEXP = /^(#{OPERATOR_TYPES.keys.join('|')})\s?(.+) | 
| 148 | 
            +
                REGEXP = /^(#{OPERATOR_TYPES.keys.join('|')})\s?(.+)$/.freeze
         | 
| 149 149 |  | 
| 150 150 | 
             
                attr_reader :operator
         | 
| 151 151 | 
             
                attr_reader :major
         | 
| @@ -164,7 +164,7 @@ module Solve | |
| 164 164 | 
             
                def initialize(constraint = nil)
         | 
| 165 165 | 
             
                  constraint = constraint.to_s
         | 
| 166 166 | 
             
                  if constraint.nil? || constraint.empty?
         | 
| 167 | 
            -
                    constraint =  | 
| 167 | 
            +
                    constraint = ">= 0.0.0"
         | 
| 168 168 | 
             
                  end
         | 
| 169 169 |  | 
| 170 170 | 
             
                  @operator, @major, @minor, @patch, @pre_release, @build = self.class.split(constraint)
         | 
| @@ -175,18 +175,18 @@ module Solve | |
| 175 175 | 
             
                  end
         | 
| 176 176 |  | 
| 177 177 | 
             
                  @version = Semverse::Version.new([
         | 
| 178 | 
            -
                     | 
| 179 | 
            -
                     | 
| 180 | 
            -
                     | 
| 181 | 
            -
                     | 
| 182 | 
            -
                     | 
| 178 | 
            +
                    major,
         | 
| 179 | 
            +
                    minor,
         | 
| 180 | 
            +
                    patch,
         | 
| 181 | 
            +
                    pre_release,
         | 
| 182 | 
            +
                    build,
         | 
| 183 183 | 
             
                  ])
         | 
| 184 184 | 
             
                end
         | 
| 185 185 |  | 
| 186 186 | 
             
                # @return [Symbol]
         | 
| 187 187 | 
             
                def operator_type
         | 
| 188 | 
            -
                  unless type = OPERATOR_TYPES.fetch(operator)
         | 
| 189 | 
            -
                    raise  | 
| 188 | 
            +
                  unless ( type = OPERATOR_TYPES.fetch(operator) )
         | 
| 189 | 
            +
                    raise "unknown operator type: #{operator}"
         | 
| 190 190 | 
             
                  end
         | 
| 191 191 |  | 
| 192 192 | 
             
                  type
         | 
| @@ -201,7 +201,7 @@ module Solve | |
| 201 201 | 
             
                def satisfies?(target)
         | 
| 202 202 | 
             
                  target = Semverse::Version.coerce(target)
         | 
| 203 203 |  | 
| 204 | 
            -
                  return false if !version | 
| 204 | 
            +
                  return false if !(version == 0) && greedy_match?(target)
         | 
| 205 205 |  | 
| 206 206 | 
             
                  compare(target)
         | 
| 207 207 | 
             
                end
         | 
| @@ -215,13 +215,13 @@ module Solve | |
| 215 215 | 
             
                # @return [Boolean]
         | 
| 216 216 | 
             
                def ==(other)
         | 
| 217 217 | 
             
                  other.is_a?(self.class) &&
         | 
| 218 | 
            -
                     | 
| 219 | 
            -
                     | 
| 218 | 
            +
                    operator == other.operator &&
         | 
| 219 | 
            +
                    version == other.version
         | 
| 220 220 | 
             
                end
         | 
| 221 221 | 
             
                alias_method :eql?, :==
         | 
| 222 222 |  | 
| 223 223 | 
             
                def inspect
         | 
| 224 | 
            -
                  "#<#{self.class | 
| 224 | 
            +
                  "#<#{self.class} #{self}>"
         | 
| 225 225 | 
             
                end
         | 
| 226 226 |  | 
| 227 227 | 
             
                def to_s
         | 
| @@ -241,15 +241,15 @@ module Solve | |
| 241 241 | 
             
                  #
         | 
| 242 242 | 
             
                  # @param [Semverse::Version] target_version
         | 
| 243 243 | 
             
                  #
         | 
| 244 | 
            -
             | 
| 245 | 
            -
             | 
| 246 | 
            -
             | 
| 244 | 
            +
                def greedy_match?(target_version)
         | 
| 245 | 
            +
                  operator_type !~ /less/ && target_version.pre_release? && !version.pre_release?
         | 
| 246 | 
            +
                end
         | 
| 247 247 |  | 
| 248 248 | 
             
                  # @param [Semverse::Version] target
         | 
| 249 249 | 
             
                  #
         | 
| 250 250 | 
             
                  # @return [Boolean]
         | 
| 251 | 
            -
             | 
| 252 | 
            -
             | 
| 253 | 
            -
             | 
| 251 | 
            +
                def compare(target)
         | 
| 252 | 
            +
                  COMPARE_FUNS.fetch(operator_type).call(self, target)
         | 
| 253 | 
            +
                end
         | 
| 254 254 | 
             
              end
         | 
| 255 255 | 
             
            end
         | 
    
        data/lib/solve/demand.rb
    CHANGED
    
    
    
        data/lib/solve/dependency.rb
    CHANGED
    
    | @@ -27,14 +27,16 @@ module Solve | |
| 27 27 | 
             
                def to_s
         | 
| 28 28 | 
             
                  "#{name} (#{constraint})"
         | 
| 29 29 | 
             
                end
         | 
| 30 | 
            +
                alias :inspect :to_s
         | 
| 30 31 |  | 
| 31 32 | 
             
                # @param [Object] other
         | 
| 32 33 | 
             
                #
         | 
| 33 34 | 
             
                # @return [Boolean]
         | 
| 34 35 | 
             
                def ==(other)
         | 
| 35 36 | 
             
                  other.is_a?(self.class) &&
         | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 37 | 
            +
                    name == other.name &&
         | 
| 38 | 
            +
                    artifact == other.artifact &&
         | 
| 39 | 
            +
                    constraint == other.constraint
         | 
| 38 40 | 
             
                end
         | 
| 39 41 | 
             
                alias_method :eql?, :==
         | 
| 40 42 | 
             
              end
         | 
    
        data/lib/solve/errors.rb
    CHANGED
    
    | @@ -47,14 +47,14 @@ module Solve | |
| 47 47 | 
             
                  def to_s
         | 
| 48 48 | 
             
                    s = ""
         | 
| 49 49 | 
             
                    s << "#{@message}\n"
         | 
| 50 | 
            -
                    s << "Missing artifacts: #{missing_artifacts.join( | 
| 50 | 
            +
                    s << "Missing artifacts: #{missing_artifacts.join(",")}\n" unless missing_artifacts.empty?
         | 
| 51 51 | 
             
                    unless constraints_excluding_all_artifacts.empty?
         | 
| 52 | 
            -
             | 
| 52 | 
            +
                      pretty = constraints_excluding_all_artifacts.map { |constraint| "(#{constraint[0]} #{constraint[1]})" }.join(",")
         | 
| 53 53 | 
             
                      s << "Constraints that match no available version: #{pretty}\n"
         | 
| 54 54 | 
             
                    end
         | 
| 55 55 | 
             
                    s << "Demand that cannot be met: #{unsatisfiable_demand}\n" if unsatisfiable_demand
         | 
| 56 56 | 
             
                    unless artifacts_with_no_satisfactory_version.empty?
         | 
| 57 | 
            -
                      s << "Artifacts for which there are conflicting dependencies: #{artifacts_with_no_satisfactory_version.join( | 
| 57 | 
            +
                      s << "Artifacts for which there are conflicting dependencies: #{artifacts_with_no_satisfactory_version.join(",")}"
         | 
| 58 58 | 
             
                    end
         | 
| 59 59 | 
             
                    s
         | 
| 60 60 | 
             
                  end
         | 
    
        data/lib/solve/gecode_solver.rb
    CHANGED
    
    | @@ -1,6 +1,6 @@ | |
| 1 | 
            -
            require  | 
| 2 | 
            -
             | 
| 3 | 
            -
            require_relative  | 
| 1 | 
            +
            require "set" unless defined?(Set)
         | 
| 2 | 
            +
            require_relative "errors"
         | 
| 3 | 
            +
            require_relative "solver/serializer"
         | 
| 4 4 |  | 
| 5 5 | 
             
            module Solve
         | 
| 6 6 | 
             
              class GecodeSolver
         | 
| @@ -10,13 +10,13 @@ module Solve | |
| 10 10 | 
             
                  #
         | 
| 11 11 | 
             
                  # @return [Integer]
         | 
| 12 12 | 
             
                  def timeout
         | 
| 13 | 
            -
                    seconds = 30 unless seconds = ENV["SOLVE_TIMEOUT"]
         | 
| 13 | 
            +
                    seconds = 30 unless ( seconds = ENV["SOLVE_TIMEOUT"] )
         | 
| 14 14 | 
             
                    seconds.to_i * 1_000
         | 
| 15 15 | 
             
                  end
         | 
| 16 16 |  | 
| 17 17 | 
             
                  # Attemp to load the dep_selector gem which this solver engine requires.
         | 
| 18 18 | 
             
                  def activate
         | 
| 19 | 
            -
                    require  | 
| 19 | 
            +
                    require "dep_selector"
         | 
| 20 20 | 
             
                  rescue LoadError => e
         | 
| 21 21 | 
             
                    raise Errors::EngineNotAvailable, "dep_selector is not installed, GecodeSolver cannot be used (#{e})"
         | 
| 22 22 | 
             
                  end
         | 
| @@ -84,126 +84,129 @@ module Solve | |
| 84 84 | 
             
                private
         | 
| 85 85 |  | 
| 86 86 | 
             
                  # DepSelector::DependencyGraph object representing the problem.
         | 
| 87 | 
            -
             | 
| 87 | 
            +
                attr_reader :ds_graph
         | 
| 88 88 |  | 
| 89 89 | 
             
                  # Timeout in milliseconds. Hardcoded to 1s for now.
         | 
| 90 | 
            -
             | 
| 90 | 
            +
                attr_reader :timeout_ms
         | 
| 91 91 |  | 
| 92 92 | 
             
                  # Runs the solver with the set of demands given. If any DepSelector
         | 
| 93 93 | 
             
                  # exceptions are raised, they are rescued and re-raised
         | 
| 94 | 
            -
             | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 98 | 
            -
             | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
             | 
| 105 | 
            -
             | 
| 106 | 
            -
                   | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 94 | 
            +
                def solve_demands(demands_as_constraints)
         | 
| 95 | 
            +
                  selector = DepSelector::Selector.new(ds_graph, (timeout_ms / 1000.0))
         | 
| 96 | 
            +
                  selector.find_solution(demands_as_constraints, all_artifacts)
         | 
| 97 | 
            +
                rescue DepSelector::Exceptions::InvalidSolutionConstraints => e
         | 
| 98 | 
            +
                  report_invalid_constraints_error(e)
         | 
| 99 | 
            +
                rescue DepSelector::Exceptions::NoSolutionExists => e
         | 
| 100 | 
            +
                  report_no_solution_error(e)
         | 
| 101 | 
            +
                rescue DepSelector::Exceptions::TimeBoundExceeded
         | 
| 102 | 
            +
                  # DepSelector timed out trying to find the solution. There may or may
         | 
| 103 | 
            +
                  # not be a solution.
         | 
| 104 | 
            +
                  raise Solve::Errors::NoSolutionError.new(
         | 
| 105 | 
            +
                    "The dependency constraints could not be solved in the time allotted."
         | 
| 106 | 
            +
                  )
         | 
| 107 | 
            +
                rescue DepSelector::Exceptions::TimeBoundExceededNoSolution
         | 
| 108 | 
            +
                  # DepSelector determined there wasn't a solution to the problem, then
         | 
| 109 | 
            +
                  # timed out trying to determine which constraints cause the conflict.
         | 
| 110 | 
            +
                  raise Solve::Errors::NoSolutionCauseUnknown.new(
         | 
| 111 | 
            +
                    "There is a dependency conflict, but the solver could not determine the precise cause in the time allotted."
         | 
| 112 | 
            +
                  )
         | 
| 113 | 
            +
                end
         | 
| 112 114 |  | 
| 113 115 | 
             
                  # Maps demands to corresponding DepSelector::SolutionConstraint objects.
         | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 117 | 
            -
             | 
| 118 | 
            -
             | 
| 119 | 
            -
                    end
         | 
| 116 | 
            +
                def demands_as_constraints
         | 
| 117 | 
            +
                  @demands_as_constraints ||= demands_array.map do |demands_item|
         | 
| 118 | 
            +
                    item_name, constraint_with_operator = demands_item
         | 
| 119 | 
            +
                    version_constraint = Semverse::Constraint.new(constraint_with_operator)
         | 
| 120 | 
            +
                    DepSelector::SolutionConstraint.new(ds_graph.package(item_name), version_constraint)
         | 
| 120 121 | 
             
                  end
         | 
| 122 | 
            +
                end
         | 
| 121 123 |  | 
| 122 124 | 
             
                  # Maps all artifacts in the graph to DepSelector::Package objects. If not
         | 
| 123 125 | 
             
                  # already done, artifacts are added to the ds_graph as a necessary side effect.
         | 
| 124 | 
            -
             | 
| 125 | 
            -
             | 
| 126 | 
            -
             | 
| 127 | 
            -
             | 
| 128 | 
            -
                   | 
| 126 | 
            +
                def all_artifacts
         | 
| 127 | 
            +
                  return @all_artifacts if @all_artifacts
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                  populate_ds_graph!
         | 
| 130 | 
            +
                  @all_artifacts
         | 
| 131 | 
            +
                end
         | 
| 129 132 |  | 
| 130 133 | 
             
                  # Converts artifacts to DepSelector::Package objects and adds them to the
         | 
| 131 134 | 
             
                  # DepSelector graph. This should only be called once; use #all_artifacts
         | 
| 132 135 | 
             
                  # to safely get the set of all artifacts.
         | 
| 133 | 
            -
             | 
| 134 | 
            -
             | 
| 136 | 
            +
                def populate_ds_graph!
         | 
| 137 | 
            +
                  @all_artifacts = Set.new
         | 
| 135 138 |  | 
| 136 | 
            -
             | 
| 137 | 
            -
             | 
| 138 | 
            -
             | 
| 139 | 
            -
                    end
         | 
| 139 | 
            +
                  graph.artifacts.each do |artifact|
         | 
| 140 | 
            +
                    add_artifact_to_ds_graph(artifact)
         | 
| 141 | 
            +
                    @all_artifacts << ds_graph.package(artifact.name)
         | 
| 140 142 | 
             
                  end
         | 
| 143 | 
            +
                end
         | 
| 141 144 |  | 
| 142 | 
            -
             | 
| 143 | 
            -
             | 
| 144 | 
            -
             | 
| 145 | 
            -
             | 
| 146 | 
            -
             | 
| 147 | 
            -
                    end
         | 
| 148 | 
            -
                    package_version
         | 
| 145 | 
            +
                def add_artifact_to_ds_graph(artifact)
         | 
| 146 | 
            +
                  package_version = ds_graph.package(artifact.name).add_version(artifact.version)
         | 
| 147 | 
            +
                  artifact.dependencies.each do |dependency|
         | 
| 148 | 
            +
                    dependency = DepSelector::Dependency.new(ds_graph.package(dependency.name), dependency.constraint)
         | 
| 149 | 
            +
                    package_version.dependencies << dependency
         | 
| 149 150 | 
             
                  end
         | 
| 151 | 
            +
                  package_version
         | 
| 152 | 
            +
                end
         | 
| 150 153 |  | 
| 151 | 
            -
             | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 154 | 
            -
             | 
| 155 | 
            -
             | 
| 156 | 
            -
                    constrained_to_no_versions = e.constrained_to_no_versions.inject([]) do |list, constraint|
         | 
| 157 | 
            -
                      list << [constraint.package.name, constraint.constraint.to_s]
         | 
| 158 | 
            -
                    end
         | 
| 154 | 
            +
                def report_invalid_constraints_error(e)
         | 
| 155 | 
            +
                  non_existent_cookbooks = e.non_existent_packages.inject([]) do |list, constraint|
         | 
| 156 | 
            +
                    list << constraint.package.name
         | 
| 157 | 
            +
                  end
         | 
| 159 158 |  | 
| 160 | 
            -
             | 
| 161 | 
            -
             | 
| 162 | 
            -
                      missing_artifacts: non_existent_cookbooks,
         | 
| 163 | 
            -
                      constraints_excluding_all_artifacts: constrained_to_no_versions
         | 
| 164 | 
            -
                    )
         | 
| 159 | 
            +
                  constrained_to_no_versions = e.constrained_to_no_versions.inject([]) do |list, constraint|
         | 
| 160 | 
            +
                    list << [constraint.package.name, constraint.constraint.to_s]
         | 
| 165 161 | 
             
                  end
         | 
| 166 162 |  | 
| 167 | 
            -
                   | 
| 168 | 
            -
                     | 
| 169 | 
            -
             | 
| 170 | 
            -
                     | 
| 163 | 
            +
                  raise Solve::Errors::NoSolutionError.new(
         | 
| 164 | 
            +
                    "Required artifacts do not exist at the desired version",
         | 
| 165 | 
            +
                    missing_artifacts: non_existent_cookbooks,
         | 
| 166 | 
            +
                    constraints_excluding_all_artifacts: constrained_to_no_versions
         | 
| 167 | 
            +
                  )
         | 
| 168 | 
            +
                end
         | 
| 171 169 |  | 
| 172 | 
            -
             | 
| 173 | 
            -
             | 
| 174 | 
            -
                     | 
| 170 | 
            +
                def report_no_solution_error(e)
         | 
| 171 | 
            +
                  most_constrained_cookbooks = e.disabled_most_constrained_packages.inject([]) do |list, package|
         | 
| 172 | 
            +
                    list << "#{package.name} = #{package.versions.first}"
         | 
| 173 | 
            +
                  end
         | 
| 175 174 |  | 
| 176 | 
            -
             | 
| 177 | 
            -
             | 
| 178 | 
            -
                      unsatisfiable_demand: e.unsatisfiable_solution_constraint.to_s,
         | 
| 179 | 
            -
                      missing_artifacts: non_existent_cookbooks,
         | 
| 180 | 
            -
                      artifacts_with_no_satisfactory_version: most_constrained_cookbooks
         | 
| 181 | 
            -
                    )
         | 
| 175 | 
            +
                  non_existent_cookbooks = e.disabled_non_existent_packages.inject([]) do |list, package|
         | 
| 176 | 
            +
                    list << package.name
         | 
| 182 177 | 
             
                  end
         | 
| 183 178 |  | 
| 184 | 
            -
                   | 
| 185 | 
            -
                     | 
| 186 | 
            -
                     | 
| 187 | 
            -
             | 
| 188 | 
            -
                     | 
| 179 | 
            +
                  raise Solve::Errors::NoSolutionError.new(
         | 
| 180 | 
            +
                    e.message,
         | 
| 181 | 
            +
                    unsatisfiable_demand: e.unsatisfiable_solution_constraint.to_s,
         | 
| 182 | 
            +
                    missing_artifacts: non_existent_cookbooks,
         | 
| 183 | 
            +
                    artifacts_with_no_satisfactory_version: most_constrained_cookbooks
         | 
| 184 | 
            +
                  )
         | 
| 185 | 
            +
                end
         | 
| 189 186 |  | 
| 190 | 
            -
             | 
| 191 | 
            -
             | 
| 192 | 
            -
             | 
| 193 | 
            -
             | 
| 194 | 
            -
             | 
| 195 | 
            -
                        fetch(node).each(&block)
         | 
| 196 | 
            -
                      end
         | 
| 197 | 
            -
                    end
         | 
| 198 | 
            -
                    begin
         | 
| 199 | 
            -
                      sorted_names = nodes.tsort
         | 
| 200 | 
            -
                    rescue TSort::Cyclic => e
         | 
| 201 | 
            -
                      raise Solve::Errors::UnsortableSolutionError.new(e, unsorted_solution)
         | 
| 202 | 
            -
                    end
         | 
| 187 | 
            +
                def build_sorted_solution(unsorted_solution)
         | 
| 188 | 
            +
                  nodes = {}
         | 
| 189 | 
            +
                  unsorted_solution.each do |name, version|
         | 
| 190 | 
            +
                    nodes[name] = @graph.artifact(name, version).dependencies.map(&:name)
         | 
| 191 | 
            +
                  end
         | 
| 203 192 |  | 
| 204 | 
            -
             | 
| 205 | 
            -
             | 
| 193 | 
            +
                  # Modified from http://ruby-doc.org/stdlib-1.9.3/libdoc/tsort/rdoc/TSort.html
         | 
| 194 | 
            +
                  class << nodes
         | 
| 195 | 
            +
                    include TSort
         | 
| 196 | 
            +
                    alias tsort_each_node each_key
         | 
| 197 | 
            +
                    def tsort_each_child(node, &block)
         | 
| 198 | 
            +
                      fetch(node).each(&block)
         | 
| 206 199 | 
             
                    end
         | 
| 207 200 | 
             
                  end
         | 
| 201 | 
            +
                  begin
         | 
| 202 | 
            +
                    sorted_names = nodes.tsort
         | 
| 203 | 
            +
                  rescue TSort::Cyclic => e
         | 
| 204 | 
            +
                    raise Solve::Errors::UnsortableSolutionError.new(e, unsorted_solution)
         | 
| 205 | 
            +
                  end
         | 
| 206 | 
            +
             | 
| 207 | 
            +
                  sorted_names.map do |artifact|
         | 
| 208 | 
            +
                    [artifact, unsorted_solution[artifact]]
         | 
| 209 | 
            +
                  end
         | 
| 210 | 
            +
                end
         | 
| 208 211 | 
             
              end
         | 
| 209 212 | 
             
            end
         |