pub_grub 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +8 -2
- data/bin/console +1 -1
- data/lib/pub_grub.rb +2 -0
- data/lib/pub_grub/assignment.rb +2 -2
- data/lib/pub_grub/incompatibility.rb +27 -5
- data/lib/pub_grub/package.rb +6 -54
- data/lib/pub_grub/partial_solution.rb +3 -3
- data/lib/pub_grub/rubygems.rb +50 -0
- data/lib/pub_grub/static_package_source.rb +50 -24
- data/lib/pub_grub/term.rb +0 -3
- data/lib/pub_grub/version_constraint.rb +29 -76
- data/lib/pub_grub/version_range.rb +322 -0
- data/lib/pub_grub/version_solver.rb +21 -13
- data/lib/pub_grub/version_union.rb +129 -0
- data/pub_grub.gemspec +3 -1
- metadata +34 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 35ae2080ad05322250f9908df76885b83bc1b2578202fccb33c13457bd570925
|
4
|
+
data.tar.gz: 0dc16b3c3e66f06548a51fa03876090d070c67821e43e6c552789e881dd8feab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e86e819b78f3b6a6e94ec3379f0cc4b61e827cbabd16940eb070156cef6b02b9333a3166616e82fe4efb9b3661c25918ed00641a8a8967550dd3315b1c7d2e7c
|
7
|
+
data.tar.gz: 441598b506f2d4d418bb9976911f248f86b1ccc1c1a13127ee9e5f8d8f408469ec48ea7c58b1356fdc9ff9f7c78eba9a266af9db7580bf24457d82465f8e878b
|
data/Gemfile.lock
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pub_grub (0.
|
4
|
+
pub_grub (0.3.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
9
|
minitest (5.11.3)
|
10
|
+
minitest-stackprof (0.2.0)
|
11
|
+
minitest (>= 5.0)
|
12
|
+
stackprof (>= 0.2.7)
|
10
13
|
rake (10.5.0)
|
14
|
+
stackprof (0.2.12)
|
11
15
|
|
12
16
|
PLATFORMS
|
13
17
|
ruby
|
@@ -15,8 +19,10 @@ PLATFORMS
|
|
15
19
|
DEPENDENCIES
|
16
20
|
bundler (~> 1.16)
|
17
21
|
minitest (~> 5.0)
|
22
|
+
minitest-stackprof
|
18
23
|
pub_grub!
|
19
24
|
rake (~> 10.0)
|
25
|
+
stackprof (~> 0.2.12)
|
20
26
|
|
21
27
|
BUNDLED WITH
|
22
|
-
1.16.
|
28
|
+
1.16.4
|
data/bin/console
CHANGED
data/lib/pub_grub.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require "pub_grub/package"
|
2
2
|
require "pub_grub/static_package_source"
|
3
3
|
require "pub_grub/term"
|
4
|
+
require "pub_grub/version_range"
|
4
5
|
require "pub_grub/version_constraint"
|
6
|
+
require "pub_grub/version_union"
|
5
7
|
require "pub_grub/version_solver"
|
6
8
|
require "pub_grub/incompatibility"
|
7
9
|
require 'pub_grub/solve_failure'
|
data/lib/pub_grub/assignment.rb
CHANGED
@@ -8,8 +8,8 @@ module PubGrub
|
|
8
8
|
@index = index
|
9
9
|
end
|
10
10
|
|
11
|
-
def self.decision(version, decision_level, index)
|
12
|
-
term = Term.new(VersionConstraint.exact(version), true)
|
11
|
+
def self.decision(package, version, decision_level, index)
|
12
|
+
term = Term.new(VersionConstraint.exact(package, version), true)
|
13
13
|
new(term, :decision, decision_level, index)
|
14
14
|
end
|
15
15
|
|
@@ -5,6 +5,12 @@ module PubGrub
|
|
5
5
|
alias_method :other, :satisfier
|
6
6
|
end
|
7
7
|
|
8
|
+
InvalidDependency = Struct.new(:package, :constraint) do
|
9
|
+
end
|
10
|
+
|
11
|
+
NoVersions = Struct.new(:constraint) do
|
12
|
+
end
|
13
|
+
|
8
14
|
attr_reader :terms, :cause
|
9
15
|
|
10
16
|
def initialize(terms, cause:)
|
@@ -35,10 +41,16 @@ module PubGrub
|
|
35
41
|
|
36
42
|
def to_s
|
37
43
|
case cause
|
44
|
+
when :root
|
45
|
+
"(root dependency)"
|
38
46
|
when :dependency
|
39
47
|
raise unless terms.length == 2
|
40
48
|
"#{terms[0].to_s(allow_every: true)} depends on #{terms[1].invert}"
|
41
|
-
|
49
|
+
when PubGrub::Incompatibility::InvalidDependency
|
50
|
+
"#{terms[0].to_s(allow_every: true)} depends on unknown package #{cause.package}"
|
51
|
+
when PubGrub::Incompatibility::NoVersions
|
52
|
+
"no versions satisfy #{cause.constraint}"
|
53
|
+
when PubGrub::Incompatibility::ConflictCause
|
42
54
|
if failure?
|
43
55
|
"version solving has failed"
|
44
56
|
elsif terms.length == 1
|
@@ -72,6 +84,8 @@ module PubGrub
|
|
72
84
|
end
|
73
85
|
end
|
74
86
|
end
|
87
|
+
else
|
88
|
+
raise "unhandled cause: #{cause.inspect}"
|
75
89
|
end
|
76
90
|
end
|
77
91
|
|
@@ -79,6 +93,17 @@ module PubGrub
|
|
79
93
|
"#<#{self.class} #{to_s}>"
|
80
94
|
end
|
81
95
|
|
96
|
+
def pretty_print(q)
|
97
|
+
q.group 2, "#<#{self.class}", ">" do
|
98
|
+
q.breakable
|
99
|
+
q.text to_s
|
100
|
+
|
101
|
+
q.breakable
|
102
|
+
q.text " caused by "
|
103
|
+
q.pp @cause
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
82
107
|
private
|
83
108
|
|
84
109
|
def cleanup_terms(terms)
|
@@ -97,12 +122,9 @@ module PubGrub
|
|
97
122
|
return terms if terms.length == 2 && terms[0].package != terms[1].package
|
98
123
|
|
99
124
|
terms.group_by(&:package).map do |package, common_terms|
|
100
|
-
term
|
101
|
-
common_terms.inject do |acc, term|
|
125
|
+
common_terms.inject do |acc, term|
|
102
126
|
acc.intersect(term)
|
103
127
|
end
|
104
|
-
|
105
|
-
term
|
106
128
|
end
|
107
129
|
end
|
108
130
|
end
|
data/lib/pub_grub/package.rb
CHANGED
@@ -2,78 +2,30 @@
|
|
2
2
|
|
3
3
|
module PubGrub
|
4
4
|
class Package
|
5
|
-
class Version
|
6
|
-
attr_reader :package, :id, :name
|
7
5
|
|
8
|
-
|
9
|
-
@package = package
|
10
|
-
@id = id
|
11
|
-
@name = name
|
12
|
-
end
|
13
|
-
|
14
|
-
def to_s
|
15
|
-
name
|
16
|
-
end
|
17
|
-
|
18
|
-
def inspect
|
19
|
-
"#<#{self.class} #{package.name} #{name} (#{id})>"
|
20
|
-
end
|
21
|
-
|
22
|
-
def <=>(other)
|
23
|
-
[package, id] <=> [other.package, other.id]
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
attr_reader :name, :versions
|
6
|
+
attr_reader :name
|
28
7
|
|
29
8
|
def initialize(name)
|
30
9
|
@name = name
|
31
|
-
@versions = []
|
32
|
-
yield self if block_given?
|
33
|
-
end
|
34
|
-
|
35
|
-
def version(version)
|
36
|
-
@versions.detect { |v| v.name == version } ||
|
37
|
-
raise("No such version of #{name.inspect}: #{version.inspect}")
|
38
|
-
end
|
39
|
-
alias_method :[], :version
|
40
|
-
|
41
|
-
def add_version(name)
|
42
|
-
Version.new(self, @versions.length, name).tap do |version|
|
43
|
-
@versions << version
|
44
|
-
end
|
45
10
|
end
|
46
11
|
|
47
12
|
def inspect
|
48
|
-
"#<#{self.class} #{name.inspect}
|
13
|
+
"#<#{self.class} #{name.inspect}>"
|
49
14
|
end
|
50
15
|
|
51
16
|
def <=>(other)
|
52
17
|
name <=> other.name
|
53
18
|
end
|
54
19
|
|
55
|
-
|
56
|
-
|
57
|
-
def to_s
|
58
|
-
"(root)"
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
attr_reader :version
|
63
|
-
|
64
|
-
def initialize
|
65
|
-
super(:root)
|
66
|
-
@version = Version.new(self, 0, "1.0.0")
|
67
|
-
@versions = [@version].freeze
|
68
|
-
end
|
69
|
-
end
|
20
|
+
ROOT = Package.new(:root)
|
21
|
+
ROOT_VERSION = 0
|
70
22
|
|
71
23
|
def self.root
|
72
|
-
|
24
|
+
ROOT
|
73
25
|
end
|
74
26
|
|
75
27
|
def self.root_version
|
76
|
-
|
28
|
+
ROOT_VERSION
|
77
29
|
end
|
78
30
|
end
|
79
31
|
end
|
@@ -59,12 +59,12 @@ module PubGrub
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
-
def decide(version)
|
62
|
+
def decide(package, version)
|
63
63
|
@attempted_solutions += 1 if @backtracking
|
64
64
|
@backtracking = false;
|
65
65
|
|
66
|
-
decisions[
|
67
|
-
assignment = Assignment.decision(version, decision_level, assignments.length)
|
66
|
+
decisions[package] = version
|
67
|
+
assignment = Assignment.decision(package, version, decision_level, assignments.length)
|
68
68
|
add_assignment(assignment)
|
69
69
|
end
|
70
70
|
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rubygems/requirement'
|
2
|
+
|
3
|
+
module PubGrub
|
4
|
+
module RubyGems
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def requirement_to_range(requirement)
|
8
|
+
ranges = requirement.requirements.map do |(op, ver)|
|
9
|
+
case op
|
10
|
+
when "~>"
|
11
|
+
# TODO: not sure this is correct for prereleases
|
12
|
+
VersionRange.new(min: ver, max: ver.bump, include_min: true)
|
13
|
+
when ">"
|
14
|
+
VersionRange.new(min: ver)
|
15
|
+
when ">="
|
16
|
+
if ver == Gem::Version.new("0")
|
17
|
+
VersionRange.any
|
18
|
+
else
|
19
|
+
VersionRange.new(min: ver, include_min: true)
|
20
|
+
end
|
21
|
+
when "<"
|
22
|
+
VersionRange.new(max: ver)
|
23
|
+
when "<="
|
24
|
+
VersionRange.new(max: ver, include_max: true)
|
25
|
+
when "="
|
26
|
+
VersionRange.new(min: ver, max: ver, include_min: true, include_max: true)
|
27
|
+
when "!="
|
28
|
+
VersionRange.new(min: ver, max: ver, include_min: true, include_max: true).invert
|
29
|
+
else
|
30
|
+
raise "bad version specifier: #{op}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
ranges.inject(&:intersect)
|
35
|
+
end
|
36
|
+
|
37
|
+
def requirement_to_constraint(package, requirement)
|
38
|
+
PubGrub::VersionConstraint.new(package, range: requirement_to_range(requirement))
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse_range(dep)
|
42
|
+
requirement_to_range(Gem::Requirement.new(dep))
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse_constraint(package, dep)
|
46
|
+
range = parse_range(dep)
|
47
|
+
PubGrub::VersionConstraint.new(package, range: range)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -27,14 +27,21 @@ module PubGrub
|
|
27
27
|
@packages = {
|
28
28
|
root: Package.root
|
29
29
|
}
|
30
|
-
@
|
31
|
-
|
32
|
-
}
|
30
|
+
@package_versions = Hash.new{ |h, k| h[k] = [] }
|
31
|
+
|
32
|
+
@deps_by_version = Hash.new { |h, k| h[k] = {} }
|
33
|
+
|
34
|
+
root_version = Package.root_version
|
35
|
+
@package_versions[Package.root] = [root_version]
|
36
|
+
@deps_by_version[Package.root][root_version] = @root_deps
|
33
37
|
|
34
38
|
@package_list.each do |name, version, deps|
|
35
39
|
@packages[name] ||= Package.new(name)
|
36
|
-
|
37
|
-
|
40
|
+
package = @packages[name]
|
41
|
+
|
42
|
+
version = Gem::Version.new(version)
|
43
|
+
@package_versions[package] << version
|
44
|
+
@deps_by_version[package][version] = deps
|
38
45
|
end
|
39
46
|
end
|
40
47
|
|
@@ -44,37 +51,56 @@ module PubGrub
|
|
44
51
|
end
|
45
52
|
alias_method :package, :get_package
|
46
53
|
|
47
|
-
def
|
48
|
-
package
|
54
|
+
def versions_for(package, range=VersionRange.any)
|
55
|
+
@package_versions[package].select do |version|
|
56
|
+
range.include?(version)
|
57
|
+
end
|
49
58
|
end
|
50
59
|
|
51
|
-
def incompatibilities_for(version)
|
52
|
-
|
53
|
-
@
|
54
|
-
|
55
|
-
|
56
|
-
|
60
|
+
def incompatibilities_for(package, version)
|
61
|
+
package_deps = @deps_by_version[package]
|
62
|
+
package_versions = @package_versions[package]
|
63
|
+
package_deps[version].map do |dep_package_name, dep_constraint_name|
|
64
|
+
sorted_versions = package_versions.sort
|
65
|
+
low = high = sorted_versions.index(version)
|
66
|
+
|
67
|
+
# find version low such that all >= low share the same dep
|
68
|
+
while low > 0 &&
|
69
|
+
package_deps[sorted_versions[low - 1]][dep_package_name] == dep_constraint_name
|
70
|
+
low -= 1
|
57
71
|
end
|
58
|
-
|
59
|
-
if
|
60
|
-
|
61
|
-
elsif (bitmap == 1 << version.id)
|
62
|
-
version.name
|
72
|
+
low =
|
73
|
+
if low == 0
|
74
|
+
nil
|
63
75
|
else
|
64
|
-
|
76
|
+
sorted_versions[low]
|
65
77
|
end
|
66
|
-
|
78
|
+
|
79
|
+
# find version high such that all < high share the same dep
|
80
|
+
while high < sorted_versions.length &&
|
81
|
+
package_deps[sorted_versions[high]][dep_package_name] == dep_constraint_name
|
82
|
+
high += 1
|
83
|
+
end
|
84
|
+
high =
|
85
|
+
if high == sorted_versions.length
|
86
|
+
nil
|
87
|
+
else
|
88
|
+
sorted_versions[high]
|
89
|
+
end
|
90
|
+
|
91
|
+
range = VersionRange.new(min: low, max: high, include_min: true)
|
92
|
+
|
93
|
+
self_constraint = VersionConstraint.new(package, range: range)
|
67
94
|
|
68
95
|
dep_package = @packages[dep_package_name]
|
69
96
|
|
70
97
|
if !dep_package
|
71
98
|
# no such package -> this version is invalid
|
72
|
-
|
73
|
-
|
74
|
-
return [Incompatibility.new([Term.new(constraint, true)], cause: :invalid_dependency)]
|
99
|
+
cause = PubGrub::Incompatibility::InvalidDependency.new(dep_package_name, dep_constraint_name)
|
100
|
+
return [Incompatibility.new([Term.new(self_constraint, true)], cause: cause)]
|
75
101
|
end
|
76
102
|
|
77
|
-
dep_constraint =
|
103
|
+
dep_constraint = PubGrub::RubyGems.parse_constraint(dep_package, dep_constraint_name)
|
78
104
|
|
79
105
|
Incompatibility.new([Term.new(self_constraint, true), Term.new(dep_constraint, false)], cause: :dependency)
|
80
106
|
end
|
data/lib/pub_grub/term.rb
CHANGED
@@ -1,41 +1,28 @@
|
|
1
|
-
require '
|
1
|
+
require 'pub_grub/version_range'
|
2
2
|
|
3
3
|
module PubGrub
|
4
4
|
class VersionConstraint
|
5
|
-
attr_reader :package, :
|
5
|
+
attr_reader :package, :range
|
6
6
|
|
7
7
|
# @param package [PubGrub::Package]
|
8
|
-
# @param
|
9
|
-
def initialize(package,
|
8
|
+
# @param range [PubGrub::VersionRange]
|
9
|
+
def initialize(package, range: nil)
|
10
10
|
@package = package
|
11
|
-
@
|
12
|
-
@bitmap = bitmap # Calculated lazily
|
11
|
+
@range = range
|
13
12
|
end
|
14
13
|
|
15
|
-
|
16
|
-
package
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
def self.any(package)
|
21
|
-
new(package, nil, bitmap: (1 << package.versions.count) - 1)
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.bitmap_matching(package)
|
25
|
-
package.versions.select do |version|
|
26
|
-
yield version
|
27
|
-
end.inject(0) do |acc, version|
|
28
|
-
acc | (1 << version.id)
|
14
|
+
class << self
|
15
|
+
def exact(package, version)
|
16
|
+
range = VersionRange.new(min: version, max: version, include_min: true, include_max: true)
|
17
|
+
new(package, range: range)
|
29
18
|
end
|
30
|
-
end
|
31
19
|
|
32
|
-
|
33
|
-
|
20
|
+
def any(package)
|
21
|
+
new(package, range: VersionRange.any)
|
22
|
+
end
|
34
23
|
|
35
|
-
|
36
|
-
|
37
|
-
@bitmap = self.class.bitmap_matching(package) do |version|
|
38
|
-
requirement.satisfied_by?(Gem::Version.new(version.name))
|
24
|
+
def empty(package)
|
25
|
+
new(package, range: VersionRange.empty)
|
39
26
|
end
|
40
27
|
end
|
41
28
|
|
@@ -43,64 +30,33 @@ module PubGrub
|
|
43
30
|
unless package == other.package
|
44
31
|
raise ArgumentError, "Can only intersect between VersionConstraint of the same package"
|
45
32
|
end
|
46
|
-
|
47
|
-
|
48
|
-
else
|
49
|
-
self.class.new(package, constraint + other.constraint, bitmap: bitmap & other.bitmap)
|
50
|
-
end
|
33
|
+
|
34
|
+
self.class.new(package, range: range.intersect(other.range))
|
51
35
|
end
|
52
36
|
|
53
37
|
def union(other)
|
54
38
|
unless package == other.package
|
55
39
|
raise ArgumentError, "Can only intersect between VersionConstraint of the same package"
|
56
40
|
end
|
57
|
-
|
58
|
-
|
59
|
-
else
|
60
|
-
self.class.new(package, "#{constraint_string} OR #{other.constraint_string}", bitmap: bitmap | other.bitmap)
|
61
|
-
end
|
41
|
+
|
42
|
+
self.class.new(package, range: range.union(other.range))
|
62
43
|
end
|
63
44
|
|
64
45
|
def invert
|
65
|
-
|
66
|
-
|
67
|
-
if constraint.length == 0
|
68
|
-
["not >= 0"]
|
69
|
-
elsif constraint.length == 1
|
70
|
-
["not #{constraint[0]}"]
|
71
|
-
else
|
72
|
-
["not (#{constraint_string})"]
|
73
|
-
end
|
74
|
-
self.class.new(package, new_constraint, bitmap: new_bitmap)
|
46
|
+
new_range = range.invert
|
47
|
+
self.class.new(package, range: new_range)
|
75
48
|
end
|
76
49
|
|
77
50
|
def difference(other)
|
78
51
|
intersect(other.invert)
|
79
52
|
end
|
80
53
|
|
81
|
-
def
|
82
|
-
|
83
|
-
bitmap[version.id] == 1
|
84
|
-
end
|
54
|
+
def allows_all?(other)
|
55
|
+
range.allows_all?(other.range)
|
85
56
|
end
|
86
57
|
|
87
|
-
|
88
|
-
|
89
|
-
bitmap.allbits?(other.bitmap)
|
90
|
-
end
|
91
|
-
|
92
|
-
def allows_any?(other)
|
93
|
-
bitmap.anybits?(other.bitmap)
|
94
|
-
end
|
95
|
-
else
|
96
|
-
def allows_all?(other)
|
97
|
-
other_bitmap = other.bitmap
|
98
|
-
(bitmap & other_bitmap) == other_bitmap
|
99
|
-
end
|
100
|
-
|
101
|
-
def allows_any?(other)
|
102
|
-
(bitmap & other.bitmap) != 0
|
103
|
-
end
|
58
|
+
def allows_any?(other)
|
59
|
+
range.intersects?(other.range)
|
104
60
|
end
|
105
61
|
|
106
62
|
def subset?(other)
|
@@ -136,27 +92,24 @@ module PubGrub
|
|
136
92
|
end
|
137
93
|
|
138
94
|
def constraint_string
|
139
|
-
|
140
|
-
when 0
|
95
|
+
if any?
|
141
96
|
">= 0"
|
142
|
-
when 1
|
143
|
-
"#{constraint[0]}"
|
144
97
|
else
|
145
|
-
|
98
|
+
range.to_s
|
146
99
|
end
|
147
100
|
end
|
148
101
|
|
149
102
|
def empty?
|
150
|
-
|
103
|
+
range.empty?
|
151
104
|
end
|
152
105
|
|
153
106
|
# Does this match every version of the package
|
154
107
|
def any?
|
155
|
-
|
108
|
+
range.any?
|
156
109
|
end
|
157
110
|
|
158
111
|
def inspect
|
159
|
-
"#<#{self.class} #{self}
|
112
|
+
"#<#{self.class} #{self}>"
|
160
113
|
end
|
161
114
|
end
|
162
115
|
end
|
@@ -0,0 +1,322 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module 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 intersects?(_)
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
def allows_all?(other)
|
27
|
+
other.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
def include?(_)
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
def any?
|
35
|
+
false
|
36
|
+
end
|
37
|
+
|
38
|
+
def constraints
|
39
|
+
["(no versions)"]
|
40
|
+
end
|
41
|
+
|
42
|
+
def ==(other)
|
43
|
+
other.class == self.class
|
44
|
+
end
|
45
|
+
|
46
|
+
def invert
|
47
|
+
VersionRange.any
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.empty
|
52
|
+
Empty.new
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.any
|
56
|
+
new
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize(min: nil, max: nil, include_min: false, include_max: false)
|
60
|
+
@min = min
|
61
|
+
@max = max
|
62
|
+
@include_min = include_min
|
63
|
+
@include_max = include_max
|
64
|
+
|
65
|
+
if min && max
|
66
|
+
if min > max
|
67
|
+
raise ArgumentError, "min version #{min} must be less than max #{max}"
|
68
|
+
elsif min == max && (!include_min || !include_max)
|
69
|
+
raise ArgumentError, "include_min and include_max must be true when min == max"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def include?(version)
|
75
|
+
compare_version(version) == 0
|
76
|
+
end
|
77
|
+
|
78
|
+
def compare_version(version)
|
79
|
+
if min
|
80
|
+
case version <=> min
|
81
|
+
when -1
|
82
|
+
return -1
|
83
|
+
when 0
|
84
|
+
return -1 if !include_min
|
85
|
+
when 1
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
if max
|
90
|
+
case version <=> max
|
91
|
+
when -1
|
92
|
+
when 0
|
93
|
+
return 1 if !include_max
|
94
|
+
when 1
|
95
|
+
return 1
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
0
|
100
|
+
end
|
101
|
+
|
102
|
+
def strictly_lower?(other)
|
103
|
+
return false if !max || !other.min
|
104
|
+
|
105
|
+
case max <=> other.min
|
106
|
+
when 0
|
107
|
+
!include_max || !other.include_min
|
108
|
+
when -1
|
109
|
+
true
|
110
|
+
when 1
|
111
|
+
false
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def strictly_higher?(other)
|
116
|
+
other.strictly_lower?(self)
|
117
|
+
end
|
118
|
+
|
119
|
+
def intersects?(other)
|
120
|
+
return false if other.empty?
|
121
|
+
return other.intersects?(self) if other.is_a?(VersionUnion)
|
122
|
+
!strictly_lower?(other) && !strictly_higher?(other)
|
123
|
+
end
|
124
|
+
alias_method :allows_any?, :intersects?
|
125
|
+
|
126
|
+
def intersect(other)
|
127
|
+
return self.class.empty unless intersects?(other)
|
128
|
+
return other.intersect(self) if other.is_a?(VersionUnion)
|
129
|
+
|
130
|
+
min_range =
|
131
|
+
if !min
|
132
|
+
other
|
133
|
+
elsif !other.min
|
134
|
+
self
|
135
|
+
else
|
136
|
+
case min <=> other.min
|
137
|
+
when 0
|
138
|
+
include_min ? other : self
|
139
|
+
when -1
|
140
|
+
other
|
141
|
+
when 1
|
142
|
+
self
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
max_range =
|
147
|
+
if !max
|
148
|
+
other
|
149
|
+
elsif !other.max
|
150
|
+
self
|
151
|
+
else
|
152
|
+
case max <=> other.max
|
153
|
+
when 0
|
154
|
+
include_max ? other : self
|
155
|
+
when -1
|
156
|
+
self
|
157
|
+
when 1
|
158
|
+
other
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
self.class.new(
|
163
|
+
min: min_range.min,
|
164
|
+
include_min: min_range.include_min,
|
165
|
+
max: max_range.max,
|
166
|
+
include_max: max_range.include_max
|
167
|
+
)
|
168
|
+
end
|
169
|
+
|
170
|
+
# The span covered by two ranges
|
171
|
+
#
|
172
|
+
# If self and other are contiguous, this builds a union of the two ranges.
|
173
|
+
# (if they aren't you are probably calling the wrong method)
|
174
|
+
def span(other)
|
175
|
+
return self if other.empty?
|
176
|
+
|
177
|
+
min_range =
|
178
|
+
if !min
|
179
|
+
self
|
180
|
+
elsif !other.min
|
181
|
+
other
|
182
|
+
else
|
183
|
+
case min <=> other.min
|
184
|
+
when 0
|
185
|
+
include_min ? self : other
|
186
|
+
when -1
|
187
|
+
self
|
188
|
+
when 1
|
189
|
+
other
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
max_range =
|
194
|
+
if !max
|
195
|
+
self
|
196
|
+
elsif !other.max
|
197
|
+
other
|
198
|
+
else
|
199
|
+
case max <=> other.max
|
200
|
+
when 0
|
201
|
+
include_max ? self : other
|
202
|
+
when -1
|
203
|
+
other
|
204
|
+
when 1
|
205
|
+
self
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
self.class.new(
|
210
|
+
min: min_range.min,
|
211
|
+
include_min: min_range.include_min,
|
212
|
+
max: max_range.max,
|
213
|
+
include_max: max_range.include_max
|
214
|
+
)
|
215
|
+
end
|
216
|
+
|
217
|
+
def union(other)
|
218
|
+
return other.union(self) if other.is_a?(VersionUnion)
|
219
|
+
|
220
|
+
if contiguous_to?(other)
|
221
|
+
span(other)
|
222
|
+
else
|
223
|
+
VersionUnion.union([self, other])
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def contiguous_to?(other)
|
228
|
+
return false if other.empty?
|
229
|
+
|
230
|
+
intersects?(other) ||
|
231
|
+
(min == other.max && (include_min || other.include_max)) ||
|
232
|
+
(max == other.min && (include_max || other.include_min))
|
233
|
+
end
|
234
|
+
|
235
|
+
def allows_all?(other)
|
236
|
+
return true if other.empty?
|
237
|
+
|
238
|
+
if other.is_a?(VersionUnion)
|
239
|
+
return other.ranges.all? { |r| allows_all?(r) }
|
240
|
+
end
|
241
|
+
|
242
|
+
return false if max && !other.max
|
243
|
+
return false if min && !other.min
|
244
|
+
|
245
|
+
if min
|
246
|
+
case min <=> other.min
|
247
|
+
when -1
|
248
|
+
when 0
|
249
|
+
return false if !include_min && other.include_min
|
250
|
+
when 1
|
251
|
+
return false
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
if max
|
256
|
+
case max <=> other.max
|
257
|
+
when -1
|
258
|
+
return false
|
259
|
+
when 0
|
260
|
+
return false if !include_max && other.include_max
|
261
|
+
when 1
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
true
|
266
|
+
end
|
267
|
+
|
268
|
+
def constraints
|
269
|
+
return ["any"] if any?
|
270
|
+
return ["#{min}"] if min == max
|
271
|
+
|
272
|
+
# FIXME: remove this
|
273
|
+
if min && max && include_min && !include_max && min.respond_to?(:bump) && min.bump == max
|
274
|
+
return ["~> #{min}"]
|
275
|
+
end
|
276
|
+
|
277
|
+
c = []
|
278
|
+
c << "#{include_min ? ">=" : ">"} #{min}" if min
|
279
|
+
c << "#{include_max ? "<=" : "<"} #{max}" if max
|
280
|
+
c
|
281
|
+
end
|
282
|
+
|
283
|
+
def any?
|
284
|
+
!min && !max
|
285
|
+
end
|
286
|
+
|
287
|
+
def empty?
|
288
|
+
false
|
289
|
+
end
|
290
|
+
|
291
|
+
def to_s
|
292
|
+
constraints.join(", ")
|
293
|
+
end
|
294
|
+
|
295
|
+
def inspect
|
296
|
+
"#<#{self.class} #{to_s}>"
|
297
|
+
end
|
298
|
+
|
299
|
+
def invert
|
300
|
+
return self.class.empty if any?
|
301
|
+
|
302
|
+
low = VersionRange.new(max: min, include_max: !include_min)
|
303
|
+
high = VersionRange.new(min: max, include_min: !include_max)
|
304
|
+
|
305
|
+
if !min
|
306
|
+
high
|
307
|
+
elsif !max
|
308
|
+
low
|
309
|
+
else
|
310
|
+
low.union(high)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def ==(other)
|
315
|
+
self.class == other.class &&
|
316
|
+
min == other.min &&
|
317
|
+
max == other.max &&
|
318
|
+
include_min == other.include_min &&
|
319
|
+
include_max == other.include_max
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
@@ -32,11 +32,11 @@ module PubGrub
|
|
32
32
|
next_package = choose_package_version
|
33
33
|
end
|
34
34
|
|
35
|
-
result = solution.decisions
|
35
|
+
result = solution.decisions
|
36
36
|
|
37
37
|
logger.info "Solution found after #{solution.attempted_solutions} attempts:"
|
38
|
-
result.each do |version|
|
39
|
-
logger.info "* #{
|
38
|
+
result.each do |package, version|
|
39
|
+
logger.info "* #{package.name} #{version}"
|
40
40
|
end
|
41
41
|
|
42
42
|
result
|
@@ -93,32 +93,40 @@ module PubGrub
|
|
93
93
|
return nil
|
94
94
|
end
|
95
95
|
|
96
|
-
unsatisfied_term =
|
97
|
-
term
|
98
|
-
|
99
|
-
|
96
|
+
unsatisfied_term, versions =
|
97
|
+
unsatisfied.map do |term|
|
98
|
+
range = term.constraint.range
|
99
|
+
[term, source.versions_for(term.package, range)]
|
100
|
+
end.min_by do |(_, v)|
|
101
|
+
v.count
|
102
|
+
end
|
103
|
+
|
104
|
+
package = unsatisfied_term.package
|
105
|
+
version = versions.first
|
100
106
|
|
101
107
|
if version.nil?
|
102
|
-
|
108
|
+
cause = Incompatibility::NoVersions.new(unsatisfied_term)
|
109
|
+
add_incompatibility Incompatibility.new([unsatisfied_term], cause: cause)
|
110
|
+
return package
|
103
111
|
end
|
104
112
|
|
105
113
|
conflict = false
|
106
114
|
|
107
|
-
source.incompatibilities_for(version).each do |incompatibility|
|
115
|
+
source.incompatibilities_for(package, version).each do |incompatibility|
|
108
116
|
add_incompatibility incompatibility
|
109
117
|
|
110
118
|
conflict ||= incompatibility.terms.all? do |term|
|
111
|
-
term.package ==
|
119
|
+
term.package == package || solution.satisfies?(term)
|
112
120
|
end
|
113
121
|
end
|
114
122
|
|
115
123
|
unless conflict
|
116
|
-
logger.info("selecting #{
|
124
|
+
logger.info("selecting #{package.name} #{version}")
|
117
125
|
|
118
|
-
solution.decide(version)
|
126
|
+
solution.decide(package, version)
|
119
127
|
end
|
120
128
|
|
121
|
-
|
129
|
+
package
|
122
130
|
end
|
123
131
|
|
124
132
|
def resolve_conflict(incompatibility)
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PubGrub
|
4
|
+
class VersionUnion
|
5
|
+
attr_reader :ranges
|
6
|
+
|
7
|
+
def self.normalize_ranges(ranges)
|
8
|
+
ranges = ranges.flat_map do |range|
|
9
|
+
if range.is_a?(VersionUnion)
|
10
|
+
range.ranges
|
11
|
+
else
|
12
|
+
[range]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
ranges.reject!(&:empty?)
|
17
|
+
|
18
|
+
return [] if ranges.empty?
|
19
|
+
|
20
|
+
mins, ranges = ranges.partition { |r| !r.min }
|
21
|
+
original_ranges = mins + ranges.sort_by { |r| [r.include_min ? 0 : 1, r.min] }
|
22
|
+
ranges = [original_ranges.shift]
|
23
|
+
original_ranges.each do |range|
|
24
|
+
if ranges.last.contiguous_to?(range)
|
25
|
+
ranges << ranges.pop.span(range)
|
26
|
+
else
|
27
|
+
ranges << range
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
ranges
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.union(ranges)
|
35
|
+
ranges = normalize_ranges(ranges)
|
36
|
+
|
37
|
+
if ranges.size == 0
|
38
|
+
VersionRange.empty
|
39
|
+
elsif ranges.size == 1
|
40
|
+
ranges[0]
|
41
|
+
else
|
42
|
+
new(ranges)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize(ranges)
|
47
|
+
raise ArgumentError unless ranges.all? { |r| r.instance_of?(VersionRange) }
|
48
|
+
@ranges = ranges
|
49
|
+
end
|
50
|
+
|
51
|
+
def include?(version)
|
52
|
+
!!ranges.bsearch {|r| r.compare_version(version) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def intersects?(other)
|
56
|
+
my_ranges = ranges.dup
|
57
|
+
other_ranges =
|
58
|
+
if other.instance_of?(VersionRange)
|
59
|
+
[other]
|
60
|
+
else
|
61
|
+
other.ranges.dup
|
62
|
+
end
|
63
|
+
|
64
|
+
my_range = my_ranges.shift
|
65
|
+
other_range = other_ranges.shift
|
66
|
+
while my_range && other_range
|
67
|
+
if my_range.intersects?(other_range)
|
68
|
+
return true
|
69
|
+
end
|
70
|
+
|
71
|
+
if !my_range.max || (other_range.max && other_range.max < my_range.max)
|
72
|
+
other_range = other_ranges.shift
|
73
|
+
else
|
74
|
+
my_range = my_ranges.shift
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
alias_method :allows_any?, :intersects?
|
79
|
+
|
80
|
+
def allows_all?(other)
|
81
|
+
other_ranges =
|
82
|
+
if other.is_a?(VersionUnion)
|
83
|
+
other.ranges
|
84
|
+
else
|
85
|
+
[other]
|
86
|
+
end
|
87
|
+
|
88
|
+
other_ranges.all? do |other_range|
|
89
|
+
ranges.any? do |range|
|
90
|
+
range.allows_all?(other_range)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def empty?
|
96
|
+
false
|
97
|
+
end
|
98
|
+
|
99
|
+
def any?
|
100
|
+
false
|
101
|
+
end
|
102
|
+
|
103
|
+
def intersect(other)
|
104
|
+
new_ranges = ranges.map{ |r| r.intersect(other) }
|
105
|
+
VersionUnion.union(new_ranges)
|
106
|
+
end
|
107
|
+
|
108
|
+
def invert
|
109
|
+
ranges.map(&:invert).inject(:intersect)
|
110
|
+
end
|
111
|
+
|
112
|
+
def union(other)
|
113
|
+
VersionUnion.union([self, other])
|
114
|
+
end
|
115
|
+
|
116
|
+
def to_s
|
117
|
+
ranges.map(&:to_s).join(" OR ")
|
118
|
+
end
|
119
|
+
|
120
|
+
def inspect
|
121
|
+
"#<#{self.class} #{to_s}>"
|
122
|
+
end
|
123
|
+
|
124
|
+
def ==(other)
|
125
|
+
self.class == other.class &&
|
126
|
+
self.ranges == other.ranges
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/pub_grub.gemspec
CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "pub_grub"
|
7
|
-
spec.version = "0.
|
7
|
+
spec.version = "0.3.0"
|
8
8
|
spec.authors = ["John Hawthorn"]
|
9
9
|
spec.email = ["john@hawthorn.email"]
|
10
10
|
|
@@ -23,4 +23,6 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.add_development_dependency "bundler", "~> 1.16"
|
24
24
|
spec.add_development_dependency "rake", "~> 10.0"
|
25
25
|
spec.add_development_dependency "minitest", "~> 5.0"
|
26
|
+
spec.add_development_dependency "stackprof", "~> 0.2.12"
|
27
|
+
spec.add_development_dependency "minitest-stackprof"
|
26
28
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pub_grub
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Hawthorn
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-11-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,34 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: stackprof
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.2.12
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.2.12
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest-stackprof
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
55
83
|
description: A version solver based on dart's PubGrub
|
56
84
|
email:
|
57
85
|
- john@hawthorn.email
|
@@ -75,11 +103,14 @@ files:
|
|
75
103
|
- lib/pub_grub/incompatibility.rb
|
76
104
|
- lib/pub_grub/package.rb
|
77
105
|
- lib/pub_grub/partial_solution.rb
|
106
|
+
- lib/pub_grub/rubygems.rb
|
78
107
|
- lib/pub_grub/solve_failure.rb
|
79
108
|
- lib/pub_grub/static_package_source.rb
|
80
109
|
- lib/pub_grub/term.rb
|
81
110
|
- lib/pub_grub/version_constraint.rb
|
111
|
+
- lib/pub_grub/version_range.rb
|
82
112
|
- lib/pub_grub/version_solver.rb
|
113
|
+
- lib/pub_grub/version_union.rb
|
83
114
|
- pub_grub.gemspec
|
84
115
|
homepage: https://github.com/jhawthorn/pubgrub
|
85
116
|
licenses:
|
@@ -101,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
101
132
|
version: '0'
|
102
133
|
requirements: []
|
103
134
|
rubyforge_project:
|
104
|
-
rubygems_version: 2.7.
|
135
|
+
rubygems_version: 2.7.7
|
105
136
|
signing_key:
|
106
137
|
specification_version: 4
|
107
138
|
summary: A version solver based on dart's PubGrub
|