Skip to content

Commit 445d821

Browse files
Merge pull request #8459 from rubygems/deivid-rodriguez/avoid-race-condition
Fix `bundle install` output sometimes getting interleaved (cherry picked from commit 0027440)
1 parent f852f3d commit 445d821

File tree

2 files changed

+47
-28
lines changed

2 files changed

+47
-28
lines changed

bundler/lib/bundler/rubygems_integration.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ def reverse_rubygems_kernel_mixin
177177
end
178178
end
179179

180-
def replace_gem(specs, specs_by_name)
180+
def replace_gem(specs_by_name)
181181
executables = nil
182182

183183
[::Kernel.singleton_class, ::Kernel].each do |kernel_class|
@@ -274,7 +274,7 @@ def replace_entrypoints(specs)
274274
else
275275
Gem::BUNDLED_GEMS.replace_require(specs) if Gem::BUNDLED_GEMS.respond_to?(:replace_require)
276276
end
277-
replace_gem(specs, specs_by_name)
277+
replace_gem(specs_by_name)
278278
stub_rubygems(specs_by_name.values)
279279
replace_bin_path(specs_by_name)
280280

@@ -293,7 +293,6 @@ def add_default_gems_to(specs)
293293
default_spec_name = default_spec.name
294294
next if specs_by_name.key?(default_spec_name)
295295

296-
specs << default_spec
297296
specs_by_name[default_spec_name] = default_spec
298297
end
299298

bundler/lib/bundler/spec_set.rb

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,13 @@ def [](key)
8383
end
8484

8585
def []=(key, value)
86-
@specs << value
86+
delete_by_name(key)
8787

88-
reset!
88+
add_spec(value)
8989
end
9090

9191
def delete(specs)
92-
Array(specs).each {|spec| @specs.delete(spec) }
93-
94-
reset!
92+
Array(specs).each {|spec| remove_spec(spec) }
9593
end
9694

9795
def sort!
@@ -168,8 +166,10 @@ def specs_with_additional_variants_from(other)
168166

169167
def delete_by_name(name)
170168
@specs.reject! {|spec| spec.name == name }
169+
@sorted&.reject! {|spec| spec.name == name }
170+
return if @lookup.nil?
171171

172-
reset!
172+
@lookup[name] = nil
173173
end
174174

175175
def version_for(name)
@@ -248,11 +248,6 @@ def materialized_specs
248248
@materializations.filter_map(&:materialized_spec)
249249
end
250250

251-
def reset!
252-
@sorted = nil
253-
@lookup = nil
254-
end
255-
256251
def complete_platform(platform)
257252
new_specs = []
258253

@@ -272,9 +267,7 @@ def complete_platform(platform)
272267
end
273268

274269
if valid_platform && new_specs.any?
275-
@specs.concat(new_specs)
276-
277-
reset!
270+
new_specs.each {|spec| add_spec(spec) }
278271
end
279272

280273
valid_platform
@@ -295,15 +288,12 @@ def valid_dependencies?(s)
295288
end
296289

297290
def sorted
298-
rake = @specs.find {|s| s.name == "rake" }
299-
begin
300-
@sorted ||= ([rake] + tsort).compact.uniq
301-
rescue TSort::Cyclic => error
302-
cgems = extract_circular_gems(error)
303-
raise CyclicDependencyError, "Your bundle requires gems that depend" \
304-
" on each other, creating an infinite loop. Please remove either" \
305-
" gem '#{cgems[0]}' or gem '#{cgems[1]}' and try again."
306-
end
291+
@sorted ||= ([@specs.find {|s| s.name == "rake" }] + tsort).compact.uniq
292+
rescue TSort::Cyclic => error
293+
cgems = extract_circular_gems(error)
294+
raise CyclicDependencyError, "Your bundle requires gems that depend" \
295+
" on each other, creating an infinite loop. Please remove either" \
296+
" gem '#{cgems[0]}' or gem '#{cgems[1]}' and try again."
307297
end
308298

309299
def extract_circular_gems(error)
@@ -314,8 +304,7 @@ def lookup
314304
@lookup ||= begin
315305
lookup = {}
316306
@specs.each do |s|
317-
lookup[s.name] ||= []
318-
lookup[s.name] << s
307+
index_spec(lookup, s.name, s)
319308
end
320309
lookup
321310
end
@@ -336,5 +325,36 @@ def tsort_each_child(s)
336325
specs_for_name.each {|s2| yield s2 }
337326
end
338327
end
328+
329+
def add_spec(spec)
330+
@specs << spec
331+
332+
name = spec.name
333+
334+
@sorted&.insert(@sorted.bsearch_index {|s| s.name >= name } || @sorted.size, spec)
335+
return if @lookup.nil?
336+
337+
index_spec(@lookup, name, spec)
338+
end
339+
340+
def remove_spec(spec)
341+
@specs.delete(spec)
342+
@sorted&.delete(spec)
343+
return if @lookup.nil?
344+
345+
indexed_specs = @lookup[spec.name]
346+
return unless indexed_specs
347+
348+
if indexed_specs.size > 1
349+
@lookup[spec.name].delete(spec)
350+
else
351+
@lookup[spec.name] = nil
352+
end
353+
end
354+
355+
def index_spec(hash, key, value)
356+
hash[key] ||= []
357+
hash[key] << value
358+
end
339359
end
340360
end

0 commit comments

Comments
 (0)