doubleshot 0.2.0-java → 0.3.0-java

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.
Files changed (47) hide show
  1. data/Doubleshot +16 -6
  2. data/README-OLD.textile +216 -0
  3. data/README.textile +38 -182
  4. data/lib/doubleshot.rb +100 -39
  5. data/lib/doubleshot/commands/gem.rb +15 -12
  6. data/lib/doubleshot/commands/test.rb +38 -5
  7. data/lib/doubleshot/configuration.rb +2 -2
  8. data/lib/doubleshot/dependencies/dependency.rb +1 -1
  9. data/lib/doubleshot/dependencies/gem_dependency.rb +2 -10
  10. data/lib/doubleshot/dependencies/jar_dependency.rb +12 -2
  11. data/lib/doubleshot/lockfile.rb +9 -6
  12. data/lib/doubleshot/pom.rb +15 -2
  13. data/lib/doubleshot/resolver.rb +1 -0
  14. data/lib/doubleshot/resolver/gem_resolver.rb +45 -0
  15. data/lib/doubleshot/resolver/gem_resolver/artifact.rb +146 -0
  16. data/lib/doubleshot/resolver/gem_resolver/demand.rb +57 -0
  17. data/lib/doubleshot/resolver/gem_resolver/dependency.rb +57 -0
  18. data/lib/doubleshot/resolver/gem_resolver/errors.rb +37 -0
  19. data/lib/doubleshot/resolver/gem_resolver/gem_source.rb +58 -0
  20. data/lib/doubleshot/resolver/gem_resolver/graph.rb +200 -0
  21. data/lib/doubleshot/resolver/gem_resolver/solver.rb +279 -0
  22. data/lib/doubleshot/resolver/gem_resolver/solver/constraint_row.rb +29 -0
  23. data/lib/doubleshot/resolver/gem_resolver/solver/constraint_table.rb +35 -0
  24. data/lib/doubleshot/resolver/gem_resolver/solver/variable_row.rb +47 -0
  25. data/lib/doubleshot/resolver/gem_resolver/solver/variable_table.rb +59 -0
  26. data/lib/doubleshot/resolver/gem_resolver/source.rb +36 -0
  27. data/lib/doubleshot/resolver/jar_resolver.rb +1 -3
  28. data/lib/ruby/gem/requirement.rb +9 -0
  29. data/target/doubleshot.jar +0 -0
  30. data/test/compiler_spec.rb +31 -3
  31. data/test/configuration_spec.rb +11 -3
  32. data/test/dependencies/gem_dependency_spec.rb +3 -17
  33. data/test/dependencies/jar_dependency_spec.rb +20 -0
  34. data/test/helper.rb +3 -1
  35. data/test/helpers/stub_source.rb +120 -0
  36. data/test/lockfile_spec.rb +9 -17
  37. data/test/pom_spec.rb +31 -1
  38. data/test/resolver/gem_resolver/artifact_spec.rb +106 -0
  39. data/test/resolver/gem_resolver/demand_spec.rb +70 -0
  40. data/test/resolver/gem_resolver/dependency_spec.rb +33 -0
  41. data/test/resolver/gem_resolver/gem_source_spec.rb +28 -0
  42. data/test/resolver/gem_resolver/graph_spec.rb +239 -0
  43. data/test/resolver/gem_resolver/solver_spec.rb +449 -0
  44. data/test/resolver/gem_resolver/source_spec.rb +18 -0
  45. data/test/resolver/gem_resolver_spec.rb +102 -0
  46. metadata +35 -73
  47. data/lib/doubleshot/jar.rb +0 -62
@@ -0,0 +1,279 @@
1
+ require "doubleshot/resolver/gem_resolver/solver/constraint_row"
2
+ require "doubleshot/resolver/gem_resolver/solver/constraint_table"
3
+ require "doubleshot/resolver/gem_resolver/solver/variable_row"
4
+ require "doubleshot/resolver/gem_resolver/solver/variable_table"
5
+
6
+ class Doubleshot
7
+ class Resolver
8
+ class GemResolver
9
+ class Solver
10
+ class << self
11
+ # Create a key to identify a demand on a Doubleshot::Resolver::GemResolver::Solver.
12
+ #
13
+ # @param [Doubleshot::Resolver::GemResolver::Demand] demand
14
+ #
15
+ # @raise [NoSolutionError]
16
+ #
17
+ # @return [Symbol]
18
+ def demand_key(demand)
19
+ "#{demand.name}-#{demand.constraint}".to_sym
20
+ end
21
+
22
+ # Returns all of the versions which satisfy all of the given constraints
23
+ #
24
+ # @param [Array<Gem::Requirement>, Array<String>] constraints
25
+ # @param [Array<Gem::Version>, Array<String>] versions
26
+ #
27
+ # @return [Array<Gem::Version>]
28
+ def satisfy_all(constraints, versions)
29
+ constraints = Array(constraints).collect do |con|
30
+ con.is_a?(Gem::Requirement) ? con : Gem::Requirement.new(con.to_s)
31
+ end.uniq
32
+
33
+ versions = Array(versions).collect do |ver|
34
+ ver.is_a?(Gem::Version) ? ver : Gem::Version.new(ver.to_s)
35
+ end.uniq
36
+
37
+ versions.select do |ver|
38
+ constraints.all? { |constraint| constraint.satisfied_by?(ver) }
39
+ end
40
+ end
41
+
42
+ # Return the best version from the given list of versions for the given list of constraints
43
+ #
44
+ # @param [Array<Gem::Requirement>, Array<String>] constraints
45
+ # @param [Array<Gem::Version>, Array<String>] versions
46
+ #
47
+ # @raise [NoSolutionError] if version matches the given constraints
48
+ #
49
+ # @return [Gem::Version]
50
+ def satisfy_best(constraints, versions)
51
+ solution = satisfy_all(constraints, versions)
52
+
53
+ if solution.empty?
54
+ raise Errors::NoSolutionError
55
+ end
56
+
57
+ solution.sort.last
58
+ end
59
+ end
60
+
61
+ # The world as we know it
62
+ #
63
+ # @return [Doubleshot::Resolver::GemResolver::Graph]
64
+ attr_reader :graph
65
+ attr_reader :demands
66
+ attr_reader :ui
67
+
68
+ attr_reader :domain
69
+ attr_reader :variable_table
70
+ attr_reader :constraint_table
71
+ attr_reader :possible_values
72
+
73
+ # @param [Doubleshot::Resolver::GemResolver::Graph] graph
74
+ # @param [Array<String>, Array<Array<String, String>>] demands
75
+ # @param [#say] ui
76
+ def initialize(graph, demands = Array.new, ui=nil)
77
+ @graph = graph
78
+ @demands = Hash.new
79
+ @ui = ui.respond_to?(:say) ? ui : nil
80
+
81
+ @domain = Hash.new
82
+ @possible_values = Hash.new
83
+ @constraint_table = ConstraintTable.new
84
+ @variable_table = VariableTable.new
85
+
86
+ Array(demands).each do |l_demand|
87
+ demands(*l_demand)
88
+ end
89
+ end
90
+
91
+ # @return [Hash]
92
+ def resolve
93
+ trace("Attempting to find a solution")
94
+ seed_demand_dependencies
95
+
96
+ while unbound_variable = variable_table.first_unbound
97
+ possible_values_for_unbound = possible_values_for(unbound_variable)
98
+ trace("Searching for a value for #{unbound_variable.artifact}")
99
+ trace("Possible values are #{possible_values_for_unbound}")
100
+
101
+ while possible_value = possible_values_for_unbound.shift
102
+ possible_artifact = graph.get_artifact(unbound_variable.artifact, possible_value.version)
103
+ possible_dependencies = possible_artifact.dependencies
104
+ all_ok = possible_dependencies.all? { |dependency| can_add_new_constraint?(dependency) }
105
+ if all_ok
106
+ trace("Attempting to use #{possible_artifact}")
107
+ add_dependencies(possible_dependencies, possible_artifact)
108
+ unbound_variable.bind(possible_value)
109
+ break
110
+ end
111
+ end
112
+
113
+ unless unbound_variable.bound?
114
+ trace("Could not find an acceptable value for #{unbound_variable.artifact}")
115
+ backtrack(unbound_variable)
116
+ end
117
+ end
118
+
119
+ solution = {}.tap do |solution|
120
+ variable_table.rows.each do |variable|
121
+ solution[variable.artifact] = variable.value.version.to_s
122
+ end
123
+ end
124
+
125
+ trace("Found Solution")
126
+ trace(solution)
127
+
128
+ solution
129
+ end
130
+
131
+ # @overload demands(name, constraint)
132
+ # Return the Doubleshot::Resolver::GemResolver::Demand from the collection of demands
133
+ # with the given name and constraint.
134
+ #
135
+ # @param [#to_s]
136
+ # @param [Gem::Requirement, #to_s]
137
+ #
138
+ # @return [Doubleshot::Resolver::GemResolver::Demand]
139
+ # @overload demands(name)
140
+ # Return the Doubleshot::Resolver::GemResolver::Demand from the collection of demands
141
+ # with the given name.
142
+ #
143
+ # @param [#to_s]
144
+ #
145
+ # @return [Doubleshot::Resolver::GemResolver::Demand]
146
+ # @overload demands
147
+ # Return the collection of demands
148
+ #
149
+ # @return [Array<Doubleshot::Resolver::GemResolver::Demand>]
150
+ def demands(*args)
151
+ if args.empty?
152
+ return demand_collection
153
+ end
154
+ if args.length > 2
155
+ raise ArgumentError, "Unexpected number of arguments. You gave: #{args.length}. Expected: 2 or less."
156
+ end
157
+
158
+ name, constraint = args
159
+ constraint ||= ">= 0"
160
+
161
+ if name.nil?
162
+ raise ArgumentError, "A name must be specified. You gave: #{args}."
163
+ end
164
+
165
+ demand = Demand.new(self, name, constraint)
166
+ add_demand(demand)
167
+ end
168
+
169
+ # Add a Doubleshot::Resolver::GemResolver::Demand to the collection of demands and
170
+ # return the added Doubleshot::Resolver::GemResolver::Demand. No change will be made
171
+ # if the demand is already a member of the collection.
172
+ #
173
+ # @param [Doubleshot::Resolver::GemResolver::Demand] demand
174
+ #
175
+ # @return [Doubleshot::Resolver::GemResolver::Demand]
176
+ def add_demand(demand)
177
+ unless has_demand?(demand)
178
+ @demands[self.class.demand_key(demand)] = demand
179
+ end
180
+
181
+ demand
182
+ end
183
+ alias_method :demand, :add_demand
184
+
185
+ # @param [Doubleshot::Resolver::GemResolver::Demand, nil] demand
186
+ def remove_demand(demand)
187
+ if has_demand?(demand)
188
+ @demands.delete(self.class.demand_key(demand))
189
+ end
190
+ end
191
+
192
+ # @param [Doubleshot::Resolver::GemResolver::Demand] demand
193
+ #
194
+ # @return [Boolean]
195
+ def has_demand?(demand)
196
+ @demands.has_key?(self.class.demand_key(demand))
197
+ end
198
+
199
+ private
200
+
201
+ # @return [Array<Doubleshot::Resolver::GemResolver::Demand>]
202
+ def demand_collection
203
+ @demands.collect { |name, demand| demand }
204
+ end
205
+
206
+ def seed_demand_dependencies
207
+ add_dependencies(demands, :root)
208
+ end
209
+
210
+ def can_add_new_constraint?(dependency)
211
+ current_binding = variable_table.find_artifact(dependency.name)
212
+ #haven't seen it before, haven't bound it yet or the binding is ok
213
+ current_binding.nil? || current_binding.value.nil? || dependency.constraint.satisfied_by?(current_binding.value.version)
214
+ end
215
+
216
+ def possible_values_for(variable)
217
+ possible_values_for_variable = possible_values[variable.artifact]
218
+ if possible_values_for_variable.nil?
219
+ constraints_for_variable = constraint_table.constraints_on_artifact(variable.artifact)
220
+ all_values_for_variable = domain[variable.artifact]
221
+ possible_values_for_variable = constraints_for_variable.inject(all_values_for_variable) do |remaining_values, constraint|
222
+ remaining_values.reject { |value| !constraint.satisfied_by?(value.version) }
223
+ end
224
+ possible_values[variable.artifact] = possible_values_for_variable
225
+ end
226
+ possible_values_for_variable
227
+ end
228
+
229
+ def add_dependencies(dependencies, source)
230
+ dependencies.each do |dependency|
231
+ trace("Adding constraint #{dependency.name} #{dependency.constraint} from #{source}")
232
+ variable_table.add(dependency.name, source)
233
+ constraint_table.add(dependency, source)
234
+ dependency_domain = graph.versions(dependency.name, dependency.constraint)
235
+ domain[dependency.name] = [(domain[dependency.name] || []), dependency_domain]
236
+ .flatten
237
+ .uniq
238
+ .sort { |left, right| right.version <=> left.version }
239
+ end
240
+ end
241
+
242
+ def reset_possible_values_for(variable)
243
+ possible_values[variable.artifact] = nil
244
+ possible_values_for(variable)
245
+ end
246
+
247
+ def backtrack(unbound_variable)
248
+ previous_variable = variable_table.before(unbound_variable.artifact)
249
+
250
+ if previous_variable.nil?
251
+ trace("Cannot backtrack any further")
252
+ raise Errors::NoSolutionError
253
+ end
254
+
255
+ trace("Unbinding #{previous_variable.artifact}")
256
+
257
+ source = previous_variable.value
258
+ removed_variables = variable_table.remove_all_with_only_this_source!(source)
259
+ removed_variables.each do |removed_variable|
260
+ possible_values[removed_variable.artifact] = nil
261
+ trace("Removed variable #{removed_variable.artifact}")
262
+ end
263
+ removed_constraints = constraint_table.remove_constraints_from_source!(source)
264
+ removed_constraints.each do |removed_constraint|
265
+ trace("Removed constraint #{removed_constraint.name} #{removed_constraint.constraint}")
266
+ end
267
+ previous_variable.unbind
268
+ variable_table.all_after(previous_variable.artifact).each do |variable|
269
+ new_possibles = reset_possible_values_for(variable)
270
+ end
271
+ end
272
+
273
+ def trace(message)
274
+ ui.say(message) unless ui.nil?
275
+ end
276
+ end
277
+ end
278
+ end
279
+ end
@@ -0,0 +1,29 @@
1
+ require "forwardable"
2
+
3
+ class Doubleshot
4
+ class Resolver
5
+ class GemResolver
6
+ class Solver
7
+ class ConstraintRow
8
+ extend Forwardable
9
+
10
+ attr_reader :source
11
+
12
+ def_delegator :dependency, :name
13
+ def_delegator :dependency, :constraint
14
+
15
+ # @param [Doubleshot::Resolver::GemResolver::Dependency] dependency
16
+ # @param [String, Symbol] source
17
+ def initialize(dependency, source)
18
+ @dependency = dependency
19
+ @source = source
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :dependency
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ class Doubleshot
2
+ class Resolver
3
+ class GemResolver
4
+ class Solver
5
+ class ConstraintTable
6
+ attr_reader :rows
7
+
8
+ def initialize
9
+ @rows = Array.new
10
+ end
11
+
12
+ # @param [Doubleshot::Resolver::GemResolver::Dependency] dependency
13
+ # @param [String, Symbol] source
14
+ #
15
+ # @return [Array<ConstraintRow>]
16
+ def add(dependency, source)
17
+ @rows << ConstraintRow.new(dependency, source)
18
+ end
19
+
20
+ def constraints_on_artifact(name)
21
+ @rows.select do |row|
22
+ row.name == name
23
+ end.map { |row| row.constraint }
24
+ end
25
+
26
+ def remove_constraints_from_source!(source)
27
+ from_source, others = @rows.partition { |row| row.source == source }
28
+ @rows = others
29
+ from_source
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,47 @@
1
+ class Doubleshot
2
+ class Resolver
3
+ class GemResolver
4
+ class Solver
5
+ class VariableRow
6
+ attr_reader :artifact
7
+ attr_reader :value
8
+ attr_reader :sources
9
+
10
+ # @param [String] artifact
11
+ # @param [String, Symbol] source
12
+ def initialize(artifact, source)
13
+ @artifact = artifact
14
+ @value = nil
15
+ @sources = Array(source)
16
+ end
17
+
18
+ # @param [String, Symbol] source
19
+ def add_source(source)
20
+ @sources << source
21
+ end
22
+
23
+ def last_source
24
+ @sources.last
25
+ end
26
+
27
+ def bind(value)
28
+ @value = value
29
+ end
30
+
31
+ def unbind
32
+ @value = nil
33
+ end
34
+
35
+ def bound?
36
+ !@value.nil?
37
+ end
38
+
39
+ # @param [String, Symbol] source
40
+ def remove_source(source)
41
+ @sources.delete(source)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,59 @@
1
+ class Doubleshot
2
+ class Resolver
3
+ class GemResolver
4
+ class Solver
5
+ class VariableTable
6
+ attr_reader :rows
7
+
8
+ def initialize
9
+ @rows = Array.new
10
+ end
11
+
12
+ # @param [String] artifact
13
+ # @param [String] source
14
+ def add(artifact, source)
15
+ row = rows.detect { |row| row.artifact == artifact }
16
+ if row.nil?
17
+ @rows << VariableRow.new(artifact, source)
18
+ else
19
+ row.add_source(source)
20
+ end
21
+ end
22
+
23
+ def first_unbound
24
+ @rows.detect { |row| row.bound? == false }
25
+ end
26
+
27
+ # @param [String] artifact
28
+ def find_artifact(artifact)
29
+ @rows.detect { |row| row.artifact == artifact }
30
+ end
31
+
32
+ # @param [String] source
33
+ def remove_all_with_only_this_source!(source)
34
+ with_only_this_source, others = @rows.partition { |row| row.sources == [source] }
35
+ @rows = others
36
+ with_only_this_source
37
+ end
38
+
39
+ # @param [String] source
40
+ def all_from_source(source)
41
+ @rows.select { |row| row.sources.include?(source) }
42
+ end
43
+
44
+ # @param [String] artifact
45
+ def before(artifact)
46
+ artifact_index = @rows.index { |row| row.artifact == artifact }
47
+ (artifact_index == 0) ? nil : @rows[artifact_index - 1]
48
+ end
49
+
50
+ # @param [String] artifact
51
+ def all_after(artifact)
52
+ artifact_index = @rows.index { |row| row.artifact == artifact }
53
+ @rows[(artifact_index+1)..-1]
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end