# File lib/bundler/definition.rb, line 9 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 32 def initialize(lockfile, dependencies, sources, unlock) @dependencies, @sources, @unlock = dependencies, sources, unlock @remote = false @specs = nil @lockfile_contents = "" 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] eager_unlock = expand_dependencies(@unlock[:gems]) @unlock[:gems] = @locked_specs.for(eager_unlock).map { |s| s.name } converge_sources converge_dependencies fixup_dependency_types! end
# File lib/bundler/definition.rb, line 145 def current_dependencies dependencies.reject { |d| !d.should_include? } end
# File lib/bundler/definition.rb, line 255 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 77 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 193 def groups dependencies.map { |d| d.groups }.flatten.uniq end
# File lib/bundler/definition.rb, line 173 def index @index ||= Index.build do |idx| @sources.each do |s| idx.use s.specs end end end
# File lib/bundler/definition.rb, line 197 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] # TODO: Warn here if we got here. return end File.open(file, 'wb'){|f| f.puts(contents) } end
# File lib/bundler/definition.rb, line 131 def missing_specs missing = [] resolve.materialize(requested_dependencies, missing) missing end
# File lib/bundler/definition.rb, line 127 def new_platform? @new_platform end
# File lib/bundler/definition.rb, line 119 def new_specs specs - @locked_specs end
# File lib/bundler/definition.rb, line 189 def no_sources? @sources.length == 1 && @sources.first.remotes.empty? end
# File lib/bundler/definition.rb, line 123 def removed_specs @locked_specs - specs end
# File lib/bundler/definition.rb, line 137 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 155 def resolve @resolve ||= begin if Bundler.settings[:frozen] @locked_specs else last_resolve = converge_locked_specs 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 98 def resolve_remotely! raise "Specs already loaded" if @specs @remote = true @sources.each { |s| s.remote! } specs end
# File lib/bundler/definition.rb, line 92 def resolve_with_cache! raise "Specs already loaded" if @specs @sources.each { |s| s.cached! } specs end
# File lib/bundler/definition.rb, line 181 def rubygems_index @rubygems_index ||= Index.build do |idx| @sources.find_all{|s| s.is_a?(Source::Rubygems) }.each do |s| idx.use s.specs end end end
# File lib/bundler/definition.rb, line 105 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 149 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 214 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
Generated with the Darkfish Rdoc Generator 2.