bundler 2.6.3 → 2.6.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,51 +2,92 @@
2
2
 
3
3
  require "rubygems/dependency"
4
4
  require_relative "shared_helpers"
5
- require_relative "rubygems_ext"
6
5
 
7
6
  module Bundler
8
7
  class Dependency < Gem::Dependency
9
- attr_reader :autorequire
10
- attr_reader :groups, :platforms, :gemfile, :path, :git, :github, :branch, :ref, :glob
11
-
12
- ALL_RUBY_VERSIONS = (18..27).to_a.concat((30..35).to_a).freeze
13
- PLATFORM_MAP = {
14
- ruby: [Gem::Platform::RUBY, ALL_RUBY_VERSIONS],
15
- mri: [Gem::Platform::RUBY, ALL_RUBY_VERSIONS],
16
- rbx: [Gem::Platform::RUBY],
17
- truffleruby: [Gem::Platform::RUBY],
18
- jruby: [Gem::Platform::JAVA, [18, 19]],
19
- windows: [Gem::Platform::WINDOWS, ALL_RUBY_VERSIONS],
20
- # deprecated
21
- mswin: [Gem::Platform::MSWIN, ALL_RUBY_VERSIONS],
22
- mswin64: [Gem::Platform::MSWIN64, ALL_RUBY_VERSIONS - [18]],
23
- mingw: [Gem::Platform::MINGW, ALL_RUBY_VERSIONS],
24
- x64_mingw: [Gem::Platform::X64_MINGW, ALL_RUBY_VERSIONS - [18, 19]],
25
- }.each_with_object({}) do |(platform, spec), hash|
26
- hash[platform] = spec[0]
27
- spec[1]&.each {|version| hash[:"#{platform}_#{version}"] = spec[0] }
28
- end.freeze
29
-
30
8
  def initialize(name, version, options = {}, &blk)
31
9
  type = options["type"] || :runtime
32
10
  super(name, version, type)
33
11
 
34
- @autorequire = nil
35
- @groups = Array(options["group"] || :default).map(&:to_sym)
36
- @source = options["source"]
37
- @path = options["path"]
38
- @git = options["git"]
39
- @github = options["github"]
40
- @branch = options["branch"]
41
- @ref = options["ref"]
42
- @glob = options["glob"]
43
- @platforms = Array(options["platforms"])
44
- @env = options["env"]
45
- @should_include = options.fetch("should_include", true)
46
- @gemfile = options["gemfile"]
47
- @force_ruby_platform = options["force_ruby_platform"] if options.key?("force_ruby_platform")
12
+ @options = options
13
+ end
14
+
15
+ def groups
16
+ @groups ||= Array(@options["group"] || :default).map(&:to_sym)
17
+ end
18
+
19
+ def source
20
+ return @source if defined?(@source)
21
+
22
+ @source = @options["source"]
23
+ end
24
+
25
+ def path
26
+ return @path if defined?(@path)
27
+
28
+ @path = @options["path"]
29
+ end
30
+
31
+ def git
32
+ return @git if defined?(@git)
48
33
 
49
- @autorequire = Array(options["require"] || []) if options.key?("require")
34
+ @git = @options["git"]
35
+ end
36
+
37
+ def github
38
+ return @github if defined?(@github)
39
+
40
+ @github = @options["github"]
41
+ end
42
+
43
+ def branch
44
+ return @branch if defined?(@branch)
45
+
46
+ @branch = @options["branch"]
47
+ end
48
+
49
+ def ref
50
+ return @ref if defined?(@ref)
51
+
52
+ @ref = @options["ref"]
53
+ end
54
+
55
+ def glob
56
+ return @glob if defined?(@glob)
57
+
58
+ @glob = @options["glob"]
59
+ end
60
+
61
+ def platforms
62
+ @platforms ||= Array(@options["platforms"])
63
+ end
64
+
65
+ def env
66
+ return @env if defined?(@env)
67
+
68
+ @env = @options["env"]
69
+ end
70
+
71
+ def should_include
72
+ @should_include ||= @options.fetch("should_include", true)
73
+ end
74
+
75
+ def gemfile
76
+ return @gemfile if defined?(@gemfile)
77
+
78
+ @gemfile = @options["gemfile"]
79
+ end
80
+
81
+ def force_ruby_platform
82
+ return @force_ruby_platform if defined?(@force_ruby_platform)
83
+
84
+ @force_ruby_platform = @options["force_ruby_platform"]
85
+ end
86
+
87
+ def autorequire
88
+ return @autorequire if defined?(@autorequire)
89
+
90
+ @autorequire = Array(@options["require"] || []) if @options.key?("require")
50
91
  end
51
92
 
52
93
  RUBY_PLATFORM_ARRAY = [Gem::Platform::RUBY].freeze
@@ -56,37 +97,41 @@ module Bundler
56
97
  # passed in the `valid_platforms` parameter
57
98
  def gem_platforms(valid_platforms)
58
99
  return RUBY_PLATFORM_ARRAY if force_ruby_platform
59
- return valid_platforms if @platforms.empty?
100
+ return valid_platforms if platforms.empty?
60
101
 
61
102
  valid_platforms.select {|p| expanded_platforms.include?(GemHelpers.generic(p)) }
62
103
  end
63
104
 
64
105
  def expanded_platforms
65
- @expanded_platforms ||= @platforms.filter_map {|pl| PLATFORM_MAP[pl] }.flatten.uniq
106
+ @expanded_platforms ||= platforms.filter_map {|pl| CurrentRuby::PLATFORM_MAP[pl] }.flatten.uniq
66
107
  end
67
108
 
68
109
  def should_include?
69
- @should_include && current_env? && current_platform?
110
+ should_include && current_env? && current_platform?
70
111
  end
71
112
 
72
113
  def gemspec_dev_dep?
73
- type == :development
114
+ @gemspec_dev_dep ||= @options.fetch("gemspec_dev_dep", false)
115
+ end
116
+
117
+ def gemfile_dep?
118
+ !gemspec_dev_dep?
74
119
  end
75
120
 
76
121
  def current_env?
77
- return true unless @env
78
- if @env.is_a?(Hash)
79
- @env.all? do |key, val|
122
+ return true unless env
123
+ if env.is_a?(Hash)
124
+ env.all? do |key, val|
80
125
  ENV[key.to_s] && (val.is_a?(String) ? ENV[key.to_s] == val : ENV[key.to_s] =~ val)
81
126
  end
82
127
  else
83
- ENV[@env.to_s]
128
+ ENV[env.to_s]
84
129
  end
85
130
  end
86
131
 
87
132
  def current_platform?
88
- return true if @platforms.empty?
89
- @platforms.any? do |p|
133
+ return true if platforms.empty?
134
+ platforms.any? do |p|
90
135
  Bundler.current_ruby.send("#{p}?")
91
136
  end
92
137
  end
data/lib/bundler/dsl.rb CHANGED
@@ -13,10 +13,10 @@ module Bundler
13
13
  builder.to_definition(lockfile, unlock)
14
14
  end
15
15
 
16
- VALID_PLATFORMS = Bundler::Dependency::PLATFORM_MAP.keys.freeze
16
+ VALID_PLATFORMS = Bundler::CurrentRuby::PLATFORM_MAP.keys.freeze
17
17
 
18
18
  VALID_KEYS = %w[group groups git path glob name branch ref tag require submodules
19
- platform platforms type source install_if gemfile force_ruby_platform].freeze
19
+ platform platforms source install_if force_ruby_platform].freeze
20
20
 
21
21
  GITHUB_PULL_REQUEST_URL = %r{\Ahttps://github\.com/([A-Za-z0-9_\-\.]+/[A-Za-z0-9_\-\.]+)/pull/(\d+)\z}
22
22
  GITLAB_MERGE_REQUEST_URL = %r{\Ahttps://gitlab\.com/([A-Za-z0-9_\-\./]+)/-/merge_requests/(\d+)\z}
@@ -77,12 +77,12 @@ module Bundler
77
77
 
78
78
  @gemspecs << spec
79
79
 
80
- gem spec.name, name: spec.name, path: path, glob: glob
80
+ path path, "glob" => glob, "name" => spec.name do
81
+ add_dependency spec.name
82
+ end
81
83
 
82
- group(development_group) do
83
- spec.development_dependencies.each do |dep|
84
- gem dep.name, *(dep.requirement.as_list + [type: :development])
85
- end
84
+ spec.development_dependencies.each do |dep|
85
+ add_dependency dep.name, dep.requirement.as_list, "gemspec_dev_dep" => true, "group" => development_group
86
86
  end
87
87
  when 0
88
88
  raise InvalidOption, "There are no gemspecs at #{expanded_path}"
@@ -94,79 +94,11 @@ module Bundler
94
94
 
95
95
  def gem(name, *args)
96
96
  options = args.last.is_a?(Hash) ? args.pop.dup : {}
97
- options["gemfile"] = @gemfile
98
97
  version = args || [">= 0"]
99
98
 
100
99
  normalize_options(name, version, options)
101
100
 
102
- dep = Dependency.new(name, version, options)
103
-
104
- # if there's already a dependency with this name we try to prefer one
105
- if current = @dependencies.find {|d| d.name == dep.name }
106
- if current.requirement != dep.requirement
107
- current_requirement_open = current.requirements_list.include?(">= 0")
108
-
109
- gemspec_dep = [dep, current].find(&:gemspec_dev_dep?)
110
- if gemspec_dep
111
- gemfile_dep = [dep, current].find(&:runtime?)
112
-
113
- if gemfile_dep && !current_requirement_open
114
- Bundler.ui.warn "A gemspec development dependency (#{gemspec_dep.name}, #{gemspec_dep.requirement}) is being overridden by a Gemfile dependency (#{gemfile_dep.name}, #{gemfile_dep.requirement}).\n" \
115
- "This behaviour may change in the future. Please remove either of them, or make sure they both have the same requirement\n"
116
- elsif gemfile_dep.nil?
117
- require_relative "vendor/pub_grub/lib/pub_grub/version_range"
118
- require_relative "vendor/pub_grub/lib/pub_grub/version_constraint"
119
- require_relative "vendor/pub_grub/lib/pub_grub/version_union"
120
- require_relative "vendor/pub_grub/lib/pub_grub/rubygems"
121
-
122
- current_gemspec_range = PubGrub::RubyGems.requirement_to_range(current.requirement)
123
- next_gemspec_range = PubGrub::RubyGems.requirement_to_range(dep.requirement)
124
-
125
- if current_gemspec_range.intersects?(next_gemspec_range)
126
- dep = Dependency.new(name, current.requirement.as_list + dep.requirement.as_list, options)
127
- else
128
- raise GemfileError, "Two gemspecs have conflicting requirements on the same gem: #{dep} and #{current}"
129
- end
130
- end
131
- else
132
- update_prompt = ""
133
-
134
- if File.basename(@gemfile) == Injector::INJECTED_GEMS
135
- if dep.requirements_list.include?(">= 0") && !current_requirement_open
136
- update_prompt = ". Gem already added"
137
- else
138
- update_prompt = ". If you want to update the gem version, run `bundle update #{current.name}`"
139
-
140
- update_prompt += ". You may also need to change the version requirement specified in the Gemfile if it's too restrictive." unless current_requirement_open
141
- end
142
- end
143
-
144
- raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \
145
- "You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \
146
- "#{update_prompt}"
147
- end
148
- end
149
-
150
- unless current.gemspec_dev_dep? && dep.gemspec_dev_dep?
151
- # Always prefer the dependency from the Gemfile
152
- if current.gemspec_dev_dep?
153
- @dependencies.delete(current)
154
- elsif dep.gemspec_dev_dep?
155
- return
156
- elsif current.source != dep.source
157
- raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \
158
- "You specified that #{dep.name} (#{dep.requirement}) should come from " \
159
- "#{current.source || "an unspecified source"} and #{dep.source}\n"
160
- else
161
- Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \
162
- "You should probably keep only one of them.\n" \
163
- "Remove any duplicate entries and specify the gem only once.\n" \
164
- "While it's not a problem now, it could cause errors if you change the version of one of them later."
165
- end
166
- end
167
- end
168
-
169
- @dependencies << dep
101
+ add_dependency(name, version, options)
170
102
  end
171
103
 
172
104
  def source(source, *args, &blk)
@@ -301,6 +233,81 @@ module Bundler
301
233
 
302
234
  private
303
235
 
236
+ def add_dependency(name, version = nil, options = {})
237
+ options["gemfile"] = @gemfile
238
+ options["source"] ||= @source
239
+ options["env"] ||= @env
240
+
241
+ dep = Dependency.new(name, version, options)
242
+
243
+ # if there's already a dependency with this name we try to prefer one
244
+ if current = @dependencies.find {|d| d.name == dep.name }
245
+ if current.requirement != dep.requirement
246
+ current_requirement_open = current.requirements_list.include?(">= 0")
247
+
248
+ gemspec_dep = [dep, current].find(&:gemspec_dev_dep?)
249
+ if gemspec_dep
250
+ gemfile_dep = [dep, current].find(&:gemfile_dep?)
251
+
252
+ if gemfile_dep && !current_requirement_open
253
+ Bundler.ui.warn "A gemspec development dependency (#{gemspec_dep.name}, #{gemspec_dep.requirement}) is being overridden by a Gemfile dependency (#{gemfile_dep.name}, #{gemfile_dep.requirement}).\n" \
254
+ "This behaviour may change in the future. Please remove either of them, or make sure they both have the same requirement\n"
255
+ elsif gemfile_dep.nil?
256
+ require_relative "vendor/pub_grub/lib/pub_grub/version_range"
257
+ require_relative "vendor/pub_grub/lib/pub_grub/version_constraint"
258
+ require_relative "vendor/pub_grub/lib/pub_grub/version_union"
259
+ require_relative "vendor/pub_grub/lib/pub_grub/rubygems"
260
+
261
+ current_gemspec_range = PubGrub::RubyGems.requirement_to_range(current.requirement)
262
+ next_gemspec_range = PubGrub::RubyGems.requirement_to_range(dep.requirement)
263
+
264
+ if current_gemspec_range.intersects?(next_gemspec_range)
265
+ dep = Dependency.new(name, current.requirement.as_list + dep.requirement.as_list, options)
266
+ else
267
+ raise GemfileError, "Two gemspec development dependencies have conflicting requirements on the same gem: #{dep} and #{current}"
268
+ end
269
+ end
270
+ else
271
+ update_prompt = ""
272
+
273
+ if File.basename(@gemfile) == Injector::INJECTED_GEMS
274
+ if dep.requirements_list.include?(">= 0") && !current_requirement_open
275
+ update_prompt = ". Gem already added"
276
+ else
277
+ update_prompt = ". If you want to update the gem version, run `bundle update #{current.name}`"
278
+
279
+ update_prompt += ". You may also need to change the version requirement specified in the Gemfile if it's too restrictive." unless current_requirement_open
280
+ end
281
+ end
282
+
283
+ raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \
284
+ "You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \
285
+ "#{update_prompt}"
286
+ end
287
+ end
288
+
289
+ unless current.gemspec_dev_dep? && dep.gemspec_dev_dep?
290
+ # Always prefer the dependency from the Gemfile
291
+ if current.gemspec_dev_dep?
292
+ @dependencies.delete(current)
293
+ elsif dep.gemspec_dev_dep?
294
+ return
295
+ elsif current.source != dep.source
296
+ raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \
297
+ "You specified that #{dep.name} (#{dep.requirement}) should come from " \
298
+ "#{current.source || "an unspecified source"} and #{dep.source}\n"
299
+ else
300
+ Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \
301
+ "You should probably keep only one of them.\n" \
302
+ "Remove any duplicate entries and specify the gem only once.\n" \
303
+ "While it's not a problem now, it could cause errors if you change the version of one of them later."
304
+ end
305
+ end
306
+ end
307
+
308
+ @dependencies << dep
309
+ end
310
+
304
311
  def with_gemfile(gemfile)
305
312
  expanded_gemfile_path = Pathname.new(gemfile).expand_path(@gemfile&.parent)
306
313
  original_gemfile = @gemfile
@@ -433,8 +440,6 @@ module Bundler
433
440
  opts["source"] = source
434
441
  end
435
442
 
436
- opts["source"] ||= @source
437
- opts["env"] ||= @env
438
443
  opts["platforms"] = platforms.dup
439
444
  opts["group"] = groups
440
445
  opts["should_include"] = install_if
@@ -6,7 +6,8 @@ module Bundler
6
6
  include MatchRemoteMetadata
7
7
 
8
8
  attr_reader :name, :version, :platform, :checksum
9
- attr_accessor :remote, :dependencies, :locked_platform
9
+ attr_writer :dependencies
10
+ attr_accessor :remote, :locked_platform
10
11
 
11
12
  def initialize(name, version, platform, spec_fetcher, dependencies, metadata = nil)
12
13
  super()
@@ -14,7 +15,8 @@ module Bundler
14
15
  @version = Gem::Version.create version
15
16
  @platform = Gem::Platform.new(platform)
16
17
  @spec_fetcher = spec_fetcher
17
- @dependencies = dependencies.map {|dep, reqs| build_dependency(dep, reqs) }
18
+ @dependencies = nil
19
+ @unbuilt_dependencies = dependencies
18
20
 
19
21
  @loaded_from = nil
20
22
  @remote_specification = nil
@@ -31,6 +33,11 @@ module Bundler
31
33
  @platform
32
34
  end
33
35
 
36
+ def dependencies
37
+ @dependencies ||= @unbuilt_dependencies.map! {|dep, reqs| build_dependency(dep, reqs) }
38
+ end
39
+ alias_method :runtime_dependencies, :dependencies
40
+
34
41
  # needed for standalone, load required_paths from local gemspec
35
42
  # after the gem is installed
36
43
  def require_paths
@@ -161,7 +168,7 @@ module Bundler
161
168
  end
162
169
 
163
170
  def build_dependency(name, requirements)
164
- Gem::Dependency.new(name, requirements)
171
+ Dependency.new(name, requirements)
165
172
  end
166
173
  end
167
174
  end
@@ -254,6 +254,10 @@ module Bundler
254
254
  @spec = spec
255
255
  end
256
256
 
257
+ def message
258
+ "Bundler found incorrect dependencies in the lockfile for #{spec.full_name}"
259
+ end
260
+
257
261
  status_code(41)
258
262
  end
259
263
  end
@@ -4,20 +4,14 @@ module Bundler
4
4
  module GemHelpers
5
5
  GENERIC_CACHE = { Gem::Platform::RUBY => Gem::Platform::RUBY } # rubocop:disable Style/MutableConstant
6
6
  GENERICS = [
7
- [Gem::Platform.new("java"), Gem::Platform.new("java")],
8
- [Gem::Platform.new("mswin32"), Gem::Platform.new("mswin32")],
9
- [Gem::Platform.new("mswin64"), Gem::Platform.new("mswin64")],
10
- [Gem::Platform.new("universal-mingw32"), Gem::Platform.new("universal-mingw32")],
11
- [Gem::Platform.new("x64-mingw32"), Gem::Platform.new("x64-mingw32")],
12
- [Gem::Platform.new("x86_64-mingw32"), Gem::Platform.new("x64-mingw32")],
13
- [Gem::Platform.new("x64-mingw-ucrt"), Gem::Platform.new("x64-mingw-ucrt")],
14
- [Gem::Platform.new("mingw32"), Gem::Platform.new("x86-mingw32")],
7
+ Gem::Platform::JAVA,
8
+ *Gem::Platform::WINDOWS,
15
9
  ].freeze
16
10
 
17
11
  def generic(p)
18
12
  GENERIC_CACHE[p] ||= begin
19
- _, found = GENERICS.find do |match, _generic|
20
- p.os == match.os && (!match.cpu || p.cpu == match.cpu)
13
+ found = GENERICS.find do |match|
14
+ p === match
21
15
  end
22
16
  found || Gem::Platform::RUBY
23
17
  end
@@ -132,8 +132,6 @@ module Bundler
132
132
  # Specific version moves can't always reliably be done during sorting
133
133
  # as not all elements are compared against each other.
134
134
  def post_sort(result, unlock, locked_version)
135
- # default :major behavior in Bundler does not do this
136
- return result if major?
137
135
  if unlock || locked_version.nil?
138
136
  result
139
137
  else
@@ -193,7 +193,7 @@ module Bundler
193
193
  def install(options)
194
194
  standalone = options[:standalone]
195
195
  force = options[:force]
196
- local = options[:local]
196
+ local = options[:local] || options[:"prefer-local"]
197
197
  jobs = installation_parallelization
198
198
  spec_installations = ParallelInstaller.call(self, @definition.specs, jobs, standalone, force, local: local)
199
199
  spec_installations.each do |installation|
@@ -121,13 +121,10 @@ module Bundler
121
121
  out
122
122
  end
123
123
 
124
- def materialize_strictly
125
- source.local!
126
-
127
- matching_specs = source.specs.search(self)
128
- return self if matching_specs.empty?
124
+ def materialize_for_cache
125
+ source.remote!
129
126
 
130
- __materialize__(matching_specs)
127
+ materialize(self, &:first)
131
128
  end
132
129
 
133
130
  def materialized_for_installation
@@ -140,53 +137,25 @@ module Bundler
140
137
  source.local!
141
138
 
142
139
  if use_exact_resolved_specifications?
143
- materialize_strictly
140
+ materialize(self) do |matching_specs|
141
+ choose_compatible(matching_specs)
142
+ end
144
143
  else
145
- matching_specs = source.specs.search([name, version])
146
- return self if matching_specs.empty?
144
+ materialize([name, version]) do |matching_specs|
145
+ target_platform = source.is_a?(Source::Path) ? platform : local_platform
147
146
 
148
- target_platform = source.is_a?(Source::Path) ? platform : local_platform
149
-
150
- installable_candidates = GemHelpers.select_best_platform_match(matching_specs, target_platform)
151
-
152
- specification = __materialize__(installable_candidates, fallback_to_non_installable: false)
153
- return specification unless specification.nil?
154
-
155
- if target_platform != platform
156
- installable_candidates = GemHelpers.select_best_platform_match(matching_specs, platform)
157
- end
147
+ installable_candidates = GemHelpers.select_best_platform_match(matching_specs, target_platform)
158
148
 
159
- __materialize__(installable_candidates)
160
- end
161
- end
149
+ specification = choose_compatible(installable_candidates, fallback_to_non_installable: false)
150
+ return specification unless specification.nil?
162
151
 
163
- # If in frozen mode, we fallback to a non-installable candidate because by
164
- # doing this we avoid re-resolving and potentially end up changing the
165
- # lock file, which is not allowed. In that case, we will give a proper error
166
- # about the mismatch higher up the stack, right before trying to install the
167
- # bad gem.
168
- def __materialize__(candidates, fallback_to_non_installable: Bundler.frozen_bundle?)
169
- search = candidates.reverse.find do |spec|
170
- spec.is_a?(StubSpecification) || spec.matches_current_metadata?
171
- end
172
- if search.nil? && fallback_to_non_installable
173
- search = candidates.last
174
- elsif search && search.full_name == full_name
175
- # We don't validate locally installed dependencies but accept what's in
176
- # the lockfile instead for performance, since loading locally installed
177
- # dependencies would mean evaluating all gemspecs, which would affect
178
- # `bundler/setup` performance
179
- if search.is_a?(StubSpecification)
180
- search.dependencies = dependencies
181
- else
182
- if !source.is_a?(Source::Path) && search.runtime_dependencies.sort != dependencies.sort
183
- raise IncorrectLockfileDependencies.new(self)
152
+ if target_platform != platform
153
+ installable_candidates = GemHelpers.select_best_platform_match(matching_specs, platform)
184
154
  end
185
155
 
186
- search.locked_platform = platform if search.instance_of?(RemoteSpecification) || search.instance_of?(EndpointSpecification)
156
+ choose_compatible(installable_candidates)
187
157
  end
188
158
  end
189
- search
190
159
  end
191
160
 
192
161
  def inspect
@@ -217,5 +186,41 @@ module Bundler
217
186
 
218
187
  (most_specific_locked_platform != generic_platform) || force_ruby_platform || Bundler.settings[:force_ruby_platform]
219
188
  end
189
+
190
+ def materialize(query)
191
+ matching_specs = source.specs.search(query)
192
+ return self if matching_specs.empty?
193
+
194
+ yield matching_specs
195
+ end
196
+
197
+ # If in frozen mode, we fallback to a non-installable candidate because by
198
+ # doing this we avoid re-resolving and potentially end up changing the
199
+ # lock file, which is not allowed. In that case, we will give a proper error
200
+ # about the mismatch higher up the stack, right before trying to install the
201
+ # bad gem.
202
+ def choose_compatible(candidates, fallback_to_non_installable: Bundler.frozen_bundle?)
203
+ search = candidates.reverse.find do |spec|
204
+ spec.is_a?(StubSpecification) || spec.matches_current_metadata?
205
+ end
206
+ if search.nil? && fallback_to_non_installable
207
+ search = candidates.last
208
+ elsif search && search.full_name == full_name
209
+ # We don't validate locally installed dependencies but accept what's in
210
+ # the lockfile instead for performance, since loading locally installed
211
+ # dependencies would mean evaluating all gemspecs, which would affect
212
+ # `bundler/setup` performance
213
+ if search.is_a?(StubSpecification)
214
+ search.dependencies = dependencies
215
+ else
216
+ if !source.is_a?(Source::Path) && search.runtime_dependencies.sort != dependencies.sort
217
+ raise IncorrectLockfileDependencies.new(self)
218
+ end
219
+
220
+ search.locked_platform = platform if search.instance_of?(RemoteSpecification) || search.instance_of?(EndpointSpecification)
221
+ end
222
+ end
223
+ search
224
+ end
220
225
  end
221
226
  end
@@ -13,5 +13,18 @@ module Bundler
13
13
  def matches_current_rubygems?
14
14
  @required_rubygems_version.satisfied_by?(Gem.rubygems_version)
15
15
  end
16
+
17
+ def expanded_dependencies
18
+ runtime_dependencies + [
19
+ metadata_dependency("Ruby", @required_ruby_version),
20
+ metadata_dependency("RubyGems", @required_rubygems_version),
21
+ ].compact
22
+ end
23
+
24
+ def metadata_dependency(name, requirement)
25
+ return if requirement.nil? || requirement.none?
26
+
27
+ Gem::Dependency.new("#{name}\0", requirement)
28
+ end
16
29
  end
17
30
  end
@@ -15,7 +15,7 @@ module Bundler
15
15
  class Package
16
16
  attr_reader :name, :platforms, :dependency, :locked_version
17
17
 
18
- def initialize(name, platforms, locked_specs:, unlock:, prerelease: false, prefer_local: false, dependency: nil)
18
+ def initialize(name, platforms, locked_specs:, unlock:, prerelease: false, prefer_local: false, dependency: nil, new_platforms: [])
19
19
  @name = name
20
20
  @platforms = platforms
21
21
  @locked_version = locked_specs.version_for(name)
@@ -24,10 +24,14 @@ module Bundler
24
24
  @top_level = !dependency.nil?
25
25
  @prerelease = @dependency.prerelease? || @locked_version&.prerelease? || prerelease ? :consider_first : :ignore
26
26
  @prefer_local = prefer_local
27
+ @new_platforms = new_platforms
27
28
  end
28
29
 
29
30
  def platform_specs(specs)
30
- platforms.map {|platform| GemHelpers.select_best_platform_match(specs, platform, prefer_locked: !unlock?) }
31
+ platforms.map do |platform|
32
+ prefer_locked = @new_platforms.include?(platform) ? false : !unlock?
33
+ GemHelpers.select_best_platform_match(specs, platform, prefer_locked: prefer_locked)
34
+ end
31
35
  end
32
36
 
33
37
  def to_s
@@ -55,7 +59,7 @@ module Bundler
55
59
  end
56
60
 
57
61
  def unlock?
58
- @unlock.empty? || @unlock.include?(name)
62
+ @unlock == true || @unlock.include?(name)
59
63
  end
60
64
 
61
65
  def ignores_prereleases?