dep_selector_over_http 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []