# File lib/bundler/definition.rb, line 11 def self.build(gemfile, lockfile, unlock) unlock ||= {} gemfile = Pathname.new(gemfile).expand_path unless gemfile.file? raise GemfileNotFound, "#{gemfile} not found" end Dsl.evaluate(gemfile, lockfile, unlock) end
How does the new system work?
Load information from Gemfile and Lockfile
Invalidate stale locked specs
All specs from stale source are stale
All specs that are reachable only through a stale dependency are stale.
If all fresh dependencies are satisfied by the locked specs, then we can try to resolve locally.
# File lib/bundler/definition.rb, line 34 def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil) @unlocking = unlock == true || !unlock.empty? @dependencies, @sources, @unlock = dependencies, sources, unlock @remote = false @specs = nil @lockfile_contents = "" @ruby_version = ruby_version if lockfile && File.exists?(lockfile) @lockfile_contents = Bundler.read_file(lockfile) locked = LockfileParser.new(@lockfile_contents) @platforms = locked.platforms if unlock != true @locked_deps = locked.dependencies @locked_specs = SpecSet.new(locked.specs) @locked_sources = locked.sources else @unlock = {} @locked_deps = [] @locked_specs = SpecSet.new([]) @locked_sources = [] end else @unlock = {} @platforms = [] @locked_deps = [] @locked_specs = SpecSet.new([]) @locked_sources = [] end @unlock[:gems] ||= [] @unlock[:sources] ||= [] current_platform = Bundler.rubygems.platforms.map { |p| generic(p) }.compact.last @new_platform = !@platforms.include?(current_platform) @platforms |= [current_platform] @path_changes = converge_paths eager_unlock = expand_dependencies(@unlock[:gems]) @unlock[:gems] = @locked_specs.for(eager_unlock).map { |s| s.name } @source_changes = converge_sources @dependency_changes = converge_dependencies @local_changes = converge_locals fixup_dependency_types! end
# File lib/bundler/definition.rb, line 152 def current_dependencies dependencies.reject { |d| !d.should_include? } end
# File lib/bundler/definition.rb, line 282 def ensure_equivalent_gemfile_and_lockfile(explicit_flag = false) changes = false msg = "You are trying to install in deployment mode after changing\n" "your Gemfile. Run `bundle install` elsewhere and add the\n" "updated Gemfile.lock to version control." unless explicit_flag msg += "\n\nIf this is a development machine, remove the Gemfile " "freeze \nby running `bundle install --no-deployment`." end added = [] deleted = [] changed = [] if @locked_sources != @sources new_sources = @sources - @locked_sources deleted_sources = @locked_sources - @sources if new_sources.any? added.concat new_sources.map { |source| "* source: #{source}" } end if deleted_sources.any? deleted.concat deleted_sources.map { |source| "* source: #{source}" } end changes = true end both_sources = Hash.new { |h,k| h[k] = ["no specified source", "no specified source"] } @dependencies.each { |d| both_sources[d.name][0] = d.source if d.source } @locked_deps.each { |d| both_sources[d.name][1] = d.source if d.source } both_sources.delete_if { |k,v| v[0] == v[1] } if @dependencies != @locked_deps new_deps = @dependencies - @locked_deps deleted_deps = @locked_deps - @dependencies if new_deps.any? added.concat new_deps.map { |d| "* #{pretty_dep(d)}" } end if deleted_deps.any? deleted.concat deleted_deps.map { |d| "* #{pretty_dep(d)}" } end both_sources.each do |name, sources| changed << "* #{name} from `#{sources[0]}` to `#{sources[1]}`" end changes = true end msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any? msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any? msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any? msg << "\n" raise ProductionError, msg if added.any? || deleted.any? || changed.any? end
# File lib/bundler/definition.rb, line 84 def fixup_dependency_types! # XXX This is a temporary workaround for a bug when using rubygems 1.8.15 # where Gem::Dependency#== matches Gem::Dependency#type. As the lockfile # doesn't carry a notion of the dependency type, if you use # add_development_dependency in a gemspec that's loaded with the gemspec # directive, the lockfile dependencies and resolved dependencies end up # with a mismatch on #type. # Test coverage to catch a regression on this is in gemspec_spec.rb @dependencies.each do |d| if ld = @locked_deps.find { |l| l.name == d.name } ld.instance_variable_set(:@type, d.type) end end end
# File lib/bundler/definition.rb, line 215 def groups dependencies.map { |d| d.groups }.flatten.uniq end
# File lib/bundler/definition.rb, line 184 def index @index ||= Index.build do |idx| dependency_names = @dependencies.dup || [] dependency_names.map! {|d| d.name } @sources.each do |s| if s.is_a?(Bundler::Source::Rubygems) s.dependency_names = dependency_names.uniq idx.add_source s.specs else source_index = s.specs dependency_names += source_index.unmet_dependency_names idx.add_source source_index end end end end
# File lib/bundler/definition.rb, line 219 def lock(file) contents = to_lock # Convert to \r\n if the existing lock has them # i.e., Windows with `git config core.autocrlf=true` contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match("\r\n") return if @lockfile_contents == contents if Bundler.settings[:frozen] Bundler.ui.error "Cannot write a changed lockfile while frozen." return end File.open(file, 'wb'){|f| f.puts(contents) } rescue Errno::EACCES raise Bundler::InstallError, "There was an error while trying to write to Gemfile.lock. It is likely that \n" "you need to allow write permissions for the file at path: \n" "#{File.expand_path(file)}" end
# File lib/bundler/definition.rb, line 138 def missing_specs missing = [] resolve.materialize(requested_dependencies, missing) missing end
# File lib/bundler/definition.rb, line 134 def new_platform? @new_platform end
# File lib/bundler/definition.rb, line 126 def new_specs specs - @locked_specs end
# File lib/bundler/definition.rb, line 130 def removed_specs @locked_specs - specs end
# File lib/bundler/definition.rb, line 144 def requested_specs @requested_specs ||= begin groups = self.groups - Bundler.settings.without groups.map! { |g| g.to_sym } specs_for(groups) end end
# File lib/bundler/definition.rb, line 162 def resolve @resolve ||= begin if Bundler.settings[:frozen] || (!@unlocking && nothing_changed?) @locked_specs else last_resolve = converge_locked_specs # Record the specs available in each gem's source, so that those # specs will be available later when the resolver knows where to # look for that gemspec (or its dependencies) source_requirements = {} dependencies.each do |dep| next unless dep.source source_requirements[dep.name] = dep.source.specs end # Run a resolve against the locally available gems last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve) end end end
# File lib/bundler/definition.rb, line 105 def resolve_remotely! raise "Specs already loaded" if @specs @remote = true @sources.each { |s| s.remote! } specs end
# File lib/bundler/definition.rb, line 99 def resolve_with_cache! raise "Specs already loaded" if @specs @sources.each { |s| s.cached! } specs end
used when frozen is enabled so we can find the bundler spec, even if (say) a git gem is not checked out.
# File lib/bundler/definition.rb, line 204 def rubygems_index @rubygems_index ||= Index.build do |idx| rubygems = @sources.find{|s| s.is_a?(Source::Rubygems) } idx.add_source rubygems.specs end end
# File lib/bundler/definition.rb, line 211 def rubygems_remotes @sources.select{|s| s.is_a?(Source::Rubygems) }.map{|s| s.remotes }.flatten end
# File lib/bundler/definition.rb, line 112 def specs @specs ||= begin specs = resolve.materialize(requested_dependencies) unless specs["bundler"].any? local = Bundler.settings[:frozen] ? rubygems_index : index bundler = local.search(Gem::Dependency.new('bundler', VERSION)).last specs["bundler"] = bundler if bundler end specs end end
# File lib/bundler/definition.rb, line 156 def specs_for(groups) deps = dependencies.select { |d| (d.groups & groups).any? } deps.delete_if { |d| !d.should_include? } specs.for(expand_dependencies(deps)) end
# File lib/bundler/definition.rb, line 241 def to_lock out = "" sorted_sources.each do |source| # Add the source header out << source.to_lock # Find all specs for this source resolve. select { |s| s.source == source }. # This needs to be sorted by full name so that # gems with the same name, but different platform # are ordered consistantly sort_by { |s| s.full_name }. each do |spec| next if spec.name == 'bundler' out << spec.to_lock end out << "\n" end out << "PLATFORMS\n" platforms.map { |p| p.to_s }.sort.each do |p| out << " #{p}\n" end out << "\n" out << "DEPENDENCIES\n" handled = [] dependencies. sort_by { |d| d.to_s }. each do |dep| next if handled.include?(dep.name) out << dep.to_lock handled << dep.name end out end
# File lib/bundler/definition.rb, line 345 def validate_ruby! return unless ruby_version system_ruby_version = Bundler::SystemRubyVersion.new if diff = ruby_version.diff(system_ruby_version) problem, expected, actual = diff msg = case problem when :engine "Your Ruby engine is #{actual}, but your Gemfile specified #{expected}" when :version "Your Ruby version is #{actual}, but your Gemfile specified #{expected}" when :engine_version "Your #{system_ruby_version.engine} version is #{actual}, but your Gemfile specified #{ruby_version.engine} #{expected}" end raise RubyVersionMismatch, msg end end
Generated with the Darkfish Rdoc Generator 2.