gel 0.3.0 → 0.8.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +26 -3
  3. data/RELEASING.md +12 -0
  4. data/exe/gel +4 -2
  5. data/gemlib/gel/stub.rb +20 -0
  6. data/lib/gel/catalog/common.rb +4 -2
  7. data/lib/gel/catalog/compact_index.rb +6 -10
  8. data/lib/gel/catalog/dependency_index.rb +10 -10
  9. data/lib/gel/catalog/legacy_index.rb +4 -6
  10. data/lib/gel/catalog/marshal_hacks.rb +2 -0
  11. data/lib/gel/catalog.rb +33 -52
  12. data/lib/gel/catalog_set.rb +100 -0
  13. data/lib/gel/command/help.rb +13 -2
  14. data/lib/gel/command/lock.rb +3 -3
  15. data/lib/gel/command/open.rb +24 -0
  16. data/lib/gel/command/shell_setup.rb +11 -8
  17. data/lib/gel/command/stub.rb +45 -2
  18. data/lib/gel/command/version.rb +7 -0
  19. data/lib/gel/command.rb +43 -6
  20. data/lib/gel/compatibility/rubygems.rb +10 -197
  21. data/lib/gel/compatibility.rb +2 -2
  22. data/lib/gel/config.rb +41 -7
  23. data/lib/gel/db.rb +93 -83
  24. data/lib/gel/direct_gem.rb +16 -4
  25. data/lib/gel/environment.rb +542 -249
  26. data/lib/gel/error.rb +156 -24
  27. data/lib/gel/gemfile_parser.rb +74 -12
  28. data/lib/gel/gemspec_parser.rb +26 -7
  29. data/lib/gel/git_catalog.rb +15 -3
  30. data/lib/gel/git_depot.rb +62 -28
  31. data/lib/gel/httpool.rb +5 -2
  32. data/lib/gel/installer.rb +61 -23
  33. data/lib/gel/lock_loader.rb +87 -112
  34. data/lib/gel/lock_parser.rb +23 -31
  35. data/lib/gel/locked_store.rb +30 -21
  36. data/lib/gel/multi_store.rb +13 -4
  37. data/lib/gel/null_solver.rb +67 -0
  38. data/lib/gel/package/abortable.rb +18 -0
  39. data/lib/gel/package/installer.rb +124 -49
  40. data/lib/gel/package.rb +21 -4
  41. data/lib/gel/path_catalog.rb +1 -1
  42. data/lib/gel/pinboard.rb +4 -2
  43. data/lib/gel/platform.rb +38 -0
  44. data/lib/gel/pub_grub/package.rb +67 -0
  45. data/lib/gel/pub_grub/preference_strategy.rb +10 -6
  46. data/lib/gel/pub_grub/solver.rb +37 -0
  47. data/lib/gel/pub_grub/source.rb +64 -92
  48. data/lib/gel/resolved_gem_set.rb +234 -0
  49. data/lib/gel/runtime.rb +3 -3
  50. data/lib/gel/set.rb +62 -0
  51. data/lib/gel/stdlib.rb +83 -0
  52. data/lib/gel/store.rb +94 -25
  53. data/lib/gel/store_catalog.rb +2 -2
  54. data/lib/gel/store_gem.rb +54 -6
  55. data/lib/gel/stub_set.rb +32 -2
  56. data/lib/gel/support/cgi_escape.rb +34 -0
  57. data/lib/gel/support/gem_platform.rb +0 -2
  58. data/lib/gel/support/sha512.rb +142 -0
  59. data/lib/gel/support/tar/tar_writer.rb +2 -2
  60. data/lib/gel/tail_file.rb +2 -1
  61. data/lib/gel/util.rb +108 -0
  62. data/lib/gel/vendor/pstore.rb +3 -0
  63. data/lib/gel/vendor/pub_grub.rb +3 -0
  64. data/lib/gel/vendor/ruby_digest.rb +3 -0
  65. data/lib/gel/vendor_catalog.rb +38 -0
  66. data/lib/gel/version.rb +1 -1
  67. data/lib/gel.rb +15 -0
  68. data/man/man1/gel-exec.1 +1 -1
  69. data/man/man1/gel-install.1 +1 -1
  70. data/man/man1/gel.1 +14 -1
  71. data/{lib/gel/compatibility → slib}/bundler/cli.rb +0 -0
  72. data/{lib/gel/compatibility → slib}/bundler/friendly_errors.rb +0 -0
  73. data/{lib/gel/compatibility/rubygems/dependency_installer.rb → slib/bundler/gem_helper.rb} +0 -0
  74. data/slib/bundler/gem_tasks.rb +0 -0
  75. data/{lib/gel/compatibility → slib}/bundler/setup.rb +0 -0
  76. data/{lib/gel/compatibility → slib}/bundler.rb +39 -3
  77. data/{lib/gel/compatibility → slib}/rubygems/command.rb +0 -0
  78. data/slib/rubygems/dependency_installer.rb +12 -0
  79. data/{lib/gel/compatibility → slib}/rubygems/gem_runner.rb +0 -0
  80. data/slib/rubygems/package.rb +6 -0
  81. data/slib/rubygems/package_task.rb +7 -0
  82. data/slib/rubygems/specification.rb +0 -0
  83. data/slib/rubygems/version.rb +0 -0
  84. data/slib/rubygems.rb +297 -0
  85. data/vendor/pstore/LICENSE.txt +22 -0
  86. data/vendor/pstore/lib/pstore.rb +488 -0
  87. data/vendor/pub_grub/LICENSE.txt +21 -0
  88. data/vendor/pub_grub/lib/pub_grub/assignment.rb +20 -0
  89. data/vendor/pub_grub/lib/pub_grub/basic_package_source.rb +183 -0
  90. data/vendor/pub_grub/lib/pub_grub/failure_writer.rb +182 -0
  91. data/vendor/pub_grub/lib/pub_grub/incompatibility.rb +143 -0
  92. data/vendor/pub_grub/lib/pub_grub/package.rb +35 -0
  93. data/vendor/pub_grub/lib/pub_grub/partial_solution.rb +121 -0
  94. data/vendor/pub_grub/lib/pub_grub/rubygems.rb +45 -0
  95. data/vendor/pub_grub/lib/pub_grub/solve_failure.rb +17 -0
  96. data/vendor/pub_grub/lib/pub_grub/static_package_source.rb +53 -0
  97. data/vendor/pub_grub/lib/pub_grub/term.rb +105 -0
  98. data/vendor/pub_grub/lib/pub_grub/version.rb +3 -0
  99. data/vendor/pub_grub/lib/pub_grub/version_constraint.rb +124 -0
  100. data/vendor/pub_grub/lib/pub_grub/version_range.rb +399 -0
  101. data/vendor/pub_grub/lib/pub_grub/version_solver.rb +247 -0
  102. data/vendor/pub_grub/lib/pub_grub/version_union.rb +174 -0
  103. data/vendor/pub_grub/lib/pub_grub.rb +31 -0
  104. data/vendor/ruby-digest/UNLICENSE +24 -0
  105. data/vendor/ruby-digest/lib/ruby_digest.rb +812 -0
  106. metadata +95 -19
data/lib/gel/installer.rb CHANGED
@@ -9,6 +9,9 @@ require_relative "package"
9
9
  require_relative "package/installer"
10
10
 
11
11
  class Gel::Installer
12
+ class SkipCatalog < Exception
13
+ end
14
+
12
15
  DOWNLOAD_CONCURRENCY = 6
13
16
  COMPILE_CONCURRENCY = 4
14
17
 
@@ -62,6 +65,8 @@ class Gel::Installer
62
65
  end
63
66
 
64
67
  def install_gem(catalogs, name, version)
68
+ raise "Refusing to install incompatible #{name.inspect}" if Gel::Environment::IGNORE_LIST.include?(name)
69
+
65
70
  synchronize do
66
71
  raise "catalogs is nil" if catalogs.nil?
67
72
  @pending[name] += 1
@@ -72,6 +77,13 @@ class Gel::Installer
72
77
  end
73
78
 
74
79
  def load_git_gem(remote, revision, name)
80
+ dir = @git_depot.git_path(remote, revision)
81
+
82
+ if Dir.exist?(dir)
83
+ # already checked out; we assume it's also compiled if necessary
84
+ return
85
+ end
86
+
75
87
  synchronize do
76
88
  @pending[name] += 1
77
89
  @download_pool.queue(name) do
@@ -81,22 +93,35 @@ class Gel::Installer
81
93
  end
82
94
 
83
95
  def work_git(remote, revision, name)
84
- @git_depot.checkout(remote, revision)
96
+ dir = @git_depot.checkout(remote, revision)
85
97
 
86
- @messages << "Using #{name} (git)\n"
87
- @pending[name] -= 1
88
- end
98
+ if filename = ["#{dir}/#{name}.gemspec", "#{dir}/#{name}/#{name}.gemspec"].detect { |f| File.exist?(f) }
99
+ spec = Gel::GemspecParser.parse(File.read(filename), filename)
100
+ g = Gel::Package::Installer::GitCompiler.new(spec, store, dir)
89
101
 
90
- def download_gem(catalogs, name, version)
91
- catalogs.each do |catalog|
92
- if fpath = catalog.cached_gem(name, version)
93
- return fpath
102
+ known_dependencies g.spec.name => g.spec.runtime_dependencies.map(&:first)
103
+ if g.needs_compile?
104
+ synchronize do
105
+ add_weight name, 1000
106
+
107
+ @compile_pool.queue(g.spec.name) do
108
+ work_compile(g)
109
+ end
110
+ end
111
+ else
112
+ work_install(g)
94
113
  end
114
+ else
115
+ clear_pending(name)
95
116
  end
117
+ end
96
118
 
119
+ def download_gem(catalogs, name, version)
97
120
  catalogs.each do |catalog|
98
121
  begin
99
- return catalog.download_gem(name, version)
122
+ if fpath = catalog.download_gem(name, version)
123
+ return fpath
124
+ end
100
125
  rescue Net::HTTPExceptions
101
126
  end
102
127
  end
@@ -131,21 +156,34 @@ class Gel::Installer
131
156
  end
132
157
  end
133
158
 
134
- g.compile
135
- work_install(g)
159
+ begin
160
+ g.compile
161
+ rescue
162
+ clear_pending(g.spec.name)
163
+ raise
164
+ else
165
+ work_install(g)
166
+ end
136
167
  end
137
168
 
138
169
  def work_install(g)
139
- @messages << "Installing #{g.spec.name} (#{g.spec.version})\n"
140
- g.install
141
- @pending[g.spec.name] -= 1
170
+ if g.is_a?(Gel::Package::Installer::GemInstaller)
171
+ @messages << "Installing #{g.spec.name} (#{g.spec.version})\n"
172
+ g.install
173
+ end
174
+
175
+ clear_pending(g.spec.name)
176
+ end
177
+
178
+ def clear_pending(name)
179
+ @pending[name] -= 1
142
180
 
143
181
  synchronize do
144
182
  compile_recheck, @compile_waiting = @compile_waiting, []
145
183
 
146
- compile_recheck.each do |g|
147
- @compile_pool.queue(g.spec.name) do
148
- work_compile(g)
184
+ compile_recheck.each do |gem|
185
+ @compile_pool.queue(gem.spec.name) do
186
+ work_compile(gem)
149
187
  end
150
188
  end
151
189
  end
@@ -202,7 +240,7 @@ class Gel::Installer
202
240
  if output
203
241
  output.write "Installed #{@download_pool.count - errors.size} of #{@download_pool.count} gems\n\nErrors encountered with #{errors.size} gems:\n\n"
204
242
  errors.each do |(_, name), exception|
205
- output.write "#{name}\n #{exception}\n\n"
243
+ output.write "#{name}\n#{exception.to_s.gsub(/^(?=.)/, " ")}\n\n"
206
244
  end
207
245
  end
208
246
 
@@ -218,12 +256,12 @@ class Gel::Installer
218
256
 
219
257
  def compile_ready?(name)
220
258
  @dependencies[name].all? do |dep|
221
- if @pending[dep] == 0
222
- compile_ready?(dep)
223
- elsif @download_pool.errors.any? { |(_, failed_name), ex| failed_name == dep }
224
- raise "Depends on #{dep.inspect}, which failed to download"
259
+ if @download_pool.errors.any? { |(_, failed_name), ex| failed_name == dep }
260
+ raise Gel::Error::ExtensionDependencyError.new(dependency: dep, failure: "download")
225
261
  elsif @compile_pool.errors.any? { |(_, failed_name), ex| failed_name == dep }
226
- raise "Depends on #{dep.inspect}, which failed to compile"
262
+ raise Gel::Error::ExtensionDependencyError.new(dependency: dep, failure: "compile")
263
+ elsif @pending[dep] == 0
264
+ compile_ready?(dep)
227
265
  else
228
266
  false
229
267
  end
@@ -1,64 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "resolved_gem_set"
4
+ require_relative "git_catalog"
5
+ require_relative "path_catalog"
6
+
7
+ require_relative "support/gem_platform"
8
+
3
9
  class Gel::LockLoader
4
- attr_reader :filename
5
10
  attr_reader :gemfile
6
11
 
7
- def initialize(filename, gemfile = nil)
8
- @filename = filename
12
+ def initialize(gem_set, gemfile = nil)
13
+ @gem_set = gem_set
9
14
  @gemfile = gemfile
10
15
  end
11
16
 
12
- def lock_content
13
- @lock_content ||= Gel::LockParser.new.parse(File.read(filename))
14
- end
15
-
16
- def each_gem
17
- lock_content.each do |(section, body)|
18
- case section
19
- when "GEM", "PATH", "GIT"
20
- specs = body["specs"]
21
- specs.each do |gem_spec, dep_specs|
22
- gem_spec =~ /\A(.+) \(([^-]+)(?:-(.+))?\)\z/
23
- name, version, platform = $1, $2, $3
24
-
25
- if dep_specs
26
- deps = dep_specs.map do |spec|
27
- spec =~ /\A(.+?)(?: \((.+)\))?\z/
28
- [$1, $2 ? $2.split(", ") : []]
29
- end
30
- else
31
- deps = []
32
- end
33
-
34
- sym =
35
- case section
36
- when "GEM"; :gem
37
- when "PATH"; :path
38
- when "GIT"; :git
39
- end
40
- yield sym, body, name, version, platform, deps
41
- end
42
- when "PLATFORMS", "DEPENDENCIES"
43
- when "RUBY VERSION"
44
- when "BUNDLED WITH"
45
- else
46
- warn "Unknown lockfile section #{section.inspect}"
47
- end
48
- end
49
- end
50
-
51
- def bundler_version
52
- _, (version,) = lock_content.assoc("BUNDLED WITH")
53
- version
54
- end
55
-
56
17
  def gem_names
57
- names = []
58
- each_gem do |section, body, name, version, platform, deps|
59
- names << name
60
- end
61
- names
18
+ @gem_set.gem_names
62
19
  end
63
20
 
64
21
  def activate(env, base_store, install: false, output: nil)
@@ -71,95 +28,113 @@ class Gel::LockLoader
71
28
  installer = Gel::Installer.new(base_store)
72
29
  end
73
30
 
74
- filtered_gems = Hash.new(nil)
75
- top_gems = []
31
+ gems_to_process = []
76
32
  if gemfile && env
77
- gemfile.gems.each do |name, *|
78
- filtered_gems[name] = false
79
- end
80
33
  env.filtered_gems(gemfile.gems).each do |name, *|
81
- top_gems << name
82
- filtered_gems[name] = true
83
- end
84
- elsif pair = lock_content.assoc("DEPENDENCIES")
85
- _, list = pair
86
- top_gems = list.map { |name| name.split(" ", 2)[0].chomp("!") }
87
- top_gems.each do |name|
88
- filtered_gems[name] = true
34
+ gems_to_process << name
89
35
  end
36
+ elsif list = @gem_set.dependency_names
37
+ gems_to_process = list
90
38
  end
91
39
 
92
- gems = {}
93
- each_gem do |section, body, name, version, platform, deps|
94
- next if env && !env.platform?(platform)
95
-
96
- gems[name] = [section, body, version, platform, deps]
40
+ local_platform = Gel::Support::GemPlatform.local
41
+ all_gems = @gem_set.gems
97
42
 
98
- installer.known_dependencies name => deps.map(&:first) if installer
43
+ deferred_direct_gem = lambda do |dir, name, version|
44
+ -> { Gel::DirectGem.new(dir, name, version) }
99
45
  end
100
46
 
101
- walk = lambda do |name|
102
- filtered_gems[name] = true
103
- next unless gems[name]
104
- gems[name].last.map(&:first).each do |dep_name|
105
- walk[dep_name] unless filtered_gems[dep_name]
106
- end
107
- end
47
+ processed_gems = {}
48
+ while name = gems_to_process.shift
49
+ next if processed_gems[name]
50
+ processed_gems[name] = true
108
51
 
109
- top_gems.each(&walk)
52
+ next if Gel::Environment::IGNORE_LIST.include?(name)
110
53
 
111
- require_relative "git_depot"
112
- require_relative "work_pool"
54
+ next unless all_versions = all_gems[name]
113
55
 
114
- Gel::WorkPool.new(8) do |work_pool|
115
- git_depot = Gel::GitDepot.new(base_store)
56
+ resolved_gems = all_versions.select do |rg|
57
+ local_platform =~ rg.platform || rg.platform.nil?
58
+ end.sort_by { |rg| rg.platform&.size || 0 }.reverse
116
59
 
117
- gems.each do |name, (section, body, version, platform, _deps)|
118
- next unless filtered_gems[name]
60
+ next if resolved_gems.empty?
119
61
 
120
- if section == :gem
121
- if installer && !base_store.gem?(name, version, platform)
122
- require_relative "catalog"
123
- catalogs = body["remote"].map { |r| Gel::Catalog.new(r, work_pool: work_pool) }
124
- installer.install_gem(catalogs, name, platform ? "#{version}-#{platform}" : version)
125
- end
62
+ resolved_gem = resolved_gems.first
63
+
64
+
65
+ case resolved_gem.catalog
66
+ when Gel::GitCatalog
67
+ dir = resolved_gem.catalog.path
126
68
 
127
- locks[name] = version
128
- else
129
- if section == :git
130
- remote = body["remote"].first
131
- revision = body["revision"].first
69
+ installer.known_dependencies name => resolved_gem.deps.map(&:first) if installer
70
+ installer&.load_git_gem(resolved_gem.catalog.remote, resolved_gem.catalog.revision, name)
132
71
 
133
- dir = git_depot.git_path(remote, revision)
134
- if installer && !Dir.exist?(dir)
135
- installer.load_git_gem(remote, revision, name)
72
+ locks[name] = deferred_direct_gem.call(dir, name, resolved_gem.version)
73
+ when Gel::PathCatalog
74
+ path = resolved_gem.catalog.path
136
75
 
137
- locks[name] = -> { Gel::DirectGem.new(dir, name, version) }
138
- next
76
+ dir = File.expand_path(path, File.dirname(@gem_set.filename))
77
+
78
+ installer.known_dependencies name => resolved_gem.deps.map(&:first) if installer
79
+ locks[name] = Gel::DirectGem.new(dir, name, resolved_gem.version)
80
+ else
81
+ unless resolved_gem = resolved_gems.find { |rg| base_store.gem?(name, rg.version, rg.platform) }
82
+ if installer
83
+ require_relative "catalog"
84
+
85
+ catalogs = @gem_set.server_catalogs
86
+
87
+ if resolved_gems.size > 1
88
+ skipped_matches = []
89
+
90
+ catalog_infos = catalogs.map { |c| c.gem_info(name) }
91
+ resolved_gems.each do |rg|
92
+ catalog_infos.each do |info|
93
+ s = rg.platform ? "#{rg.version}-#{rg.platform}" : rg.version
94
+ if i = info[s]
95
+ if i[:ruby] && !Gel::Support::GemRequirement.new(i[:ruby].split("&")).satisfied_by?(Gel::Support::GemVersion.new(RUBY_VERSION))
96
+ skipped_matches << s
97
+ else
98
+ resolved_gem = rg
99
+ break
100
+ end
101
+ end
102
+ end
103
+
104
+ break if resolved_gem
105
+ end
106
+
107
+ if resolved_gem.nil?
108
+ raise UnsatisfiableRubyVersionError.new(name: name, running: RUBY_VERSION, attempted_platforms: skipped_matches)
109
+ end
110
+ else
111
+ resolved_gem = resolved_gems.first
139
112
  end
113
+
114
+ installer.known_dependencies name => resolved_gem.deps.map(&:first)
115
+ installer.install_gem(catalogs, name, resolved_gem.platform ? "#{resolved_gem.version}-#{resolved_gem.platform}" : resolved_gem.version)
140
116
  else
141
- dir = File.expand_path(body["remote"].first, File.dirname(filename))
117
+ raise Gel::Error::MissingGemError.new(name: name)
142
118
  end
143
-
144
- locks[name] = Gel::DirectGem.new(dir, name, version)
145
119
  end
120
+
121
+ locks[name] = resolved_gem.version.to_s
146
122
  end
147
123
 
148
- installer.wait(output) if installer
149
124
 
150
- locks.each do |name, locked|
151
- locks[name] = locked.call if locked.is_a?(Proc)
125
+ resolved_gem.deps.map(&:first).each do |dep_name|
126
+ gems_to_process.unshift dep_name
152
127
  end
153
128
  end
154
129
 
155
- locked_store.lock(locks)
156
-
157
- if env
158
- env.open(locked_store)
130
+ installer.wait(output) if installer
159
131
 
160
- env.gems_from_lock(locks)
132
+ locks.each do |name, locked|
133
+ locks[name] = locked.call if locked.is_a?(Proc)
161
134
  end
162
135
 
136
+ locked_store.lock(locks)
137
+
163
138
  locked_store
164
139
  end
165
140
  end
@@ -1,34 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "strscan"
4
-
5
3
  class Gel::LockParser
6
- def parse(content)
4
+ def parse(input)
7
5
  sections = []
8
6
 
9
- scanner = StringScanner.new(content)
10
- while section = scanner.scan(/^(\w.+)\n/)
11
- section.chomp!
7
+ input = input.dup
8
+ while input.sub!(/\A(\w.+)\n/, "")
9
+ section = $1
10
+
12
11
  case section
13
12
  when "GIT", "PATH", "GEM"
14
13
  content = {}
15
- while scanner.skip(/^ \b/)
16
- label = scanner.scan(/[^:]+:/).chop
17
- if scanner.skip(/ /)
18
- value = scanner.scan(/.*/)
19
- scanner.skip(/\n/)
20
- (content[label] ||= []) << value
21
- else
22
- scanner.skip(/\n/)
14
+ while input.sub!(/\A {2}\b([^:]+):(\s)/, "")
15
+ label = $1
16
+ separator = $2
17
+
18
+ if separator == "\n"
23
19
  value = []
24
- while scanner.skip(/^ \b/)
25
- entry = scanner.scan(/.*/)
26
- scanner.skip(/\n/)
20
+ while input.sub!(/\A {4}\b(.*)\n/, "")
21
+ entry = $1
27
22
  children = []
28
- while scanner.skip(/^ \b/)
29
- child = scanner.scan(/.*/)
30
- children << child
31
- scanner.skip(/\n/)
23
+ while input.sub!(/\A {6}\b(.*)\n/, "")
24
+ children << $1
32
25
  end
33
26
  if children.empty?
34
27
  value << [entry]
@@ -37,24 +30,23 @@ class Gel::LockParser
37
30
  end
38
31
  end
39
32
  content[label] = value
33
+ else
34
+ input.sub!(/\A(.*)\n/, "")
35
+ (content[label] ||= []) << $1
40
36
  end
41
37
  end
42
38
  when "PLATFORMS", "DEPENDENCIES"
43
39
  content = []
44
- while scanner.skip(/^ \b/)
45
- entry = scanner.scan(/.*/)
46
- content << entry
47
- scanner.skip(/\n/)
40
+ while input.sub!(/\A {2}\b(.*)\n/, "")
41
+ content << $1
48
42
  end
49
- when "BUNDLED WITH"
43
+ when "BUNDLED WITH", "RUBY VERSION"
50
44
  content = []
51
- while scanner.skip(/^ \b/)
52
- entry = scanner.scan(/.*/)
53
- content << entry
54
- scanner.skip(/\n/)
45
+ while input.sub!(/\A {3}\b(.*)\n/, "")
46
+ content << $1
55
47
  end
56
48
  end
57
- scanner.skip(/\n+/)
49
+ input.sub!(/\A\n+/, "")
58
50
 
59
51
  sections << [section, content]
60
52
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  class Gel::LockedStore
4
4
  attr_reader :inner
5
+ attr_reader :locked_versions
5
6
 
6
7
  def initialize(inner)
7
8
  @inner = inner
@@ -11,6 +12,10 @@ class Gel::LockedStore
11
12
  @full_cache = false
12
13
  end
13
14
 
15
+ def stub_set
16
+ @inner.stub_set
17
+ end
18
+
14
19
  def paths
15
20
  @inner.paths
16
21
  end
@@ -20,7 +25,7 @@ class Gel::LockedStore
20
25
  end
21
26
 
22
27
  def inspect
23
- content = @locked_versions.map { |name, version| "#{name}=#{version.is_a?(String) ? version : version.root}" }
28
+ content = (@locked_versions || []).map { |name, version| "#{name}=#{version.is_a?(String) ? version : version.root}" }
24
29
  content = ["(none)"] if content.empty?
25
30
  content.sort!
26
31
 
@@ -33,8 +38,8 @@ class Gel::LockedStore
33
38
  inner_versions = {}
34
39
  locks.each do |name, version|
35
40
  if version.is_a?(Gel::StoreGem)
36
- version.libs do |file, subdir|
37
- @lib_cache[file] << [version, subdir]
41
+ version.libs do |file, subdir, ext|
42
+ @lib_cache[file] << [version, subdir, ext]
38
43
  end
39
44
  else
40
45
  inner_versions[name] = version
@@ -43,8 +48,8 @@ class Gel::LockedStore
43
48
 
44
49
  g = @inner.gems(inner_versions)
45
50
  @inner.libs_for_gems(inner_versions) do |name, version, subs|
46
- subs.each do |subdir, files|
47
- v = [g[name], subdir]
51
+ subs.each do |(subdir, ext), files|
52
+ v = [g[name], subdir, ext]
48
53
  files.each do |file|
49
54
  @lib_cache[file] << v
50
55
  end
@@ -83,29 +88,33 @@ class Gel::LockedStore
83
88
  end
84
89
 
85
90
  def gems_for_lib(file)
86
- if c = @lib_cache.fetch(file, nil)
87
- c.each { |gem, subdir| yield gem, subdir }
88
- return
89
- end
91
+ search_name, search_ext = Gel::Util.split_filename_for_require(file)
90
92
 
91
- hits = []
92
- unless @full_cache
93
- @inner.gems_for_lib(file) do |gem, subdir|
94
- if locked?(gem)
95
- hits << [gem, subdir]
96
- yield gem, subdir
93
+ unless hits = @lib_cache.fetch(search_name, nil)
94
+ hits = []
95
+
96
+ unless @full_cache
97
+ @inner.gems_for_lib(search_name) do |gem, subdir, ext|
98
+ if locked?(gem)
99
+ hits << [gem, subdir, ext]
100
+ end
97
101
  end
98
102
  end
99
- end
100
103
 
101
- locked_gems.each do |gem|
102
- if File.exist?(gem.path(file) + ".rb")
103
- hits << [gem, nil]
104
- yield gem, nil
104
+ locked_gems.each do |gem|
105
+ gem.entries_for_lib(search_name) do |subdir, ext|
106
+ hits << [gem, subdir, ext]
107
+ end
105
108
  end
109
+
110
+ @lib_cache[file] = hits
106
111
  end
107
112
 
108
- @lib_cache[file] = hits
113
+ hits.each do |gem, subdir, ext|
114
+ if Gel::Util.ext_matches_requested?(ext, search_ext)
115
+ yield gem, subdir, ext
116
+ end
117
+ end
109
118
  end
110
119
 
111
120
  def each(gem_name = nil)
@@ -10,12 +10,21 @@ class Gel::MultiStore
10
10
  attr_reader :monitor
11
11
 
12
12
  def initialize(root, stores)
13
- @root = File.realpath(File.expand_path(root)) if root
13
+ @root = root && File.realpath(File.expand_path(root))
14
14
  @stores = stores
15
15
 
16
16
  @monitor = Monitor.new
17
17
  end
18
18
 
19
+ def marshal_dump
20
+ [@root, @stores]
21
+ end
22
+
23
+ def marshal_load(v)
24
+ @root, @stores = v
25
+ @monitor = Monitor.new
26
+ end
27
+
19
28
  def stub_set
20
29
  @stub_set ||= Gel::StubSet.new(@root)
21
30
  end
@@ -47,8 +56,8 @@ class Gel::MultiStore
47
56
 
48
57
  def gems_for_lib(file)
49
58
  @stores.each do |_, store|
50
- store.gems_for_lib(file) do |gem, subdir|
51
- yield gem, subdir
59
+ store.gems_for_lib(file) do |gem, subdir, ext|
60
+ yield gem, subdir, ext
52
61
  end
53
62
  end
54
63
  end
@@ -73,7 +82,7 @@ class Gel::MultiStore
73
82
  result = {}
74
83
 
75
84
  @stores.each do |_, store|
76
- result.update(store.gems(name_version_pairs)) do |l, r|
85
+ result.update(store.gems(name_version_pairs)) do |_, l, r|
77
86
  l
78
87
  end
79
88
  end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Gel::NullSolver
4
+ NullPackage = Struct.new(:name, :platform)
5
+
6
+ def initialize(gemfile:, catalog_set:, platforms:)
7
+ @gemfile = gemfile
8
+ @catalog_set = catalog_set
9
+ @platforms = platforms
10
+
11
+ @platform = @platforms.first
12
+
13
+ @packages = Hash.new { |h, k| h[k] = NullPackage.new(k, @platform) }
14
+
15
+ @solution = {}
16
+ @constraints = Hash.new { |h, k| h[k] = [] }
17
+
18
+ @gemfile.gems.each do |name, constraints, options|
19
+ raise "NullSolver can't apply platform constraints" if options[:platforms]
20
+ @constraints[name].concat(constraints || [])
21
+ end
22
+ end
23
+
24
+ def solved?
25
+ gems_to_solve.empty?
26
+ end
27
+
28
+ def work
29
+ name = gems_to_solve.first
30
+
31
+ req = Gel::Support::GemRequirement.new(@constraints[name])
32
+
33
+ choice =
34
+ @catalog_set.entries_for(@packages[name]).map(&:gem_version).
35
+ sort_by { |v| [v.prerelease? ? 0 : 1, v] }.
36
+ reverse.find { |v| req.satisfied_by?(v) }
37
+
38
+ if choice.nil?
39
+ raise "Failed to resolve #{name.inspect} (#{req.inspect}) given #{@solution.inspect}"
40
+ end
41
+
42
+ @solution[name] = choice
43
+
44
+ @catalog_set.dependencies_for(@packages[name], choice).each do |dep_name, constraints|
45
+ if @solution[dep_name]
46
+ new_req = Gel::Support::GemRequirement.new(constraints)
47
+ unless new_req.satisfied_by?(@solution[dep_name])
48
+ raise "Already chose #{dep_name.inspect} #{@solution[dep_name]}, which is incompatible with #{name.inspect} #{choice.inspect} (wants #{new_req})"
49
+ end
50
+ else
51
+ @constraints[name].concat(constraints || [])
52
+ end
53
+ end
54
+ end
55
+
56
+ def each_resolved_package
57
+ @solution.each do |name, version|
58
+ yield @packages[name], version
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def gems_to_solve
65
+ @constraints.keys - @solution.keys
66
+ end
67
+ end