dep_selector_over_http 0.0.9

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.
@@ -0,0 +1,59 @@
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/exceptions'
21
+
22
+ module DepSelector
23
+ class DenselyPackedSet
24
+ attr_reader :sorted_elements
25
+
26
+ def initialize(elements)
27
+ @sorted_elements = elements.sort
28
+ @element_to_index = {}
29
+ @sorted_elements.each_with_index{|elt, idx| @element_to_index[elt] = idx}
30
+ end
31
+
32
+ def range
33
+ Range.new(0, @sorted_elements.size-1)
34
+ end
35
+
36
+ def index(element)
37
+ unless @element_to_index.has_key?(element)
38
+ msg = "#{element} is not a valid version for this package"
39
+ raise Exceptions::InvalidVersion.new(msg)
40
+ end
41
+ @element_to_index[element]
42
+ end
43
+
44
+ def [](constraint)
45
+ # TODO [cw/mark,2010/11/22]: don't actually need an array here, re-write
46
+ range = []
47
+ started = false
48
+ done = false
49
+ sorted_elements.each_with_index do |element, idx|
50
+ if constraint.include?(element)
51
+ raise "Currently only handle continuous gap between #{range.last} and #{idx} for #{constraint.to_s} over #{@sorted_elements.join(', ')}" if (range.any? && range.last+1 != idx)
52
+ range << idx
53
+ end
54
+ end
55
+
56
+ range.empty? ? [] : Range.new(range.first, range.last)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,3 @@
1
+ module DepSelector
2
+ VERSION = "0.0.9"
3
+ end
@@ -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,71 @@
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
+
22
+ # DependencyGraphs contain Packages, which in turn contain
23
+ # PackageVersions. Packages are created at access-time through
24
+ # #package
25
+ module DepSelector
26
+ class DependencyGraph
27
+
28
+ DebugOptionFile = "/tmp/DepSelectorDebugOn"
29
+
30
+ attr_reader :packages
31
+
32
+ def initialize
33
+ @packages = {}
34
+ end
35
+
36
+ def package(name)
37
+ packages.has_key?(name) ? packages[name] : (packages[name]=Package.new(self, name))
38
+ end
39
+
40
+ def each_package
41
+ packages.each do |name, pkg|
42
+ yield pkg
43
+ end
44
+ end
45
+
46
+ def gecode_wrapper
47
+ raise "Must invoke generate_gecode_wrapper_constraints before attempting to access gecode_wrapper" unless @gecode_wrapper
48
+ @gecode_wrapper
49
+ end
50
+
51
+ # Note: only invoke this method once all Packages and
52
+ # PackageVersions have been added.
53
+ def generate_gecode_wrapper_constraints(packages_to_include_in_solve=nil)
54
+ raise "#generate_gecode_wrapper_constraints references c extensions. don't use."
55
+ end
56
+
57
+ def gecode_model_vars
58
+ packages.inject({}){|acc, elt| acc[elt.first] = elt.last.gecode_model_var ; acc }
59
+ end
60
+
61
+ def to_s(incl_densely_packed_versions = false)
62
+ packages.keys.sort.map{|name| packages[name].to_s(incl_densely_packed_versions)}.join("\n")
63
+ end
64
+
65
+ # TODO [cw,2010/11/23]: this is a simple but inefficient impl. Do
66
+ # it for realz.
67
+ def clone
68
+ Marshal.load(Marshal.dump(self))
69
+ end
70
+ end
71
+ 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,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,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,116 @@
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_version'
21
+ require 'dep_selector/densely_packed_set'
22
+
23
+ module DepSelector
24
+ class Package
25
+ attr_reader :dependency_graph, :name, :versions
26
+
27
+ def initialize(dependency_graph, name)
28
+ @dependency_graph = dependency_graph
29
+ @name = name
30
+ @versions = []
31
+ end
32
+
33
+ def add_version(version)
34
+ versions << (pv = PackageVersion.new(self, version))
35
+ pv
36
+ end
37
+
38
+ # Note: only invoke this method after all PackageVersions have been added
39
+ def densely_packed_versions
40
+ @densely_packed_versions ||= DenselyPackedSet.new(versions.map{|pkg_version| pkg_version.version})
41
+ end
42
+
43
+ # Given a version, this method returns the corresonding
44
+ # PackageVersion. Given a version constraint, this method returns
45
+ # an array of matching PackageVersions.
46
+ #--
47
+ # TODO [cw,2011/2/4]: rationalize this with DenselyPackedSet#[]
48
+ def [](version_or_constraint)
49
+ # version constraints must abide the include? contract
50
+ if version_or_constraint.respond_to?(:include?)
51
+ versions.select do |ver|
52
+ version_or_constraint.include?(ver)
53
+ end
54
+ else
55
+ versions.find{|pkg_version| pkg_version.version == version_or_constraint}
56
+ end
57
+ end
58
+
59
+ # A Package is considered valid if it has at least one version
60
+ def valid?
61
+ versions.any?
62
+ end
63
+
64
+ def to_s(incl_densely_packed_versions = false)
65
+ components = []
66
+ components << "Package #{name}"
67
+ if incl_densely_packed_versions
68
+ components << " (#{densely_packed_versions.range})"
69
+ end
70
+ versions.each{|version| components << "\n #{version.to_s(incl_densely_packed_versions)}"}
71
+ components.flatten.join
72
+ end
73
+
74
+ # Note: only invoke this method after all PackageVersions have been added
75
+ def gecode_package_id
76
+ # Note: gecode does naive bounds propagation at every post,
77
+ # which means that any package with exactly one version is
78
+ # considered bound and its dependencies propagated even though
79
+ # there might not be a solution constraint that requires that
80
+ # package to be bound, which means that otherwise-irrelevant
81
+ # constraints (e.g. A1->B1 when the solution constraint is B=2
82
+ # and there is nothing to induce a dependency on A) can cause
83
+ # unsatisfiability. Therefore, we want every package to have at
84
+ # least two versions, one of which is neither the target of
85
+ # other packages' dependencies nor induces other
86
+ # dependencies. Package version id -1 serves this purpose.
87
+ #
88
+ # TODO [cw, 2011/2/22]: Do we likewise want to leave packages
89
+ # with no versions (the target of an invalid dependency) with
90
+ # two versions in order to allow the solver to explore the
91
+ # invalid portion of the state space instead of naively limiting
92
+ # it for the purposes of having failure count heuristics?
93
+ max = densely_packed_versions.range.max || -1
94
+ @gecode_package_id ||= dependency_graph.gecode_wrapper.add_package(-1, max, 0)
95
+ end
96
+
97
+ def generate_gecode_wrapper_constraints
98
+ # if this is a valid package (has versions), we don't need to
99
+ # explicitly call gecode_package_id, because they will do it for
100
+ # us; however, if this package isn't valid (it only exists as an
101
+ # invalid dependency and thus has no versions), the solver gets
102
+ # confused, because we won't have generated its package id by
103
+ # the time it starts solving.
104
+ gecode_package_id
105
+ versions.each{|version| version.generate_gecode_wrapper_constraints }
106
+ end
107
+
108
+ def eql?(o)
109
+ # TODO [cw,2011/2/7]: this is really shallow. should implement
110
+ # == for DependencyGraph
111
+ self.class == o.class && name == o.name
112
+ end
113
+ alias :== :eql?
114
+
115
+ end
116
+ end
@@ -0,0 +1,68 @@
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 PackageVersion
22
+ attr_accessor :package, :version, :dependencies
23
+
24
+ def initialize(package, version)
25
+ @package = package
26
+ @version = version
27
+ @dependencies = []
28
+ end
29
+
30
+ def generate_gecode_wrapper_constraints
31
+ pkg_densely_packed_version = package.densely_packed_versions.index(version)
32
+
33
+ dependencies.each do |dep|
34
+ dep_pkg_range = dep.package.densely_packed_versions[dep.constraint]
35
+ package.dependency_graph.gecode_wrapper.add_version_constraint(package.gecode_package_id, pkg_densely_packed_version, dep.package.gecode_package_id, dep_pkg_range.min, dep_pkg_range.max)
36
+ end
37
+ end
38
+
39
+ def to_s(incl_densely_packed_versions = false)
40
+ components = []
41
+ components << "#{version}"
42
+ if incl_densely_packed_versions
43
+ components << " (#{package.densely_packed_versions.index(version)})"
44
+ end
45
+ components << " -> [#{dependencies.map{|d|d.to_s(incl_densely_packed_versions)}.join(', ')}]"
46
+ components.join
47
+ end
48
+
49
+ def hash
50
+ # Didn't put any thought or research into this, probably can be
51
+ # done better
52
+ to_s.hash
53
+ end
54
+
55
+ def eql?(o)
56
+ o.class == self.class &&
57
+ package == o.package &&
58
+ version == o.version &&
59
+ dependencies == o.dependencies
60
+ end
61
+ alias :== :eql?
62
+
63
+ def to_hash
64
+ { :package_name => package.name, :version => version }
65
+ end
66
+
67
+ end
68
+ end
@@ -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/dependency_graph'
21
+ require 'dep_selector/exceptions'
22
+ require 'dep_selector/error_reporter'
23
+ require 'dep_selector/error_reporter/simple_tree_traverser'
24
+
25
+ # A Selector contains the a DependencyGraph, which is populated with
26
+ # the dependency relationships, and an array of solution
27
+ # constraints. When a solution is asked for (via #find_solution),
28
+ # either a valid assignment is returned or the first solution
29
+ # constraint that makes a solution impossible.
30
+ module DepSelector
31
+ class Selector
32
+ attr_accessor :dep_graph, :error_reporter
33
+
34
+ DEFAULT_ERROR_REPORTER = ErrorReporter::SimpleTreeTraverser.new
35
+
36
+ def initialize(dep_graph, error_reporter = DEFAULT_ERROR_REPORTER)
37
+ @dep_graph = dep_graph
38
+ @error_reporter = error_reporter
39
+ end
40
+
41
+ def find_solution(solution_constraints, valid_packages = nil)
42
+ result = Net::HTTP.post_form(URI('http://localhost:9393/'), {selector: self.to_yaml, solution_constraints: solution_constraints.to_yaml, valid_packages: valid_packages.to_yaml})
43
+ JSON.parse(result.body)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,22 @@
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/dependency'
21
+
22
+ class DepSelector::SolutionConstraint < DepSelector::Dependency ; end
@@ -0,0 +1,74 @@
1
+ #
2
+ # Author:: Seth Falcon (<seth@opscode.com>)
3
+ # Author:: Christopher Walters (<cw@opscode.com>)
4
+ # Copyright:: Copyright 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/exceptions'
21
+
22
+ module DepSelector
23
+ class Version
24
+ include Comparable
25
+ attr_reader :major, :minor, :patch
26
+
27
+ def initialize(str="")
28
+ @major, @minor, @patch = parse(str)
29
+ end
30
+
31
+ def inspect
32
+ "#{@major}.#{@minor}.#{@patch}"
33
+ end
34
+
35
+ def to_s
36
+ "#{@major}.#{@minor}.#{@patch}"
37
+ end
38
+
39
+ def <=>(v)
40
+ [:major, :minor, :patch].each do |method|
41
+ ans = (self.send(method) <=> v.send(method))
42
+ return ans if ans != 0
43
+ end
44
+ 0
45
+ end
46
+
47
+ def hash
48
+ # Didn't put any thought or research into this, probably can be
49
+ # done better
50
+ to_s.hash
51
+ end
52
+
53
+ # For hash
54
+ def eql?(other)
55
+ other.is_a?(Version) && self == other
56
+ end
57
+
58
+ private
59
+
60
+ def parse(str="")
61
+ @major, @minor, @patch =
62
+ case str.to_s
63
+ when /^(\d+)\.(\d+)\.(\d+)$/
64
+ [ $1.to_i, $2.to_i, $3.to_i ]
65
+ when /^(\d+)\.(\d+)$/
66
+ [ $1.to_i, $2.to_i, 0 ]
67
+ else
68
+ msg = "'#{str.to_s}' does not match 'x.y.z' or 'x.y'"
69
+ raise Exceptions::InvalidVersion.new msg
70
+ end
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,120 @@
1
+ #
2
+ # Author:: Seth Falcon (<seth@opscode.com>)
3
+ # Copyright:: Copyright 2010-2011 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'dep_selector/version'
20
+ require 'dep_selector/exceptions'
21
+
22
+ module DepSelector
23
+ class VersionConstraint
24
+ DEFAULT_CONSTRAINT = ">= 0.0.0"
25
+ STANDARD_OPS = %w(< > <= >=)
26
+ OPS = %w(< > = <= >= ~>)
27
+ PATTERN = /^(#{OPS.join('|')}) (.+)$/
28
+
29
+ attr_reader :op, :version
30
+
31
+ def initialize(constraint_spec=nil)
32
+ constraint_spec ||= DEFAULT_CONSTRAINT
33
+ case constraint_spec
34
+ when nil
35
+ parse(DEFAULT_CONSTRAINT)
36
+ when Array
37
+ parse_from_array(constraint_spec)
38
+ when String
39
+ parse(constraint_spec)
40
+ else
41
+ msg = "VersionConstraint should be created from a String. You gave: #{constraint_spec.inspect}"
42
+ raise Exceptions::InvalidVersionConstraint, msg
43
+ end
44
+ end
45
+
46
+ def include?(v)
47
+ version = if v.respond_to? :version
48
+ Version.new(v.version.to_s)
49
+ else
50
+ Version.new(v.to_s)
51
+ end
52
+ do_op(version)
53
+ end
54
+
55
+ def inspect
56
+ "(#{@op} #{@version})"
57
+ end
58
+
59
+ def to_s
60
+ "#{@op} #{@version}"
61
+ end
62
+
63
+ def eql?(o)
64
+ o.class == self.class && @op == o.op && @version == o.version
65
+ end
66
+ alias_method :==, :eql?
67
+
68
+ private
69
+
70
+ def do_op(other_version)
71
+ if STANDARD_OPS.include? @op
72
+ other_version.send(@op.to_sym, @version)
73
+ elsif @op == '='
74
+ other_version == @version
75
+ elsif @op == '~>'
76
+ if @missing_patch_level
77
+ (other_version.major == @version.major &&
78
+ other_version.minor >= @version.minor)
79
+ else
80
+ (other_version.major == @version.major &&
81
+ other_version.minor == @version.minor &&
82
+ other_version.patch >= @version.patch)
83
+ end
84
+ else # should never happen
85
+ raise "bad op #{@op}"
86
+ end
87
+ end
88
+
89
+ def parse_from_array(constraint_spec)
90
+ if constraint_spec.empty?
91
+ parse(DEFAULT_CONSTRAINT)
92
+ elsif constraint_spec.size == 1
93
+ parse(constraint_spec.first)
94
+ else
95
+ msg = "only one version constraint operation is supported, but you gave #{constraint_spec.size} "
96
+ msg << "['#{constraint_spec.join(', ')}']"
97
+ raise Exceptions::InvalidVersionConstraint, msg
98
+ end
99
+ end
100
+
101
+ def parse(str)
102
+ @missing_patch_level = false
103
+ if str.index(" ").nil? && str =~ /^[0-9]/
104
+ # try for lone version, implied '='
105
+ @version = Version.new(str)
106
+ @op = "="
107
+ elsif PATTERN.match str
108
+ @op = $1
109
+ raw_version = $2
110
+ @version = Version.new(raw_version)
111
+ if raw_version.split('.').count == 2
112
+ @missing_patch_level = true
113
+ end
114
+ else
115
+ raise Exceptions::InvalidVersionConstraint, "'#{str}'"
116
+ end
117
+ end
118
+
119
+ end
120
+ end
@@ -0,0 +1,34 @@
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/dep_selector_version'
21
+
22
+ require 'dep_selector/selector'
23
+ require 'dep_selector/dependency_graph'
24
+ require 'dep_selector/package'
25
+ require 'dep_selector/package_version'
26
+ require 'dep_selector/dependency'
27
+ require 'dep_selector/solution_constraint'
28
+ require 'dep_selector/version'
29
+ require 'dep_selector/version_constraint'
30
+ require 'dep_selector/exceptions'
31
+
32
+ # error reporting
33
+ require 'dep_selector/error_reporter'
34
+ require 'dep_selector/error_reporter/simple_tree_traverser'
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dep_selector_over_http
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.9
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Josiah Kiehl
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-23 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Given packages, versions, and a dependency graph, find a valid assignment
15
+ of package versions
16
+ email:
17
+ - cw@opscode.com
18
+ - mark@opscode.com
19
+ executables: []
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - lib/dep_selector/densely_packed_set.rb
24
+ - lib/dep_selector/dep_selector_version.rb
25
+ - lib/dep_selector/dependency.rb
26
+ - lib/dep_selector/dependency_graph.rb
27
+ - lib/dep_selector/error_reporter/simple_tree_traverser.rb
28
+ - lib/dep_selector/error_reporter.rb
29
+ - lib/dep_selector/exceptions.rb
30
+ - lib/dep_selector/package.rb
31
+ - lib/dep_selector/package_version.rb
32
+ - lib/dep_selector/selector.rb
33
+ - lib/dep_selector/solution_constraint.rb
34
+ - lib/dep_selector/version.rb
35
+ - lib/dep_selector/version_constraint.rb
36
+ - lib/dep_selector.rb
37
+ homepage: http://github.com/RiotGames/dep-selector-over-http
38
+ licenses:
39
+ - Apache v2
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubyforge_project:
58
+ rubygems_version: 1.8.11
59
+ signing_key:
60
+ specification_version: 3
61
+ summary: Given packages, versions, and a dependency graph, find a valid assignment
62
+ of package versions
63
+ test_files: []