dep_selector 0.0.1
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.
- data/ext/dep_gecode/dep_selector_swig.i +64 -0
- data/ext/dep_gecode/dep_selector_swig_wrap.cxx +2462 -0
- data/ext/dep_gecode/dep_selector_to_gecode.cpp +557 -0
- data/ext/dep_gecode/dep_selector_to_gecode.h +136 -0
- data/ext/dep_gecode/dep_selector_to_gecode_interface.cpp +122 -0
- data/ext/dep_gecode/dep_selector_to_gecode_interface.h +70 -0
- data/ext/dep_gecode/extconf.rb +36 -0
- data/ext/dep_gecode/lib/dep_selector_to_gecode.rb +11 -0
- data/lib/dep_selector.rb +32 -0
- data/lib/dep_selector/densely_packed_set.rb +59 -0
- data/lib/dep_selector/dependency.rb +46 -0
- data/lib/dep_selector/dependency_graph.rb +83 -0
- data/lib/dep_selector/error_reporter.rb +28 -0
- data/lib/dep_selector/error_reporter/simple_tree_traverser.rb +183 -0
- data/lib/dep_selector/exceptions.rb +67 -0
- data/lib/dep_selector/gecode_wrapper.rb +151 -0
- data/lib/dep_selector/package.rb +116 -0
- data/lib/dep_selector/package_version.rb +68 -0
- data/lib/dep_selector/selector.rb +264 -0
- data/lib/dep_selector/solution_constraint.rb +22 -0
- data/lib/dep_selector/version.rb +74 -0
- data/lib/dep_selector/version_constraint.rb +120 -0
- metadata +81 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Christopher Walters (<cw@opscode.com>)
|
3
|
+
# Author:: Mark Anderson (<mark@opscode.com>)
|
4
|
+
# Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
|
5
|
+
# License:: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'dep_selector/version_constraint'
|
21
|
+
|
22
|
+
module DepSelector
|
23
|
+
class Dependency
|
24
|
+
attr_reader :package, :constraint
|
25
|
+
|
26
|
+
def initialize(package, constraint=nil)
|
27
|
+
@package = package
|
28
|
+
@constraint = constraint || VersionConstraint.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s(incl_densely_packed_versions = false)
|
32
|
+
range = package.densely_packed_versions[constraint]
|
33
|
+
"(#{package.name} #{constraint.to_s}#{incl_densely_packed_versions ? " (#{range})" : ''})"
|
34
|
+
end
|
35
|
+
|
36
|
+
def ==(o)
|
37
|
+
o.respond_to?(:package) && package == o.package &&
|
38
|
+
o.respond_to?(:constraint) && constraint == o.constraint
|
39
|
+
end
|
40
|
+
|
41
|
+
def eql?(o)
|
42
|
+
self.class == o.class && self == o
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Christopher Walters (<cw@opscode.com>)
|
3
|
+
# Author:: Mark Anderson (<mark@opscode.com>)
|
4
|
+
# Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
|
5
|
+
# License:: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'dep_selector/package'
|
21
|
+
require 'dep_selector/gecode_wrapper'
|
22
|
+
|
23
|
+
# DependencyGraphs contain Packages, which in turn contain
|
24
|
+
# PackageVersions. Packages are created at access-time through
|
25
|
+
# #package
|
26
|
+
module DepSelector
|
27
|
+
class DependencyGraph
|
28
|
+
|
29
|
+
attr_reader :packages
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
@packages = {}
|
33
|
+
end
|
34
|
+
|
35
|
+
def package(name)
|
36
|
+
packages.has_key?(name) ? packages[name] : (packages[name]=Package.new(self, name))
|
37
|
+
end
|
38
|
+
|
39
|
+
def each_package
|
40
|
+
packages.each do |name, pkg|
|
41
|
+
yield pkg
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def gecode_wrapper
|
46
|
+
raise "Must invoke generate_gecode_wrapper_constraints before attempting to access gecode_wrapper" unless @gecode_wrapper
|
47
|
+
@gecode_wrapper
|
48
|
+
end
|
49
|
+
|
50
|
+
# Note: only invoke this method once all Packages and
|
51
|
+
# PackageVersions have been added.
|
52
|
+
def generate_gecode_wrapper_constraints(packages_to_include_in_solve=nil)
|
53
|
+
unless @gecode_wrapper
|
54
|
+
packages_in_solve =
|
55
|
+
if packages_to_include_in_solve
|
56
|
+
packages_to_include_in_solve
|
57
|
+
else
|
58
|
+
packages.map{ |name, pkg| pkg }
|
59
|
+
end
|
60
|
+
|
61
|
+
# In addition to all the packages that the user specified,
|
62
|
+
# there is a "ghost" package that contains the solution
|
63
|
+
# constraints. See Selector#solve for more information.
|
64
|
+
@gecode_wrapper = GecodeWrapper.new(packages_in_solve.size + 1)
|
65
|
+
packages_in_solve.each{ |pkg| pkg.generate_gecode_wrapper_constraints }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def gecode_model_vars
|
70
|
+
packages.inject({}){|acc, elt| acc[elt.first] = elt.last.gecode_model_var ; acc }
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_s(incl_densely_packed_versions = false)
|
74
|
+
packages.keys.sort.map{|name| packages[name].to_s(incl_densely_packed_versions)}.join("\n")
|
75
|
+
end
|
76
|
+
|
77
|
+
# TODO [cw,2010/11/23]: this is a simple but inefficient impl. Do
|
78
|
+
# it for realz.
|
79
|
+
def clone
|
80
|
+
Marshal.load(Marshal.dump(self))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Christopher Walters (<cw@opscode.com>)
|
3
|
+
# Author:: Mark Anderson (<mark@opscode.com>)
|
4
|
+
# Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
|
5
|
+
# License:: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
module DepSelector
|
21
|
+
class ErrorReporter
|
22
|
+
|
23
|
+
def give_feedback(workspace, solution_constraints, unsatisfiable_constraint_idx, most_constrained_package)
|
24
|
+
raise "Sub-class must implement"
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Christopher Walters (<cw@opscode.com>)
|
3
|
+
# Author:: Mark Anderson (<mark@opscode.com>)
|
4
|
+
# Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
|
5
|
+
# License:: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'dep_selector/error_reporter'
|
21
|
+
|
22
|
+
# This error reporter simply maps the versions of packages explicitly
|
23
|
+
# included in the list of solution constraints to the restrictions
|
24
|
+
# placed on the most constrained package.
|
25
|
+
module DepSelector
|
26
|
+
class ErrorReporter
|
27
|
+
class SimpleTreeTraverser < ErrorReporter
|
28
|
+
|
29
|
+
def give_feedback(dep_graph, soln_constraints, unsatisfiable_constraint_idx, most_constrained_pkg)
|
30
|
+
unsatisfiable_soln_constraint = soln_constraints[unsatisfiable_constraint_idx]
|
31
|
+
feedback = "Unable to satisfy constraints on package #{most_constrained_pkg.name}"
|
32
|
+
feedback << ", which does not exist," unless most_constrained_pkg.valid?
|
33
|
+
feedback << " due to solution constraint #{unsatisfiable_soln_constraint}. "
|
34
|
+
|
35
|
+
all_paths = paths_from_soln_constraints_to_pkg_constraints(dep_graph, soln_constraints, most_constrained_pkg)
|
36
|
+
collapsed_paths = collapse_adjacent_paths(all_paths).map{|collapsed_path| "[#{print_path(collapsed_path).join(' -> ')}]"}
|
37
|
+
|
38
|
+
feedback << "Solution constraints that may result in a constraint on #{most_constrained_pkg.name}: #{collapsed_paths.join(', ')}"
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def paths_from_soln_constraints_to_pkg_constraints(dep_graph, soln_constraints, most_constrained_pkg)
|
44
|
+
all_paths = []
|
45
|
+
soln_constraints.each do |soln_constraint|
|
46
|
+
paths_to_pkg(dep_graph,
|
47
|
+
soln_constraint.package,
|
48
|
+
soln_constraint.constraint,
|
49
|
+
most_constrained_pkg,
|
50
|
+
[],
|
51
|
+
all_paths)
|
52
|
+
end
|
53
|
+
|
54
|
+
all_paths
|
55
|
+
end
|
56
|
+
|
57
|
+
def paths_to_pkg(dep_graph, curr_pkg, version_constraint, target_pkg, curr_path, all_paths)
|
58
|
+
if curr_pkg == target_pkg
|
59
|
+
# register the culminating constraint
|
60
|
+
all_paths.push(Array.new(curr_path).push(SolutionConstraint.new(curr_pkg, version_constraint)))
|
61
|
+
return
|
62
|
+
end
|
63
|
+
|
64
|
+
# curr_pkg has no versions, it is invalid so don't recurse
|
65
|
+
if curr_pkg.versions.empty?
|
66
|
+
# TODO [cw, 2011/2/17]: find a way to track these invalid
|
67
|
+
# packages and return as potential conflict-causing
|
68
|
+
# constraints.
|
69
|
+
return
|
70
|
+
end
|
71
|
+
|
72
|
+
if curr_path.select{|elt| elt.package == curr_pkg}.any?
|
73
|
+
# TODO [cw, 2011/2/18]: this indicates a circular dependency
|
74
|
+
# in the dependency graph. This might be useful warning
|
75
|
+
# information to report to the user.
|
76
|
+
return
|
77
|
+
end
|
78
|
+
|
79
|
+
# determine all versions of curr_pkg that match
|
80
|
+
# version_constraint and recurse into them
|
81
|
+
curr_pkg[version_constraint].each do |curr_pkg_ver|
|
82
|
+
curr_path.push(curr_pkg_ver)
|
83
|
+
curr_pkg_ver.dependencies.each do |dep|
|
84
|
+
paths_to_pkg(dep_graph, dep.package, dep.constraint, target_pkg, curr_path, all_paths)
|
85
|
+
end
|
86
|
+
curr_path.pop
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# This is a simple collapsing function. For each adjacent path,
|
91
|
+
# if there is only one element different between the two paths
|
92
|
+
# and their packages are the same (meaning only the version
|
93
|
+
# binding is different), then the elements are considered
|
94
|
+
# collasable. The merged path has all the common elements and a
|
95
|
+
# set containing the two version bindings in place of the
|
96
|
+
# contentious path item.
|
97
|
+
def collapse_adjacent_paths(paths)
|
98
|
+
return paths if paths.length < 2
|
99
|
+
|
100
|
+
paths.inject([]) do |collapsed_paths, path|
|
101
|
+
merge_path_into_collapsed_paths(collapsed_paths, path)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def print_path(path)
|
106
|
+
path.map do |step|
|
107
|
+
if step.respond_to? :version
|
108
|
+
"(#{step.package.name} = #{step.version})"
|
109
|
+
elsif step.respond_to? :constraint
|
110
|
+
step.to_s
|
111
|
+
elsif step.kind_of?(Array)
|
112
|
+
# TODO [cw, 2011/2/23]: consider detecting complete
|
113
|
+
# ranges here instead of calling each out individually
|
114
|
+
"(#{step.first.package.name} = {#{step.map{|elt| "#{elt.version}"}.join(',')}})"
|
115
|
+
else
|
116
|
+
raise "don't know how to print step"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# collapses path_under_consideration onto the end of
|
122
|
+
# collapsed_paths or adds a new path to be used in the next
|
123
|
+
# round(s) of collapsing.
|
124
|
+
#
|
125
|
+
# Note: collapsed_paths is side-effected
|
126
|
+
def merge_path_into_collapsed_paths(collapsed_paths, path_under_consideration)
|
127
|
+
curr_collapsed_path = collapsed_paths.last
|
128
|
+
|
129
|
+
# if there is no curr_collapsed_path or it isn't the same
|
130
|
+
# length as path_under_consideration, then they cannot
|
131
|
+
# possibly be mergeable
|
132
|
+
if curr_collapsed_path.nil? || curr_collapsed_path.length != path_under_consideration.length
|
133
|
+
# TODO [cw.2011/2/7]: do we need this to be a new array, or
|
134
|
+
# can we save ourselves a little memory and work by just
|
135
|
+
# pushing the reference to path_under_consideration?
|
136
|
+
return collapsed_paths << Array.new(path_under_consideration)
|
137
|
+
end
|
138
|
+
|
139
|
+
# lengths are equal, so find the first path element where
|
140
|
+
# curr_collapsed_path and path_under_consideration diverge, if
|
141
|
+
# that is the only unequal element, it's for the same package,
|
142
|
+
# and they are both PackageVersion objects then merge;
|
143
|
+
# otherwise, this is a new path
|
144
|
+
#
|
145
|
+
# TODO [cw,2011/2/7]: should we merge even if they're not for
|
146
|
+
# the same package?
|
147
|
+
unequal_idx = nil
|
148
|
+
merged_set = nil
|
149
|
+
mergeable = true
|
150
|
+
path_under_consideration.each_with_index do |path_element, curr_idx|
|
151
|
+
if path_element != curr_collapsed_path[curr_idx]
|
152
|
+
unless unequal_idx
|
153
|
+
merged_set = [curr_collapsed_path[curr_idx]].flatten
|
154
|
+
if merged_set.first.package == path_element.package &&
|
155
|
+
merged_set.first.is_a?(PackageVersion) &&
|
156
|
+
path_element.is_a?(PackageVersion)
|
157
|
+
merged_set << path_element
|
158
|
+
else
|
159
|
+
mergeable = false
|
160
|
+
break
|
161
|
+
end
|
162
|
+
unequal_idx = curr_idx
|
163
|
+
else
|
164
|
+
# this is the second place they are unequal. fast-fail,
|
165
|
+
# because we know we can't merge the paths.
|
166
|
+
mergeable = false
|
167
|
+
break
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
if unequal_idx && mergeable
|
173
|
+
curr_collapsed_path[unequal_idx] = merged_set
|
174
|
+
else
|
175
|
+
collapsed_paths << Array.new(path_under_consideration)
|
176
|
+
end
|
177
|
+
|
178
|
+
collapsed_paths
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Christopher Walters (<cw@opscode.com>)
|
3
|
+
# Author:: Mark Anderson (<mark@opscode.com>)
|
4
|
+
# Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
|
5
|
+
# License:: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
module DepSelector
|
21
|
+
module Exceptions
|
22
|
+
|
23
|
+
# This exception is what the client of dep_selector should
|
24
|
+
# catch. It contains the solution constraint that introduces
|
25
|
+
# unsatisfiability, as well as the set of packages that are
|
26
|
+
# required to be disabled due to
|
27
|
+
class NoSolutionExists < StandardError
|
28
|
+
attr_reader :message, :unsatisfiable_solution_constraint,
|
29
|
+
:disabled_non_existent_packages,
|
30
|
+
:disabled_most_constrained_packages
|
31
|
+
def initialize(message, unsatisfiable_solution_constraint,
|
32
|
+
disabled_non_existent_packages = [],
|
33
|
+
disabled_most_constrained_packages = [])
|
34
|
+
@message = message
|
35
|
+
@unsatisfiable_solution_constraint = unsatisfiable_solution_constraint
|
36
|
+
@disabled_non_existent_packages = disabled_non_existent_packages
|
37
|
+
@disabled_most_constrained_packages = disabled_most_constrained_packages
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# This exception is thrown by gecode_wrapper and only used
|
42
|
+
# internally
|
43
|
+
class NoSolutionFound < StandardError
|
44
|
+
attr_reader :unsatisfiable_problem
|
45
|
+
def initialize(unsatisfiable_problem=nil)
|
46
|
+
@unsatisfiable_problem = unsatisfiable_problem
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# This exception is thrown during Selector#find_solution if any of
|
51
|
+
# the solution constraints make finding solution impossible. The
|
52
|
+
# two cases are if a solution constraint references a package that
|
53
|
+
# doesn't exist or if the constraint on an extant package matches
|
54
|
+
# no versions.
|
55
|
+
class InvalidSolutionConstraints < ArgumentError
|
56
|
+
attr_reader :non_existent_packages, :constrained_to_no_versions
|
57
|
+
def initialize(non_existent_packages, constrained_to_no_versions)
|
58
|
+
@non_existent_packages = non_existent_packages
|
59
|
+
@constrained_to_no_versions = constrained_to_no_versions
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class InvalidVersion < ArgumentError ; end
|
64
|
+
class InvalidVersionConstraint < ArgumentError; end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Christopher Walters (<cw@opscode.com>)
|
3
|
+
# Author:: Mark Anderson (<mark@opscode.com>)
|
4
|
+
# Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
|
5
|
+
# License:: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require "dep_gecode"
|
21
|
+
require 'dep_selector/exceptions'
|
22
|
+
|
23
|
+
module DepSelector
|
24
|
+
class GecodeWrapper
|
25
|
+
attr_reader :gecode_problem
|
26
|
+
DontCareConstraint = -1
|
27
|
+
NoMatchConstraint = -2
|
28
|
+
|
29
|
+
# This insures that we properly deallocate the c++ class at the heart of dep_gecode.
|
30
|
+
# modeled after http://www.mikeperham.com/2010/02/24/the-trouble-with-ruby-finalizers/
|
31
|
+
def initialize(problem_or_package_count)
|
32
|
+
if (problem_or_package_count.is_a?(Numeric))
|
33
|
+
@gecode_problem = Dep_gecode.VersionProblemCreate(problem_or_package_count)
|
34
|
+
else
|
35
|
+
@gecode_problem = problem_or_package_count
|
36
|
+
end
|
37
|
+
ObjectSpace.define_finalizer(self, self.class.finalize(@gecode_problem))
|
38
|
+
end
|
39
|
+
def self.finalize(gecode_problem)
|
40
|
+
proc { Dep_gecode.VersionProblemDestroy(gecode_problem) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def check_package_id(package_id, param_name)
|
44
|
+
raise "Gecode #{param_name} is out of range #{package_id}" unless (package_id >= 0 && package_id < self.size())
|
45
|
+
end
|
46
|
+
|
47
|
+
def size()
|
48
|
+
raise "Gecode internal failure" if gecode_problem.nil?
|
49
|
+
Dep_gecode.VersionProblemSize(gecode_problem)
|
50
|
+
end
|
51
|
+
def package_count()
|
52
|
+
raise "Gecode internal failure" if gecode_problem.nil?
|
53
|
+
Dep_gecode.VersionProblemPackageCount(gecode_problem)
|
54
|
+
end
|
55
|
+
def add_package(min, max, current_version)
|
56
|
+
raise "Gecode internal failure" if gecode_problem.nil?
|
57
|
+
Dep_gecode.AddPackage(gecode_problem, min, max, current_version)
|
58
|
+
end
|
59
|
+
|
60
|
+
def add_version_constraint(package_id, version, dependent_package_id, min_dependent_version, max_dependent_version)
|
61
|
+
raise "Gecode internal failure" if gecode_problem.nil?
|
62
|
+
check_package_id(package_id, "package_id")
|
63
|
+
check_package_id(dependent_package_id, "dependent_package_id")
|
64
|
+
|
65
|
+
# Valid package versions are between -1 and its max (-1 means
|
66
|
+
# don't care, meaning it doesn't need to be assigned). To
|
67
|
+
# indicate constraints that match no versions, -2 is used, since
|
68
|
+
# it's not a valid assignment of the variable; thus, any branch
|
69
|
+
# that assigns -2 will fail.
|
70
|
+
#
|
71
|
+
# This mechanism is also used when a dependent package has no
|
72
|
+
# versions, which only happens if the dependency's package is
|
73
|
+
# auto-vivified when creating the parent PackageVersion's
|
74
|
+
# dependency but with no corresponding set of PackageVersions
|
75
|
+
# (i.e. it's an invalid deendency, because it does not exist in
|
76
|
+
# the dependency graph). Again, we won't abort immediately, but
|
77
|
+
# we'll add a constraint to the package that makes exploring
|
78
|
+
# that portion of the solution space unsatisfiable. Thus it is
|
79
|
+
# impossible to find solutions dependent on non-existent
|
80
|
+
# packages.
|
81
|
+
|
82
|
+
min = min_dependent_version || NoMatchConstraint
|
83
|
+
max = max_dependent_version || NoMatchConstraint
|
84
|
+
Dep_gecode.AddVersionConstraint(gecode_problem, package_id, version, dependent_package_id, min, max)
|
85
|
+
|
86
|
+
# if the package was constrained to no versions, hint to the
|
87
|
+
# solver that in the event of failure, it should prefer to
|
88
|
+
# indicate constraints on dependent_package_id as the culprit
|
89
|
+
if min == NoMatchConstraint && max == NoMatchConstraint
|
90
|
+
Dep_gecode.MarkPackageSuspicious(gecode_problem, dependent_package_id)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
def get_package_version(package_id)
|
94
|
+
raise "Gecode internal failure" if gecode_problem.nil?
|
95
|
+
check_package_id(package_id, "package_id")
|
96
|
+
Dep_gecode.GetPackageVersion(gecode_problem, package_id)
|
97
|
+
end
|
98
|
+
def is_package_disabled?(package_id)
|
99
|
+
raise "Gecode internal failure" if gecode_problem.nil?
|
100
|
+
check_package_id(package_id, "package_id")
|
101
|
+
Dep_gecode.GetPackageDisabledState(gecode_problem, package_id);
|
102
|
+
end
|
103
|
+
|
104
|
+
def get_package_max(package_id)
|
105
|
+
raise "Gecode internal failure" if gecode_problem.nil?
|
106
|
+
check_package_id(package_id, "package_id")
|
107
|
+
Dep_gecode.GetPackageMax(gecode_problem, package_id)
|
108
|
+
end
|
109
|
+
def get_package_min(package_id)
|
110
|
+
raise "Gecode internal failure" if gecode_problem.nil?
|
111
|
+
check_package_id(package_id, "package_id")
|
112
|
+
Dep_gecode.GetPackageMin(gecode_problem, package_id)
|
113
|
+
end
|
114
|
+
def dump()
|
115
|
+
raise "Gecode internal failure" if gecode_problem.nil?
|
116
|
+
Dep_gecode.VersionProblemDump(gecode_problem)
|
117
|
+
end
|
118
|
+
def dump_package_var(package_id)
|
119
|
+
raise "Gecode internal failure" if gecode_problem.nil?
|
120
|
+
check_package_id(package_id, "package_id")
|
121
|
+
Dep_gecode.VersionProblemPrintPackageVar(gecode_problem, package_id)
|
122
|
+
end
|
123
|
+
|
124
|
+
def package_disabled_count
|
125
|
+
raise "Gecode internal failure (package disabled count)" if gecode_problem.nil?
|
126
|
+
Dep_gecode.GetDisabledVariableCount(gecode_problem)
|
127
|
+
end
|
128
|
+
|
129
|
+
def mark_required(package_id)
|
130
|
+
raise "Gecode internal failure (mark_required)" if gecode_problem.nil?
|
131
|
+
check_package_id(package_id, "package_id")
|
132
|
+
Dep_gecode.MarkPackageRequired(gecode_problem, package_id);
|
133
|
+
end
|
134
|
+
|
135
|
+
def mark_preferred_to_be_at_latest(package_id, weight)
|
136
|
+
raise "Gecode internal failure (mark_preferred_to_be_at_latest)" if gecode_problem.nil?
|
137
|
+
check_package_id(package_id, "package_id")
|
138
|
+
Dep_gecode.MarkPackagePreferredToBeAtLatest(gecode_problem, package_id, weight);
|
139
|
+
end
|
140
|
+
|
141
|
+
def solve()
|
142
|
+
raise "Gecode internal failure (solve)" if gecode_problem.nil?
|
143
|
+
solution = GecodeWrapper.new(Dep_gecode.Solve(gecode_problem))
|
144
|
+
raise "Gecode internal failure (no solution found)" if (solution.nil?)
|
145
|
+
|
146
|
+
raise Exceptions::NoSolutionFound.new(solution) if solution.package_disabled_count > 0
|
147
|
+
solution
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|