summaryrefslogtreecommitdiff
path: root/lib/bundler/environment.rb
blob: 77ce480631bb31bfb6f05855202ce94458c999e0 (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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
require 'erb'

module Bundler
  class Environment
    attr_reader :root

    def initialize(root, definition)
      @root = root
      @definition = definition
    end

    def index
      @index ||= Index.build do |idx|
        sources.each do |s|
          idx.use s.local_specs
        end
      end
    end

    def requested_specs
      @requested_specs ||= begin
        groups = @definition.groups - Bundler.settings.without
        groups.map! { |g| g.to_sym }
        specs_for(groups)
      end
    end

    def specs
      @specs ||= resolve_locally || resolve_remotely
    end

    def dependencies
      @definition.dependencies
    end

    def resolved_dependencies
      @definition.resolved_dependencies
    end

    def lock
      Bundler.ui.info("The bundle is already locked, relocking.") if locked?
      sources.each { |s| s.lock if s.respond_to?(:lock) }
      FileUtils.mkdir_p("#{root}/.bundle")
      write_yml_lock
      write_rb_lock
      Bundler.ui.confirm("The bundle is now locked. Use `bundle show` to list the gems in the environment.")
    end

  private

    def sources
      @definition.sources
    end

    def resolve(type, index)
      source_requirements = {}
      resolved_dependencies.each do |dep|
        next unless dep.source && dep.source.respond_to?(type)
        source_requirements[dep.name] = dep.source.send(type)
      end

      # Run a resolve against the locally available gems
      Resolver.resolve(resolved_dependencies, index, source_requirements)
    end

    def resolve_locally
      resolve(:local_specs, index)
    end

    def resolve_remotely
      raise NotImplementedError
    end

    def specs_for(groups)
      deps = dependencies.select { |d| (d.groups & groups).any? }
      specs.for(deps)
    end

    # ==== Locking

    def locked?
      File.exist?("#{root}/Gemfile.lock")
    end

    def write_rb_lock
      begin
        env_file = Bundler.default_gemfile.dirname.join(".bundle/environment.rb")
        env_file.dirname.mkpath
        File.open(env_file, 'w') do |f|
          f.puts <<-RB
  require "rubygems"
  require "bundler/setup"
          RB
        end
      rescue Errno::EACCES
        Bundler.ui.warn "Cannot write .bundle/environment.rb file"
      end
    end

    def gemfile_fingerprint
      Digest::SHA1.hexdigest(File.read(Bundler.default_gemfile))
    end

    def specs_for_lock_file
      requested_specs.map do |s|
        hash = {
          :name => s.name,
          :load_paths => s.load_paths
        }
        if s.respond_to?(:relative_loaded_from) && s.relative_loaded_from
          hash[:virtual_spec] = s.to_ruby
        end
        hash[:loaded_from] = s.loaded_from.to_s
        hash
      end
    end

    def load_paths
      specs.map { |s| s.load_paths }.flatten
    end

    def write_yml_lock
      File.open("#{root}/Gemfile.lock", 'w') do |f|
        f.puts details
      end
    end

    def details
      output = ""

      pinned_sources = dependencies.map {|d| d.source }
      all_sources    = @definition.sources.map {|s| s }

      specified_sources = all_sources - pinned_sources

      unless specified_sources.length == 1 && specified_sources.first.remotes.empty?
        output << "sources:\n"

        specified_sources.each do |source|
          o = source.to_lock
          output << "  #{source.to_lock}\n" unless o.empty?
        end
        output << "\n"
      end

      unless @definition.dependencies.empty?
        output << "dependencies:\n"
        @definition.dependencies.sort_by {|d| d.name }.each do |dependency|
          output << dependency.to_lock
        end
        output << "\n"
      end

      output << "specs:\n"
      specs.sort_by {|s| s.name }.each do |spec|
        output << spec.to_lock
      end

      output
    end

    def autorequires_for_groups(*groups)
      groups.map! { |g| g.to_sym }
      groups = groups.any? ? groups : (@definition.groups - Bundler.settings.without)
      autorequires = Hash.new { |h,k| h[k] = [] }

      ordered_deps = @definition.dependencies.find_all{|d| (d.groups & groups).any? }

      ordered_deps.each do |dep|
        dep.groups.each do |group|
          # If there is no autorequire, then rescue from
          # autorequiring the gems name
          if dep.autorequire
            dep.autorequire.each do |file|
              autorequires[group] << [file, true]
            end
          else
            autorequires[group] << [dep.name, false]
          end
        end
      end

      if groups.empty?
        autorequires
      else
        groups.inject({}) { |h,g| h[g] = autorequires[g]; h }
      end
    end
  end
end