Skip to content

Commit b5798ef

Browse files
Merge pull request #7999 from rubygems/deivid-rodriguez/dsl-improvements
Handle two `gemspec` usages in same Gemfile with same dep and compatible requirements
2 parents 958ee6f + a8d14c1 commit b5798ef

File tree

2 files changed

+88
-15
lines changed

2 files changed

+88
-15
lines changed

bundler/lib/bundler/dsl.rb

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,23 @@ def gem(name, *args)
110110
if gemspec_dep
111111
gemfile_dep = [dep, current].find(&:runtime?)
112112

113-
unless current_requirement_open
113+
if gemfile_dep && !current_requirement_open
114114
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" \
115115
"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
116130
end
117131
else
118132
update_prompt = ""
@@ -133,20 +147,22 @@ def gem(name, *args)
133147
end
134148
end
135149

136-
# Always prefer the dependency from the Gemfile
137-
if current.gemspec_dev_dep?
138-
@dependencies.delete(current)
139-
elsif dep.gemspec_dev_dep?
140-
return
141-
elsif current.source != dep.source
142-
raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \
143-
"You specified that #{dep.name} (#{dep.requirement}) should come from " \
144-
"#{current.source || "an unspecified source"} and #{dep.source}\n"
145-
else
146-
Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \
147-
"You should probably keep only one of them.\n" \
148-
"Remove any duplicate entries and specify the gem only once.\n" \
149-
"While it's not a problem now, it could cause errors if you change the version of one of them later."
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
150166
end
151167
end
152168

bundler/spec/commands/install_spec.rb

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,63 @@
485485
expect(the_bundle).to include_gems("rubocop 1.36.0")
486486
end
487487

488+
it "includes the gem without warning if two gemspecs add it with compatible requirements" do
489+
gem1 = tmp("my-gem-1")
490+
gem2 = tmp("my-gem-2")
491+
492+
build_lib "my-gem", path: gem1 do |s|
493+
s.add_development_dependency "rubocop", "~> 1.0"
494+
end
495+
496+
build_lib "my-gem-2", path: gem2 do |s|
497+
s.add_development_dependency "rubocop", "~> 1.36.0"
498+
end
499+
500+
build_repo4 do
501+
build_gem "rubocop", "1.36.0"
502+
end
503+
504+
gemfile <<~G
505+
source "https://gem.repo4"
506+
507+
gemspec path: "#{gem1}"
508+
gemspec path: "#{gem2}"
509+
G
510+
511+
bundle :install
512+
513+
expect(err).to be_empty
514+
expect(the_bundle).to include_gems("rubocop 1.36.0")
515+
end
516+
517+
it "errors out if two gemspecs add it with incompatible requirements" do
518+
gem1 = tmp("my-gem-1")
519+
gem2 = tmp("my-gem-2")
520+
521+
build_lib "my-gem", path: gem1 do |s|
522+
s.add_development_dependency "rubocop", "~> 2.0"
523+
end
524+
525+
build_lib "my-gem-2", path: gem2 do |s|
526+
s.add_development_dependency "rubocop", "~> 1.36.0"
527+
end
528+
529+
build_repo4 do
530+
build_gem "rubocop", "1.36.0"
531+
end
532+
533+
gemfile <<~G
534+
source "https://gem.repo4"
535+
536+
gemspec path: "#{gem1}"
537+
gemspec path: "#{gem2}"
538+
G
539+
540+
bundle :install, raise_on_error: false
541+
542+
expect(err).to include("Two gemspecs have conflicting requirements on the same gem: rubocop (~> 1.36.0, development) and rubocop (~> 2.0, development). Bundler cannot continue.")
543+
end
544+
488545
it "warns when a Gemfile dependency is overriding a gemspec development dependency, with different requirements" do
489546
build_lib "my-gem", path: bundled_app do |s|
490547
s.add_development_dependency "rails", ">= 5"

0 commit comments

Comments
 (0)