pub_grub 0.3.2 → 0.4.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 +1 -1
- data/lib/pub_grub.rb +1 -1
- data/lib/pub_grub/basic_package_source.rb +166 -0
- data/lib/pub_grub/failure_writer.rb +2 -0
- data/lib/pub_grub/incompatibility.rb +4 -1
- data/lib/pub_grub/package.rb +4 -0
- data/lib/pub_grub/static_package_source.rb +19 -75
- data/lib/pub_grub/version_constraint.rb +2 -2
- data/lib/pub_grub/version_solver.rb +28 -14
- data/lib/pub_grub/version_union.rb +1 -1
- data/pub_grub.gemspec +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 906d1263305a0df7b8082cf01fd1abb3ddbd92f294744b1d9131603396be31a1
|
4
|
+
data.tar.gz: 6566971cf3a8ed458d15d21233614351d12b407c3e056f3bc580b0efe810fef0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2d793abfc4b0481e6a056f2355310c60e8450058f777754284e011369d2e420a14c37939a8631b3d1ae01ec3f7b1f7c4250234a245ba4b2b6d6d4fc4ea90a5ee
|
7
|
+
data.tar.gz: 526cc6588093a2ec82b44fdad8a2bf6c09f3520c02d1c9a248e11576ca80e6c2817dc8564573b95d8da637e29ea5bed80379632f176a8729bb7c4eecba227950
|
data/Gemfile.lock
CHANGED
data/lib/pub_grub.rb
CHANGED
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'pub_grub/version_constraint'
|
2
|
+
require 'pub_grub/incompatibility'
|
3
|
+
|
4
|
+
module PubGrub
|
5
|
+
# Types:
|
6
|
+
#
|
7
|
+
# Where possible, PubGrub will accept user-defined types, so long as they quack.
|
8
|
+
#
|
9
|
+
# ## "Package":
|
10
|
+
#
|
11
|
+
# This class will be used to represent the various packages being solved for.
|
12
|
+
# .to_s will be called when displaying errors and debugging info, it should
|
13
|
+
# probably return the package's name.
|
14
|
+
# It must also have a reasonable definition of #== and #hash
|
15
|
+
#
|
16
|
+
# Example classes: String ("rails")
|
17
|
+
#
|
18
|
+
#
|
19
|
+
# ## "Version":
|
20
|
+
#
|
21
|
+
# This class will be used to represent a single version number.
|
22
|
+
#
|
23
|
+
# Versions don't need to store their associated package, however they will
|
24
|
+
# only be compared against other versions of the same package.
|
25
|
+
#
|
26
|
+
# It must be Comparible (and implement <=> reasonably)
|
27
|
+
#
|
28
|
+
# Example classes: Gem::Version, Integer
|
29
|
+
#
|
30
|
+
#
|
31
|
+
# ## "Dependency"
|
32
|
+
#
|
33
|
+
# This class represents the requirement one package has on another. It is
|
34
|
+
# returned by dependencies_for(package, version) and will be passed to
|
35
|
+
# parse_dependency to convert it to a format PubGrub understands.
|
36
|
+
#
|
37
|
+
# It must also have a reasonable definition of #==
|
38
|
+
#
|
39
|
+
# Example classes: String ("~> 1.0"), Gem::Requirement
|
40
|
+
#
|
41
|
+
class BasicPackageSource
|
42
|
+
# Override me!
|
43
|
+
#
|
44
|
+
# This is called per package to find all possible versions of a package.
|
45
|
+
#
|
46
|
+
# It is called at most once per-package
|
47
|
+
#
|
48
|
+
# Returns: Array of versions for a package, in preferred order of selection
|
49
|
+
def all_versions_for(package)
|
50
|
+
raise NotImplementedError
|
51
|
+
end
|
52
|
+
|
53
|
+
# Override me!
|
54
|
+
#
|
55
|
+
# Returns: Hash in the form of { package => requirement, ... }
|
56
|
+
def dependencies_for(package, version)
|
57
|
+
raise NotImplementedError
|
58
|
+
end
|
59
|
+
|
60
|
+
# Override me!
|
61
|
+
#
|
62
|
+
# Convert a (user-defined) dependency into a format PubGrub understands.
|
63
|
+
#
|
64
|
+
# Package is passed to this method but for many implementations is not
|
65
|
+
# needed.
|
66
|
+
#
|
67
|
+
# Returns: either a PubGrub::VersionRange, PubGrub::VersionUnion, or a
|
68
|
+
# PubGrub::VersionConstraint
|
69
|
+
def parse_dependency(package, dependency)
|
70
|
+
raise NotImplementedError
|
71
|
+
end
|
72
|
+
|
73
|
+
# Override me!
|
74
|
+
#
|
75
|
+
# If not overridden, this will call dependencies_for with the root package.
|
76
|
+
#
|
77
|
+
# Returns: Hash in the form of { package => requirement, ... } (see dependencies_for)
|
78
|
+
def root_dependencies
|
79
|
+
dependencies_for(@root_package, @root_version)
|
80
|
+
end
|
81
|
+
|
82
|
+
def initialize
|
83
|
+
@root_package = Package.root
|
84
|
+
@root_version = Package.root_version
|
85
|
+
|
86
|
+
@cached_versions = Hash.new do |h,k|
|
87
|
+
if k == @root_package
|
88
|
+
h[k] = [0]
|
89
|
+
else
|
90
|
+
h[k] = all_versions_for(k)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
@sorted_versions = Hash.new { |h,k| h[k] = @cached_versions[k].sort }
|
94
|
+
|
95
|
+
@cached_dependencies = Hash.new do |packages, package|
|
96
|
+
if package == @root_package
|
97
|
+
packages[package] = {
|
98
|
+
@root_version => root_dependencies
|
99
|
+
}
|
100
|
+
else
|
101
|
+
packages[package] = Hash.new do |versions, version|
|
102
|
+
versions[version] = dependencies_for(package, version)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def versions_for(package, range=VersionRange.any)
|
109
|
+
@cached_versions[package].select do |version|
|
110
|
+
range.include?(version)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def incompatibilities_for(package, version)
|
115
|
+
package_deps = @cached_dependencies[package]
|
116
|
+
sorted_versions = @sorted_versions[package]
|
117
|
+
package_deps[version].map do |dep_package, dep_constraint_name|
|
118
|
+
low = high = sorted_versions.index(version)
|
119
|
+
|
120
|
+
# find version low such that all >= low share the same dep
|
121
|
+
while low > 0 &&
|
122
|
+
package_deps[sorted_versions[low - 1]][dep_package] == dep_constraint_name
|
123
|
+
low -= 1
|
124
|
+
end
|
125
|
+
low =
|
126
|
+
if low == 0
|
127
|
+
nil
|
128
|
+
else
|
129
|
+
sorted_versions[low]
|
130
|
+
end
|
131
|
+
|
132
|
+
# find version high such that all < high share the same dep
|
133
|
+
while high < sorted_versions.length &&
|
134
|
+
package_deps[sorted_versions[high]][dep_package] == dep_constraint_name
|
135
|
+
high += 1
|
136
|
+
end
|
137
|
+
high =
|
138
|
+
if high == sorted_versions.length
|
139
|
+
nil
|
140
|
+
else
|
141
|
+
sorted_versions[high]
|
142
|
+
end
|
143
|
+
|
144
|
+
range = VersionRange.new(min: low, max: high, include_min: true)
|
145
|
+
|
146
|
+
self_constraint = VersionConstraint.new(package, range: range)
|
147
|
+
|
148
|
+
if !@packages.include?(dep_package)
|
149
|
+
# no such package -> this version is invalid
|
150
|
+
end
|
151
|
+
|
152
|
+
dep_constraint = parse_dependency(dep_package, dep_constraint_name)
|
153
|
+
if !dep_constraint
|
154
|
+
# falsey indicates this dependency was invalid
|
155
|
+
cause = PubGrub::Incompatibility::InvalidDependency.new(dep_package, dep_constraint_name)
|
156
|
+
return [Incompatibility.new([Term.new(self_constraint, true)], cause: cause)]
|
157
|
+
elsif !dep_constraint.is_a?(VersionConstraint)
|
158
|
+
# Upgrade range/union to VersionConstraint
|
159
|
+
dep_constraint = VersionConstraint.new(dep_package, range: dep_constraint)
|
160
|
+
end
|
161
|
+
|
162
|
+
Incompatibility.new([Term.new(self_constraint, true), Term.new(dep_constraint, false)], cause: :dependency)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -16,6 +16,10 @@ module PubGrub
|
|
16
16
|
def initialize(terms, cause:)
|
17
17
|
@cause = cause
|
18
18
|
@terms = cleanup_terms(terms)
|
19
|
+
|
20
|
+
if cause == :dependency && @terms.length != 2
|
21
|
+
raise ArgumentError, "a dependency Incompatibility must have exactly two terms. Got #{terms.inspect}"
|
22
|
+
end
|
19
23
|
end
|
20
24
|
|
21
25
|
def failure?
|
@@ -44,7 +48,6 @@ module PubGrub
|
|
44
48
|
when :root
|
45
49
|
"(root dependency)"
|
46
50
|
when :dependency
|
47
|
-
raise unless terms.length == 2
|
48
51
|
"#{terms[0].to_s(allow_every: true)} depends on #{terms[1].invert}"
|
49
52
|
when PubGrub::Incompatibility::InvalidDependency
|
50
53
|
"#{terms[0].to_s(allow_every: true)} depends on unknown package #{cause.package}"
|
data/lib/pub_grub/package.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'pub_grub/package'
|
2
2
|
require 'pub_grub/version_constraint'
|
3
3
|
require 'pub_grub/incompatibility'
|
4
|
+
require 'pub_grub/basic_package_source'
|
4
5
|
|
5
6
|
module PubGrub
|
6
|
-
class StaticPackageSource
|
7
|
+
class StaticPackageSource < BasicPackageSource
|
7
8
|
class DSL
|
8
9
|
def initialize(packages, root_deps)
|
9
10
|
@packages = packages
|
@@ -15,95 +16,38 @@ module PubGrub
|
|
15
16
|
end
|
16
17
|
|
17
18
|
def add(name, version, deps: {})
|
18
|
-
|
19
|
+
version = Gem::Version.new(version)
|
20
|
+
@packages[name] ||= {}
|
21
|
+
raise ArgumentError, "#{name} #{version} declared twice" if @packages[name].key?(version)
|
22
|
+
@packages[name][version] = deps
|
19
23
|
end
|
20
24
|
end
|
21
25
|
|
22
26
|
def initialize
|
23
27
|
@root_deps = {}
|
24
|
-
@
|
25
|
-
yield DSL.new(@package_list, @root_deps)
|
26
|
-
|
27
|
-
@packages = {
|
28
|
-
root: Package.root
|
29
|
-
}
|
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
|
28
|
+
@packages = {}
|
37
29
|
|
38
|
-
@
|
39
|
-
@packages[name] ||= Package.new(name)
|
40
|
-
package = @packages[name]
|
30
|
+
yield DSL.new(@packages, @root_deps)
|
41
31
|
|
42
|
-
|
43
|
-
@package_versions[package] << version
|
44
|
-
@deps_by_version[package][version] = deps
|
45
|
-
end
|
32
|
+
super()
|
46
33
|
end
|
47
34
|
|
48
|
-
def
|
49
|
-
@packages[
|
50
|
-
raise("No such package in source: #{name.inspect}")
|
35
|
+
def all_versions_for(package)
|
36
|
+
@packages[package].keys
|
51
37
|
end
|
52
|
-
alias_method :package, :get_package
|
53
38
|
|
54
|
-
def
|
55
|
-
@
|
56
|
-
range.include?(version)
|
57
|
-
end
|
39
|
+
def root_dependencies
|
40
|
+
@root_deps
|
58
41
|
end
|
59
42
|
|
60
|
-
def
|
61
|
-
|
62
|
-
|
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
|
71
|
-
end
|
72
|
-
low =
|
73
|
-
if low == 0
|
74
|
-
nil
|
75
|
-
else
|
76
|
-
sorted_versions[low]
|
77
|
-
end
|
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)
|
94
|
-
|
95
|
-
dep_package = @packages[dep_package_name]
|
96
|
-
|
97
|
-
if !dep_package
|
98
|
-
# no such package -> this version is invalid
|
99
|
-
cause = PubGrub::Incompatibility::InvalidDependency.new(dep_package_name, dep_constraint_name)
|
100
|
-
return [Incompatibility.new([Term.new(self_constraint, true)], cause: cause)]
|
101
|
-
end
|
43
|
+
def dependencies_for(package, version)
|
44
|
+
@packages[package][version]
|
45
|
+
end
|
102
46
|
|
103
|
-
|
47
|
+
def parse_dependency(package, dependency)
|
48
|
+
return false unless @packages.key?(package)
|
104
49
|
|
105
|
-
|
106
|
-
end
|
50
|
+
PubGrub::RubyGems.parse_constraint(package, dependency)
|
107
51
|
end
|
108
52
|
end
|
109
53
|
end
|
@@ -85,9 +85,9 @@ module PubGrub
|
|
85
85
|
if package == Package.root
|
86
86
|
"root"
|
87
87
|
elsif allow_every && any?
|
88
|
-
"every version of #{package
|
88
|
+
"every version of #{package}"
|
89
89
|
else
|
90
|
-
"#{package
|
90
|
+
"#{package} #{constraint_string}"
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
@@ -8,7 +8,7 @@ module PubGrub
|
|
8
8
|
attr_reader :source
|
9
9
|
attr_reader :solution
|
10
10
|
|
11
|
-
def initialize(source:)
|
11
|
+
def initialize(source:, root: Package.root)
|
12
12
|
@source = source
|
13
13
|
|
14
14
|
# { package => [incompatibility, ...]}
|
@@ -19,29 +19,43 @@ module PubGrub
|
|
19
19
|
@solution = PartialSolution.new
|
20
20
|
|
21
21
|
add_incompatibility Incompatibility.new([
|
22
|
-
Term.new(VersionConstraint.any(
|
22
|
+
Term.new(VersionConstraint.any(root), false)
|
23
23
|
], cause: :root)
|
24
|
+
|
25
|
+
propagate(root)
|
24
26
|
end
|
25
27
|
|
26
|
-
def
|
27
|
-
|
28
|
+
def solved?
|
29
|
+
solution.unsatisfied.empty?
|
30
|
+
end
|
28
31
|
|
29
|
-
|
30
|
-
|
32
|
+
# Returns true if there is more work to be done, false otherwise
|
33
|
+
def work
|
34
|
+
return false if solved?
|
31
35
|
|
32
|
-
|
33
|
-
|
36
|
+
next_package = choose_package_version
|
37
|
+
propagate(next_package)
|
34
38
|
|
35
|
-
|
39
|
+
if solved?
|
40
|
+
logger.info "Solution found after #{solution.attempted_solutions} attempts:"
|
41
|
+
solution.decisions.each do |package, version|
|
42
|
+
logger.info "* #{package} #{version}"
|
43
|
+
end
|
36
44
|
|
37
|
-
|
38
|
-
|
39
|
-
|
45
|
+
false
|
46
|
+
else
|
47
|
+
true
|
40
48
|
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def solve
|
52
|
+
work until solved?
|
41
53
|
|
42
|
-
|
54
|
+
solution.decisions
|
43
55
|
end
|
44
56
|
|
57
|
+
alias_method :result, :solve
|
58
|
+
|
45
59
|
private
|
46
60
|
|
47
61
|
def propagate(initial_package)
|
@@ -121,7 +135,7 @@ module PubGrub
|
|
121
135
|
end
|
122
136
|
|
123
137
|
unless conflict
|
124
|
-
logger.info("selecting #{package
|
138
|
+
logger.info("selecting #{package} #{version}")
|
125
139
|
|
126
140
|
solution.decide(package, version)
|
127
141
|
end
|
@@ -55,7 +55,7 @@ module PubGrub
|
|
55
55
|
def select_versions(all_versions)
|
56
56
|
versions = []
|
57
57
|
ranges.inject(all_versions) do |acc, range|
|
58
|
-
|
58
|
+
_, matching, higher = range.partition_versions(acc)
|
59
59
|
versions.concat matching
|
60
60
|
higher
|
61
61
|
end
|
data/pub_grub.gemspec
CHANGED
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.4.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-12-
|
11
|
+
date: 2018-12-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -99,6 +99,7 @@ files:
|
|
99
99
|
- bin/setup
|
100
100
|
- lib/pub_grub.rb
|
101
101
|
- lib/pub_grub/assignment.rb
|
102
|
+
- lib/pub_grub/basic_package_source.rb
|
102
103
|
- lib/pub_grub/failure_writer.rb
|
103
104
|
- lib/pub_grub/incompatibility.rb
|
104
105
|
- lib/pub_grub/package.rb
|
@@ -132,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
132
133
|
version: '0'
|
133
134
|
requirements: []
|
134
135
|
rubyforge_project:
|
135
|
-
rubygems_version: 2.7.
|
136
|
+
rubygems_version: 2.7.6
|
136
137
|
signing_key:
|
137
138
|
specification_version: 4
|
138
139
|
summary: A version solver based on dart's PubGrub
|