scint 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/FEATURES.md +13 -0
- data/README.md +216 -0
- data/bin/bundler-vs-scint +233 -0
- data/bin/scint +35 -0
- data/bin/scint-io-summary +46 -0
- data/bin/scint-syscall-trace +41 -0
- data/lib/bundler/setup.rb +5 -0
- data/lib/bundler.rb +168 -0
- data/lib/scint/cache/layout.rb +131 -0
- data/lib/scint/cache/metadata_store.rb +75 -0
- data/lib/scint/cache/prewarm.rb +192 -0
- data/lib/scint/cli/add.rb +85 -0
- data/lib/scint/cli/cache.rb +316 -0
- data/lib/scint/cli/exec.rb +150 -0
- data/lib/scint/cli/install.rb +1047 -0
- data/lib/scint/cli/remove.rb +60 -0
- data/lib/scint/cli.rb +77 -0
- data/lib/scint/commands/exec.rb +17 -0
- data/lib/scint/commands/install.rb +17 -0
- data/lib/scint/credentials.rb +153 -0
- data/lib/scint/debug/io_trace.rb +218 -0
- data/lib/scint/debug/sampler.rb +138 -0
- data/lib/scint/downloader/fetcher.rb +113 -0
- data/lib/scint/downloader/pool.rb +112 -0
- data/lib/scint/errors.rb +63 -0
- data/lib/scint/fs.rb +119 -0
- data/lib/scint/gem/extractor.rb +86 -0
- data/lib/scint/gem/package.rb +62 -0
- data/lib/scint/gemfile/dependency.rb +30 -0
- data/lib/scint/gemfile/editor.rb +93 -0
- data/lib/scint/gemfile/parser.rb +275 -0
- data/lib/scint/index/cache.rb +166 -0
- data/lib/scint/index/client.rb +301 -0
- data/lib/scint/index/parser.rb +142 -0
- data/lib/scint/installer/extension_builder.rb +264 -0
- data/lib/scint/installer/linker.rb +226 -0
- data/lib/scint/installer/planner.rb +140 -0
- data/lib/scint/installer/preparer.rb +207 -0
- data/lib/scint/lockfile/parser.rb +251 -0
- data/lib/scint/lockfile/writer.rb +178 -0
- data/lib/scint/platform.rb +71 -0
- data/lib/scint/progress.rb +579 -0
- data/lib/scint/resolver/provider.rb +230 -0
- data/lib/scint/resolver/resolver.rb +249 -0
- data/lib/scint/runtime/exec.rb +141 -0
- data/lib/scint/runtime/setup.rb +45 -0
- data/lib/scint/scheduler.rb +392 -0
- data/lib/scint/source/base.rb +46 -0
- data/lib/scint/source/git.rb +92 -0
- data/lib/scint/source/path.rb +70 -0
- data/lib/scint/source/rubygems.rb +79 -0
- data/lib/scint/vendor/pub_grub/assignment.rb +20 -0
- data/lib/scint/vendor/pub_grub/basic_package_source.rb +169 -0
- data/lib/scint/vendor/pub_grub/failure_writer.rb +182 -0
- data/lib/scint/vendor/pub_grub/incompatibility.rb +150 -0
- data/lib/scint/vendor/pub_grub/package.rb +43 -0
- data/lib/scint/vendor/pub_grub/partial_solution.rb +121 -0
- data/lib/scint/vendor/pub_grub/rubygems.rb +45 -0
- data/lib/scint/vendor/pub_grub/solve_failure.rb +19 -0
- data/lib/scint/vendor/pub_grub/static_package_source.rb +61 -0
- data/lib/scint/vendor/pub_grub/strategy.rb +42 -0
- data/lib/scint/vendor/pub_grub/term.rb +105 -0
- data/lib/scint/vendor/pub_grub/version.rb +3 -0
- data/lib/scint/vendor/pub_grub/version_constraint.rb +129 -0
- data/lib/scint/vendor/pub_grub/version_range.rb +423 -0
- data/lib/scint/vendor/pub_grub/version_solver.rb +236 -0
- data/lib/scint/vendor/pub_grub/version_union.rb +178 -0
- data/lib/scint/vendor/pub_grub.rb +32 -0
- data/lib/scint/worker_pool.rb +114 -0
- data/lib/scint.rb +87 -0
- metadata +116 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Scint::PubGrub
|
|
4
|
+
class VersionRange
|
|
5
|
+
attr_reader :min, :max, :include_min, :include_max
|
|
6
|
+
|
|
7
|
+
alias_method :include_min?, :include_min
|
|
8
|
+
alias_method :include_max?, :include_max
|
|
9
|
+
|
|
10
|
+
class Empty < VersionRange
|
|
11
|
+
undef_method :min, :max
|
|
12
|
+
undef_method :include_min, :include_min?
|
|
13
|
+
undef_method :include_max, :include_max?
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def empty?
|
|
19
|
+
true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def eql?(other)
|
|
23
|
+
other.empty?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def hash
|
|
27
|
+
[].hash
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def intersects?(_)
|
|
31
|
+
false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def intersect(other)
|
|
35
|
+
self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def allows_all?(other)
|
|
39
|
+
other.empty?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def include?(_)
|
|
43
|
+
false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def any?
|
|
47
|
+
false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def to_s
|
|
51
|
+
"(no versions)"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def ==(other)
|
|
55
|
+
other.class == self.class
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def invert
|
|
59
|
+
VersionRange.any
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def select_versions(_)
|
|
63
|
+
[]
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
EMPTY = Empty.new
|
|
68
|
+
Empty.singleton_class.undef_method(:new)
|
|
69
|
+
|
|
70
|
+
def self.empty
|
|
71
|
+
EMPTY
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def self.any
|
|
75
|
+
new
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def initialize(min: nil, max: nil, include_min: false, include_max: false, name: nil)
|
|
79
|
+
raise ArgumentError, "Ranges without a lower bound cannot have include_min == true" if !min && include_min == true
|
|
80
|
+
raise ArgumentError, "Ranges without an upper bound cannot have include_max == true" if !max && include_max == true
|
|
81
|
+
|
|
82
|
+
@min = min
|
|
83
|
+
@max = max
|
|
84
|
+
@include_min = include_min
|
|
85
|
+
@include_max = include_max
|
|
86
|
+
@name = name
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def hash
|
|
90
|
+
@hash ||= min.hash ^ max.hash ^ include_min.hash ^ include_max.hash
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def eql?(other)
|
|
94
|
+
if other.is_a?(VersionRange)
|
|
95
|
+
!other.empty? &&
|
|
96
|
+
min.eql?(other.min) &&
|
|
97
|
+
max.eql?(other.max) &&
|
|
98
|
+
include_min.eql?(other.include_min) &&
|
|
99
|
+
include_max.eql?(other.include_max)
|
|
100
|
+
else
|
|
101
|
+
ranges.eql?(other.ranges)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def ranges
|
|
106
|
+
[self]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def include?(version)
|
|
110
|
+
compare_version(version) == 0
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Partitions passed versions into [lower, within, higher]
|
|
114
|
+
#
|
|
115
|
+
# versions must be sorted
|
|
116
|
+
def partition_versions(versions)
|
|
117
|
+
min_index =
|
|
118
|
+
if !min || versions.empty?
|
|
119
|
+
0
|
|
120
|
+
elsif include_min?
|
|
121
|
+
(0..versions.size).bsearch { |i| versions[i].nil? || versions[i] >= min }
|
|
122
|
+
else
|
|
123
|
+
(0..versions.size).bsearch { |i| versions[i].nil? || versions[i] > min }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
lower = versions.slice(0, min_index)
|
|
127
|
+
versions = versions.slice(min_index, versions.size)
|
|
128
|
+
|
|
129
|
+
max_index =
|
|
130
|
+
if !max || versions.empty?
|
|
131
|
+
versions.size
|
|
132
|
+
elsif include_max?
|
|
133
|
+
(0..versions.size).bsearch { |i| versions[i].nil? || versions[i] > max }
|
|
134
|
+
else
|
|
135
|
+
(0..versions.size).bsearch { |i| versions[i].nil? || versions[i] >= max }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
[
|
|
139
|
+
lower,
|
|
140
|
+
versions.slice(0, max_index),
|
|
141
|
+
versions.slice(max_index, versions.size)
|
|
142
|
+
]
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Returns versions which are included by this range.
|
|
146
|
+
#
|
|
147
|
+
# versions must be sorted
|
|
148
|
+
def select_versions(versions)
|
|
149
|
+
return versions if any?
|
|
150
|
+
|
|
151
|
+
partition_versions(versions)[1]
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def compare_version(version)
|
|
155
|
+
if min
|
|
156
|
+
case version <=> min
|
|
157
|
+
when -1
|
|
158
|
+
return -1
|
|
159
|
+
when 0
|
|
160
|
+
return -1 if !include_min
|
|
161
|
+
when 1
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
if max
|
|
166
|
+
case version <=> max
|
|
167
|
+
when -1
|
|
168
|
+
when 0
|
|
169
|
+
return 1 if !include_max
|
|
170
|
+
when 1
|
|
171
|
+
return 1
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
0
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def strictly_lower?(other)
|
|
179
|
+
return false if !max || !other.min
|
|
180
|
+
|
|
181
|
+
case max <=> other.min
|
|
182
|
+
when 0
|
|
183
|
+
!include_max || !other.include_min
|
|
184
|
+
when -1
|
|
185
|
+
true
|
|
186
|
+
when 1
|
|
187
|
+
false
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def strictly_higher?(other)
|
|
192
|
+
other.strictly_lower?(self)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def intersects?(other)
|
|
196
|
+
return false if other.empty?
|
|
197
|
+
return other.intersects?(self) if other.is_a?(VersionUnion)
|
|
198
|
+
!strictly_lower?(other) && !strictly_higher?(other)
|
|
199
|
+
end
|
|
200
|
+
alias_method :allows_any?, :intersects?
|
|
201
|
+
|
|
202
|
+
def intersect(other)
|
|
203
|
+
return other if other.empty?
|
|
204
|
+
return other.intersect(self) if other.is_a?(VersionUnion)
|
|
205
|
+
|
|
206
|
+
min_range =
|
|
207
|
+
if !min
|
|
208
|
+
other
|
|
209
|
+
elsif !other.min
|
|
210
|
+
self
|
|
211
|
+
else
|
|
212
|
+
case min <=> other.min
|
|
213
|
+
when 0
|
|
214
|
+
include_min ? other : self
|
|
215
|
+
when -1
|
|
216
|
+
other
|
|
217
|
+
when 1
|
|
218
|
+
self
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
max_range =
|
|
223
|
+
if !max
|
|
224
|
+
other
|
|
225
|
+
elsif !other.max
|
|
226
|
+
self
|
|
227
|
+
else
|
|
228
|
+
case max <=> other.max
|
|
229
|
+
when 0
|
|
230
|
+
include_max ? other : self
|
|
231
|
+
when -1
|
|
232
|
+
self
|
|
233
|
+
when 1
|
|
234
|
+
other
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
if !min_range.equal?(max_range) && min_range.min && max_range.max
|
|
239
|
+
case min_range.min <=> max_range.max
|
|
240
|
+
when -1
|
|
241
|
+
when 0
|
|
242
|
+
if !min_range.include_min || !max_range.include_max
|
|
243
|
+
return EMPTY
|
|
244
|
+
end
|
|
245
|
+
when 1
|
|
246
|
+
return EMPTY
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
VersionRange.new(
|
|
251
|
+
min: min_range.min,
|
|
252
|
+
include_min: min_range.include_min,
|
|
253
|
+
max: max_range.max,
|
|
254
|
+
include_max: max_range.include_max
|
|
255
|
+
)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# The span covered by two ranges
|
|
259
|
+
#
|
|
260
|
+
# If self and other are contiguous, this builds a union of the two ranges.
|
|
261
|
+
# (if they aren't you are probably calling the wrong method)
|
|
262
|
+
def span(other)
|
|
263
|
+
return self if other.empty?
|
|
264
|
+
|
|
265
|
+
min_range =
|
|
266
|
+
if !min
|
|
267
|
+
self
|
|
268
|
+
elsif !other.min
|
|
269
|
+
other
|
|
270
|
+
else
|
|
271
|
+
case min <=> other.min
|
|
272
|
+
when 0
|
|
273
|
+
include_min ? self : other
|
|
274
|
+
when -1
|
|
275
|
+
self
|
|
276
|
+
when 1
|
|
277
|
+
other
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
max_range =
|
|
282
|
+
if !max
|
|
283
|
+
self
|
|
284
|
+
elsif !other.max
|
|
285
|
+
other
|
|
286
|
+
else
|
|
287
|
+
case max <=> other.max
|
|
288
|
+
when 0
|
|
289
|
+
include_max ? self : other
|
|
290
|
+
when -1
|
|
291
|
+
other
|
|
292
|
+
when 1
|
|
293
|
+
self
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
VersionRange.new(
|
|
298
|
+
min: min_range.min,
|
|
299
|
+
include_min: min_range.include_min,
|
|
300
|
+
max: max_range.max,
|
|
301
|
+
include_max: max_range.include_max
|
|
302
|
+
)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def union(other)
|
|
306
|
+
return other.union(self) if other.is_a?(VersionUnion)
|
|
307
|
+
|
|
308
|
+
if contiguous_to?(other)
|
|
309
|
+
span(other)
|
|
310
|
+
else
|
|
311
|
+
VersionUnion.union([self, other])
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def contiguous_to?(other)
|
|
316
|
+
return false if other.empty?
|
|
317
|
+
return true if any?
|
|
318
|
+
|
|
319
|
+
intersects?(other) || contiguous_below?(other) || contiguous_above?(other)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def contiguous_below?(other)
|
|
323
|
+
return false if !max || !other.min
|
|
324
|
+
|
|
325
|
+
max == other.min && (include_max || other.include_min)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def contiguous_above?(other)
|
|
329
|
+
other.contiguous_below?(self)
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def allows_all?(other)
|
|
333
|
+
return true if other.empty?
|
|
334
|
+
|
|
335
|
+
if other.is_a?(VersionUnion)
|
|
336
|
+
return VersionUnion.new([self]).allows_all?(other)
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
return false if max && !other.max
|
|
340
|
+
return false if min && !other.min
|
|
341
|
+
|
|
342
|
+
if min
|
|
343
|
+
case min <=> other.min
|
|
344
|
+
when -1
|
|
345
|
+
when 0
|
|
346
|
+
return false if !include_min && other.include_min
|
|
347
|
+
when 1
|
|
348
|
+
return false
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
if max
|
|
353
|
+
case max <=> other.max
|
|
354
|
+
when -1
|
|
355
|
+
return false
|
|
356
|
+
when 0
|
|
357
|
+
return false if !include_max && other.include_max
|
|
358
|
+
when 1
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
true
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def any?
|
|
366
|
+
!min && !max
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def empty?
|
|
370
|
+
false
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def to_s
|
|
374
|
+
@name ||= constraints.join(", ")
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def inspect
|
|
378
|
+
"#<#{self.class} #{to_s}>"
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def upper_invert
|
|
382
|
+
return self.class.empty unless max
|
|
383
|
+
|
|
384
|
+
VersionRange.new(min: max, include_min: !include_max)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def invert
|
|
388
|
+
return self.class.empty if any?
|
|
389
|
+
|
|
390
|
+
low = -> { VersionRange.new(max: min, include_max: !include_min) }
|
|
391
|
+
high = -> { VersionRange.new(min: max, include_min: !include_max) }
|
|
392
|
+
|
|
393
|
+
if !min
|
|
394
|
+
high.call
|
|
395
|
+
elsif !max
|
|
396
|
+
low.call
|
|
397
|
+
else
|
|
398
|
+
low.call.union(high.call)
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def ==(other)
|
|
403
|
+
self.class == other.class &&
|
|
404
|
+
min == other.min &&
|
|
405
|
+
max == other.max &&
|
|
406
|
+
include_min == other.include_min &&
|
|
407
|
+
include_max == other.include_max
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
private
|
|
411
|
+
|
|
412
|
+
def constraints
|
|
413
|
+
return ["any"] if any?
|
|
414
|
+
return ["= #{min}"] if min.to_s == max.to_s
|
|
415
|
+
|
|
416
|
+
c = []
|
|
417
|
+
c << "#{include_min ? ">=" : ">"} #{min}" if min
|
|
418
|
+
c << "#{include_max ? "<=" : "<"} #{max}" if max
|
|
419
|
+
c
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
end
|
|
423
|
+
end
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
require_relative 'partial_solution'
|
|
2
|
+
require_relative 'term'
|
|
3
|
+
require_relative 'incompatibility'
|
|
4
|
+
require_relative 'solve_failure'
|
|
5
|
+
require_relative 'strategy'
|
|
6
|
+
|
|
7
|
+
module Scint::PubGrub
|
|
8
|
+
class VersionSolver
|
|
9
|
+
attr_reader :logger
|
|
10
|
+
attr_reader :source
|
|
11
|
+
attr_reader :solution
|
|
12
|
+
attr_reader :strategy
|
|
13
|
+
|
|
14
|
+
def initialize(source:, root: Package.root, strategy: Strategy.new(source), logger: Scint::PubGrub.logger)
|
|
15
|
+
@logger = logger
|
|
16
|
+
|
|
17
|
+
@source = source
|
|
18
|
+
@strategy = strategy
|
|
19
|
+
|
|
20
|
+
# { package => [incompatibility, ...]}
|
|
21
|
+
@incompatibilities = Hash.new do |h, k|
|
|
22
|
+
h[k] = []
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@seen_incompatibilities = {}
|
|
26
|
+
|
|
27
|
+
@solution = PartialSolution.new
|
|
28
|
+
|
|
29
|
+
add_incompatibility Incompatibility.new([
|
|
30
|
+
Term.new(VersionConstraint.any(root), false)
|
|
31
|
+
], cause: :root)
|
|
32
|
+
|
|
33
|
+
propagate(root)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def solved?
|
|
37
|
+
solution.unsatisfied.empty?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Returns true if there is more work to be done, false otherwise
|
|
41
|
+
def work
|
|
42
|
+
unsatisfied_terms = solution.unsatisfied
|
|
43
|
+
if unsatisfied_terms.empty?
|
|
44
|
+
logger.info { "Solution found after #{solution.attempted_solutions} attempts:" }
|
|
45
|
+
solution.decisions.each do |package, version|
|
|
46
|
+
next if Package.root?(package)
|
|
47
|
+
logger.info { "* #{package} #{version}" }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
return false
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
next_package = choose_package_version_from(unsatisfied_terms)
|
|
54
|
+
propagate(next_package)
|
|
55
|
+
|
|
56
|
+
true
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def solve
|
|
60
|
+
while work; end
|
|
61
|
+
|
|
62
|
+
solution.decisions
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
alias_method :result, :solve
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def propagate(initial_package)
|
|
70
|
+
changed = [initial_package]
|
|
71
|
+
while package = changed.shift
|
|
72
|
+
@incompatibilities[package].reverse_each do |incompatibility|
|
|
73
|
+
result = propagate_incompatibility(incompatibility)
|
|
74
|
+
if result == :conflict
|
|
75
|
+
root_cause = resolve_conflict(incompatibility)
|
|
76
|
+
changed.clear
|
|
77
|
+
changed << propagate_incompatibility(root_cause)
|
|
78
|
+
elsif result # should be a Package
|
|
79
|
+
changed << result
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
changed.uniq!
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def propagate_incompatibility(incompatibility)
|
|
87
|
+
unsatisfied = nil
|
|
88
|
+
incompatibility.terms.each do |term|
|
|
89
|
+
relation = solution.relation(term)
|
|
90
|
+
if relation == :disjoint
|
|
91
|
+
return nil
|
|
92
|
+
elsif relation == :overlap
|
|
93
|
+
# If more than one term is inconclusive, we can't deduce anything
|
|
94
|
+
return nil if unsatisfied
|
|
95
|
+
unsatisfied = term
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
if !unsatisfied
|
|
100
|
+
return :conflict
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
logger.debug { "derived: #{unsatisfied.invert}" }
|
|
104
|
+
|
|
105
|
+
solution.derive(unsatisfied.invert, incompatibility)
|
|
106
|
+
|
|
107
|
+
unsatisfied.package
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def choose_package_version_from(unsatisfied_terms)
|
|
111
|
+
remaining = unsatisfied_terms.map { |t| [t.package, t.constraint.range] }.to_h
|
|
112
|
+
|
|
113
|
+
package, version = strategy.next_package_and_version(remaining)
|
|
114
|
+
|
|
115
|
+
logger.debug { "attempting #{package} #{version}" }
|
|
116
|
+
|
|
117
|
+
if version.nil?
|
|
118
|
+
unsatisfied_term = unsatisfied_terms.find { |t| t.package == package }
|
|
119
|
+
add_incompatibility source.no_versions_incompatibility_for(package, unsatisfied_term)
|
|
120
|
+
return package
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
conflict = false
|
|
124
|
+
|
|
125
|
+
source.incompatibilities_for(package, version).each do |incompatibility|
|
|
126
|
+
if @seen_incompatibilities.include?(incompatibility)
|
|
127
|
+
logger.debug { "knew: #{incompatibility}" }
|
|
128
|
+
next
|
|
129
|
+
end
|
|
130
|
+
@seen_incompatibilities[incompatibility] = true
|
|
131
|
+
|
|
132
|
+
add_incompatibility incompatibility
|
|
133
|
+
|
|
134
|
+
conflict ||= incompatibility.terms.all? do |term|
|
|
135
|
+
term.package == package || solution.satisfies?(term)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
unless conflict
|
|
140
|
+
logger.info { "selected #{package} #{version}" }
|
|
141
|
+
|
|
142
|
+
solution.decide(package, version)
|
|
143
|
+
else
|
|
144
|
+
logger.info { "conflict: #{conflict.inspect}" }
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
package
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def resolve_conflict(incompatibility)
|
|
151
|
+
logger.info { "conflict: #{incompatibility}" }
|
|
152
|
+
|
|
153
|
+
new_incompatibility = nil
|
|
154
|
+
|
|
155
|
+
while !incompatibility.failure?
|
|
156
|
+
most_recent_term = nil
|
|
157
|
+
most_recent_satisfier = nil
|
|
158
|
+
difference = nil
|
|
159
|
+
|
|
160
|
+
previous_level = 1
|
|
161
|
+
|
|
162
|
+
incompatibility.terms.each do |term|
|
|
163
|
+
satisfier = solution.satisfier(term)
|
|
164
|
+
|
|
165
|
+
if most_recent_satisfier.nil?
|
|
166
|
+
most_recent_term = term
|
|
167
|
+
most_recent_satisfier = satisfier
|
|
168
|
+
elsif most_recent_satisfier.index < satisfier.index
|
|
169
|
+
previous_level = [previous_level, most_recent_satisfier.decision_level].max
|
|
170
|
+
most_recent_term = term
|
|
171
|
+
most_recent_satisfier = satisfier
|
|
172
|
+
difference = nil
|
|
173
|
+
else
|
|
174
|
+
previous_level = [previous_level, satisfier.decision_level].max
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
if most_recent_term == term
|
|
178
|
+
difference = most_recent_satisfier.term.difference(most_recent_term)
|
|
179
|
+
if difference.empty?
|
|
180
|
+
difference = nil
|
|
181
|
+
else
|
|
182
|
+
difference_satisfier = solution.satisfier(difference.inverse)
|
|
183
|
+
previous_level = [previous_level, difference_satisfier.decision_level].max
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
if previous_level < most_recent_satisfier.decision_level ||
|
|
189
|
+
most_recent_satisfier.decision?
|
|
190
|
+
|
|
191
|
+
logger.info { "backtracking to #{previous_level}" }
|
|
192
|
+
solution.backtrack(previous_level)
|
|
193
|
+
|
|
194
|
+
if new_incompatibility
|
|
195
|
+
add_incompatibility(new_incompatibility)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
return incompatibility
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
new_terms = []
|
|
202
|
+
new_terms += incompatibility.terms - [most_recent_term]
|
|
203
|
+
new_terms += most_recent_satisfier.cause.terms.reject { |term|
|
|
204
|
+
term.package == most_recent_satisfier.term.package
|
|
205
|
+
}
|
|
206
|
+
if difference
|
|
207
|
+
new_terms << difference.invert
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
new_incompatibility = Incompatibility.new(new_terms, cause: Incompatibility::ConflictCause.new(incompatibility, most_recent_satisfier.cause))
|
|
211
|
+
|
|
212
|
+
if incompatibility.to_s == new_incompatibility.to_s
|
|
213
|
+
logger.info { "!! failed to resolve conflicts, this shouldn't have happened" }
|
|
214
|
+
break
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
incompatibility = new_incompatibility
|
|
218
|
+
|
|
219
|
+
partially = difference ? " partially" : ""
|
|
220
|
+
logger.info { "! #{most_recent_term} is#{partially} satisfied by #{most_recent_satisfier.term}" }
|
|
221
|
+
logger.info { "! which is caused by #{most_recent_satisfier.cause}" }
|
|
222
|
+
logger.info { "! thus #{incompatibility}" }
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
raise SolveFailure.new(incompatibility)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def add_incompatibility(incompatibility)
|
|
229
|
+
logger.debug { "fact: #{incompatibility}" }
|
|
230
|
+
incompatibility.terms.each do |term|
|
|
231
|
+
package = term.package
|
|
232
|
+
@incompatibilities[package] << incompatibility
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
end
|