pub_grub 0.2.0 → 0.3.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 +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
|