aboutsummaryrefslogtreecommitdiffstats
path: root/lib/bundler/installer.rb
blob: 216bc4c95a7666b1eb8ee8bf1b1741eb4699d701 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
require 'rubygems/dependency_installer'

module Bundler
  class Installer < Environment
    def self.install(root, definition, options)
      new(root, definition).run(options)
    end

    def run(options)
      if actual_dependencies.empty?
        Bundler.ui.warn "The Gemfile specifies no dependencies"
        return
      end

      # Ensure that BUNDLE_PATH exists
      FileUtils.mkdir_p(Bundler.bundle_path)

      # Must install gems in the order that the resolver provides
      # as dependencies might actually affect the installation of
      # the gem.
      specs.each do |spec|
        spec.source.fetch(spec) if spec.source.respond_to?(:fetch)

        if spec.groups & Bundler.settings.without == spec.groups
          Bundler.ui.debug "  * Not in requested group; skipping."
          next
        end

        # unless spec.source.is_a?(Source::SystemGems)
          Bundler.ui.info "Installing #{spec.name} (#{spec.version}) from #{spec.source} "
        # end

        spec.source.install(spec)

        Bundler.ui.info ""
      end

      Bundler.ui.confirm "Your bundle is complete!"
    end

    def dependencies
      @definition.dependencies
    end

    def actual_dependencies
      @definition.actual_dependencies
    end

    def specs
      @specs ||= group_specs(resolve_locally || resolve_remotely)
    end

  private

    def sources
      @definition.sources
    end

    def resolve_locally
      # Return unless all the dependencies have = version requirements
      return if actual_dependencies.any? { |d| ambiguous?(d) }

      source_requirements = {}
      actual_dependencies.each do |dep|
        next unless dep.source && dep.source.respond_to?(:local_specs)
        source_requirements[dep.name] = dep.source.local_specs
      end

      # Run a resolve against the locally available gems
      specs = Resolver.resolve(actual_dependencies, local_index, source_requirements)

      # Simple logic for now. Can improve later.
      specs.length == actual_dependencies.length && specs
    rescue Bundler::GemNotFound => e
      nil
      raise if ENV["OMG"]
    end

    def resolve_remotely
      index # trigger building the index
      Bundler.ui.info "Resolving dependencies"
      source_requirements = {}
      actual_dependencies.each do |dep|
        next unless dep.source
        source_requirements[dep.name] = dep.source.specs
      end

      specs = Resolver.resolve(actual_dependencies, index, source_requirements)
      specs
    end

    def ambiguous?(dep)
      dep.requirement.requirements.any? { |op,_| op != '=' }
    end

    def index
      @index ||= begin
        index = Index.new

        rg_sources = sources.select { |s| s.is_a?(Source::Rubygems) }
        other_sources = sources.select { |s| !s.is_a?(Source::Rubygems)   }

        other_sources.each do |source|
          i = source.specs
          Bundler.ui.debug "Source: Processing index"
          index = i.merge(index)
        end

        index = Index.from_installed_gems.merge(index)
        index = Index.from_cached_specs("#{Bundler.bundle_path}/cache").merge(index)

        if File.directory?("#{root}/vendor/cache")
          index = cache_source.specs.merge(index)
        end

        rg_sources.each do |source|
          i = source.specs
          Bundler.ui.debug "Source: Processing index"
          index = i.merge(index)
        end

        index
      end
    end

    def local_index
      @local_index ||= begin
        index = Index.new

        sources.each do |source|
          next unless source.respond_to?(:local_specs)
          index = source.local_specs.merge(index)
        end

        if File.directory?("#{root}/vendor/cache")
          index = cache_source.specs.merge(index).freeze
        end

        index = Index.from_installed_gems.merge(index)
        Index.from_cached_specs("#{Bundler.bundle_path}/cache").merge(index)
      end
    end

    def cache_source
      Source::GemCache.new("path" => "#{root}/vendor/cache")
    end

  end
end