diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb index b1a997f3f25..97c124e0150 100644 --- a/lib/bundler/installer/parallel_installer.rb +++ b/lib/bundler/installer/parallel_installer.rb @@ -90,6 +90,7 @@ def initialize(installer, all_specs, size, standalone, force) @standalone = standalone @force = force @specs = all_specs.map {|s| SpecInstallation.new(s) } + @spec_set = all_specs end def call @@ -116,7 +117,7 @@ def worker_pool spec_install.post_install_message = message elsif !success spec_install.state = :failed - spec_install.error = message + spec_install.error = "#{message}\n\n#{require_tree_for_spec(spec_install.spec)}" end spec_install } @@ -166,6 +167,19 @@ def check_for_corrupt_lockfile Bundler.ui.warn(warning.join("\n")) end + def require_tree_for_spec(spec) + tree = @spec_set.what_required(spec) + t = String.new("In #{File.basename(SharedHelpers.default_gemfile)}:\n") + tree.each_with_index do |s, depth| + t << " " * depth.succ << s.name + unless tree.last == s + t << %( was resolved to #{s.version}, which depends on) + end + t << %(\n) + end + t + end + # Keys in the remains hash represent uninstalled gems specs. # We enqueue all gem specs that do not have any dependencies. # Later we call this lambda again to install specs that depended on diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb index 19b9cddc3eb..f3826beaa59 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -118,6 +118,13 @@ def find_by_name_and_platform(name, platform) @specs.detect {|spec| spec.name == name && spec.match_platform(platform) } end + def what_required(spec) + unless req = find {|s| s.dependencies.any? {|d| d.type == :runtime && d.name == spec.name } } + return [spec] + end + what_required(req) << spec + end + private def sorted diff --git a/spec/install/failure_spec.rb b/spec/install/failure_spec.rb new file mode 100644 index 00000000000..738b2cf1bd8 --- /dev/null +++ b/spec/install/failure_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true +require "spec_helper" + +RSpec.describe "bundle install" do + context "installing a gem fails" do + it "prints out why that gem was being installed" do + build_repo2 do + build_gem "activesupport", "2.3.2" do |s| + s.extensions << "Rakefile" + s.write "Rakefile", <<-RUBY + task :default do + abort "make installing activesupport-2.3.2 fail" + end + RUBY + end + end + + install_gemfile <<-G + source "file:#{gem_repo2}" + gem "rails" + G + expect(out).to end_with(<<-M.strip) +An error occurred while installing activesupport (2.3.2), and Bundler cannot continue. +Make sure that `gem install activesupport -v '2.3.2'` succeeds before bundling. + +In Gemfile: + rails was resolved to 2.3.2, which depends on + actionmailer was resolved to 2.3.2, which depends on + activesupport + M + end + end +end diff --git a/spec/install/gemfile/git_spec.rb b/spec/install/gemfile/git_spec.rb index ce639ee89f0..d2d46279aaf 100644 --- a/spec/install/gemfile/git_spec.rb +++ b/spec/install/gemfile/git_spec.rb @@ -1085,7 +1085,12 @@ gem "foo", :git => "#{lib_path("foo-1.0")}" G - expect(out).to include("An error occurred while installing foo (1.0)") + expect(out).to end_with(<<-M.strip) +An error occurred while installing foo (1.0), and Bundler cannot continue. + +In Gemfile: + foo + M expect(out).not_to include("gem install foo") end